diff --git a/_support/Config/BadRegistrar.php b/_support/Config/BadRegistrar.php index 877d7be..a651d11 100644 --- a/_support/Config/BadRegistrar.php +++ b/_support/Config/BadRegistrar.php @@ -10,7 +10,7 @@ class BadRegistrar { - public static function RegistrarConfig2() + public static function RegistrarConfig() { return 'I am not worthy'; } diff --git a/_support/Config/MockServices.php b/_support/Config/MockServices.php new file mode 100644 index 0000000..0bd6400 --- /dev/null +++ b/_support/Config/MockServices.php @@ -0,0 +1,28 @@ + TESTPATH . '_support/', + ]; + public $classmap = []; + + //-------------------------------------------------------------------- + + public function __construct() + { + // Don't call the parent since we don't want the default mappings. + // parent::__construct(); + } + + //-------------------------------------------------------------------- + public static function locator(bool $getShared = true) + { + return new \CodeIgniter\Autoloader\FileLocator(static::autoloader()); + } + +} diff --git a/_support/Controllers/Popcorn.php b/_support/Controllers/Popcorn.php new file mode 100644 index 0000000..b3826d3 --- /dev/null +++ b/_support/Controllers/Popcorn.php @@ -0,0 +1,58 @@ +respond('Oops', 567, 'Surprise'); + } + + public function popper() + { + throw new \RuntimeException('Surprise', 500); + } + + public function weasel() + { + $this->respond('', 200); + } + + public function oops() + { + $this->failUnauthorized(); + } + + public function goaway() + { + return redirect()->to('/'); + } + + // @see https://github.com/codeigniter4/CodeIgniter4/issues/1834 + public function index3() + { + $response = $this->response->setJSON([ + 'lang' => $this->request->getLocale(), + ]); + + // echo var_dump($this->response->getBody()); + return $response; + } + +} diff --git a/_support/Database/Migrations/20160428212500_Create_test_tables.php b/_support/Database/Migrations/20160428212500_Create_test_tables.php index 8d4a93a..d81ab34 100644 --- a/_support/Database/Migrations/20160428212500_Create_test_tables.php +++ b/_support/Database/Migrations/20160428212500_Create_test_tables.php @@ -86,14 +86,20 @@ $this->forge->addKey('id', true); $this->forge->createTable('empty', true); - //No Primary Key + // Secondary Table $this->forge->addField([ + 'id' => [ + 'type' => 'INTEGER', + 'constraint' => 3, + $unique_or_auto => true, + ], 'key' => [ 'type' => 'VARCHAR', 'constraint' => 40, ], 'value' => ['type' => 'TEXT'], ]); + $this->forge->addKey('id', true); $this->forge->createTable('secondary', true); } diff --git a/_support/Models/SecondaryModel.php b/_support/Models/SecondaryModel.php index 19d8228..6088bd6 100644 --- a/_support/Models/SecondaryModel.php +++ b/_support/Models/SecondaryModel.php @@ -6,7 +6,7 @@ { protected $table = 'secondary'; - protected $primaryKey = null; + protected $primaryKey = 'id'; protected $returnType = 'object'; diff --git a/app/Config/Events.php b/app/Config/Events.php index 97c732a..cfdb537 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -19,23 +19,25 @@ * Events::on('create', [$myInstance, 'myMethod']); */ -/* - * -------------------------------------------------------------------- - * Debug Toolbar Listeners. - * -------------------------------------------------------------------- - * If you delete, they will no longer be collected. - */ -if (ENVIRONMENT !== 'production') -{ - Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); +Events::on('pre_system', function () { + while (\ob_get_level() > 0) + { + \ob_end_flush(); + } - Events::on('pre_system', function () { - if (ENVIRONMENT !== 'testing') - { - \ob_start(function ($buffer) { - return $buffer; - }); - } - Services::toolbar()->respond(); + \ob_start(function ($buffer) { + return $buffer; }); -} + + /* + * -------------------------------------------------------------------- + * Debug Toolbar Listeners. + * -------------------------------------------------------------------- + * If you delete, they will no longer be collected. + */ + if (ENVIRONMENT !== 'production') + { + Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); + Services::toolbar()->respond(); + } +}); diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php new file mode 100644 index 0000000..abffc56 --- /dev/null +++ b/app/Controllers/BaseController.php @@ -0,0 +1,46 @@ +session = \Config\Services::session(); + + } +} diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php index 84b7135..b87de40 100644 --- a/app/Controllers/Home.php +++ b/app/Controllers/Home.php @@ -2,7 +2,7 @@ use CodeIgniter\Controller; -class Home extends Controller +class Home extends BaseController { public function index() { diff --git a/contributing.md b/contributing.md index 78593e2..09dff38 100644 --- a/contributing.md +++ b/contributing.md @@ -5,7 +5,7 @@ We expect all contributions to conform to our style guide, be commented (inside the PHP source files), be documented (in the user guide), and unit tested (in the test folder). -There is a [Contributing to CodeIgniter](./contributing/index.rst) section in the repository which describes the contribution process; this page is an overview. +There is a [Contributing to CodeIgniter](./contributing/README.rst) section in the repository which describes the contribution process; this page is an overview. ## Issues @@ -15,7 +15,7 @@ 2. The issue has already been fixed (check the develop branch, or look for closed Issues) 3. Is it something really obvious that you can fix yourself? -Reporting issues is helpful but an even [better approach](https://codeigniter4.github.io/CodeIgniter4/contributing/workflow.html) is to send a Pull Request, which is done by "Forking" the main repository and committing to your own copy. This will require you to use the version control system called Git. +Reporting issues is helpful but an even [better approach](./contributing/workflow.rst) is to send a Pull Request, which is done by "Forking" the main repository and committing to your own copy. This will require you to use the version control system called Git. ## Guidelines @@ -26,7 +26,7 @@ ### PHP Style -All code must meet the [Style Guide](https://codeigniter4.github.io/CodeIgniter4/contributing/styleguide.html). +All code must meet the [Style Guide](./contributing/styleguide.rst). This makes certain that all code is the same format as the existing code and means it will be as readable as possible. ### Documentation @@ -46,7 +46,7 @@ ### Signing -You must [GPG-sign](https://codeigniter4.github.io/CodeIgniter4/contributing/signing.html) your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open source project. This is *not* just a "signed-off-by" commit, but instead a digitally signed one. +You must [GPG-sign](./contributing/signing.rst) your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open source project. This is *not* just a "signed-off-by" commit, but instead a digitally signed one. ## How-to Guide diff --git a/public/.htaccess b/public/.htaccess index 3a3f07d..adfbcd5 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -5,6 +5,9 @@ # Sets the environment that CodeIgniter runs under. # SetEnv CI_ENVIRONMENT development +# Disable directory browsing +Options All -Indexes + # ---------------------------------------------------------------------- # UTF-8 encoding # ---------------------------------------------------------------------- @@ -18,6 +21,16 @@ # ---------------------------------------------------------------------- +# Activate CORS +# ---------------------------------------------------------------------- + + + + Header set Access-Control-Allow-Origin "*" + + + +# ---------------------------------------------------------------------- # Rewrite engine # ---------------------------------------------------------------------- @@ -47,6 +60,12 @@ RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1 [L] + # Disable image hotlinkiing start + RewriteCond %{HTTP_REFERER} !^$ + RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?example.com [NC] + RewriteRule \.(jpg|jpeg|png|gif)$ – [NC,F,L] + # Disable image hotlinkiing end + # Ensure Authorization header is passed along RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] @@ -58,10 +77,58 @@ ErrorDocument 404 index.php +# Disable server signature start + ServerSignature Off +# Disable server signature end + +# BEGIN Expires + + ExpiresActive On + ExpiresByType text/css "access 1 month" + ExpiresByType text/html "access 1 month" + ExpiresByType image/gif "access 1 year" + ExpiresByType image/png "access 1 year" + ExpiresByType image/jpg "access 1 year" + ExpiresByType image/jpeg "access 1 year" + ExpiresByType image/x-icon "access 1 year" + ExpiresByType image/svg+xml "access plus 1 month" + ExpiresByType audio/ogg "access plus 1 year" + ExpiresByType video/mp4 "access plus 1 year" + ExpiresByType video/ogg "access plus 1 year" + ExpiresByType video/webm "access plus 1 year" + ExpiresByType application/atom+xml "access plus 1 hour" + ExpiresByType application/rss+xml "access plus 1 hour" + ExpiresByType application/pdf "access 1 month" + ExpiresByType application/javascript "access 1 month" + ExpiresByType text/x-javascript "access 1 month" + ExpiresByType text/x-component "access plus 1 month" + ExpiresByType application/x-shockwave-flash "access 1 month" + ExpiresByType font/opentype "access plus 1 month" + ExpiresByType application/vnd.ms-fontobject "access plus 1 month" + ExpiresByType application/x-font-ttf "access plus 1 month" + ExpiresByType application/font-woff "access plus 1 month" + ExpiresByType application/font-woff2 "access plus 1 month" + ExpiresDefault "access 1 month" + +# END Expires + # ---------------------------------------------------------------------- # Gzip compression # ---------------------------------------------------------------------- +# Start gzip compression + + mod_gzip_on Yes + mod_gzip_dechunk Yes + mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$ + mod_gzip_item_include handler ^cgi-script$ + mod_gzip_item_include mime ^text/.* + mod_gzip_item_include mime ^application/x-javascript.* + mod_gzip_item_exclude mime ^image/.* + mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.* + +# End gzip compression + # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/ @@ -85,13 +152,31 @@ application/x-font-ttf \ application/xhtml+xml \ application/xml \ + application/x-javascript \ + application/x-font \ + application/x-font-truetype \ + application/x-font-otf \ + application/x-font-woff \ + application/x-font-woff2 \ + application/x-font-opentype \ font/opentype \ - image/svg+xml \ + font/ttf \ + font/otf \ + font/eot \ + font/woff \ + font/woff2 \ + image/svg+xml svg svgz \ image/x-icon \ text/css \ text/html \ text/plain \ text/x-component \ - text/xml + text/xml \ + text/javascript \ + + # For Olders Browsers Which Can't Handle Compression + BrowserMatch ^Mozilla/4 gzip-only-text/html + BrowserMatch ^Mozilla/4\.0[678] no-gzip + BrowserMatch \bMSIE !no-gzip !gzip-only-text/html diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 8ad5add..c168e37 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -1,4 +1,4 @@ -getNamespace($prefix) returns an array of paths for that namespace + foreach ($this->autoloader->getNamespace($prefix) as $namespacePath) + { + $fullPath = realpath($namespacePath . $path); + + if (! is_dir($fullPath)) + { + continue; + } + + $tempFiles = get_filenames($fullPath, true); + + if (! empty($tempFiles)) + { + $files = array_merge($files, $tempFiles); + } + } + + return $files; + } + + //-------------------------------------------------------------------- + + /** * Checks the application folder to see if the file can be found. * Only for use with filenames that DO NOT include namespacing. * diff --git a/system/CLI/BaseCommand.php b/system/CLI/BaseCommand.php index 5b46d7d..154a028 100644 --- a/system/CLI/BaseCommand.php +++ b/system/CLI/BaseCommand.php @@ -1,5 +1,4 @@ -setRule($field, null, $rules); @@ -289,7 +300,27 @@ //-------------------------------------------------------------------- /** - * Outputs a string to the cli. + * Outputs a string to the CLI without any surrounding newlines. + * Useful for showing repeating elements on a single line. + * + * @param string $text + * @param string|null $foreground + * @param string|null $background + */ + public static function print(string $text = '', string $foreground = null, string $background = null) + { + if ($foreground || $background) + { + $text = static::color($text, $foreground, $background); + } + + static::$lastWrite = null; + + fwrite(STDOUT, $text); + } + + /** + * Outputs a string to the cli on it's own line. * * @param string $text The text to output * @param string $foreground @@ -302,6 +333,12 @@ $text = static::color($text, $foreground, $background); } + if (static::$lastWrite !== 'write') + { + $text = PHP_EOL . $text; + static::$lastWrite = 'write'; + } + fwrite(STDOUT, $text . PHP_EOL); } @@ -380,10 +417,12 @@ /** * if operating system === windows + * + * @return boolean */ - public static function isWindows() + public static function isWindows(): bool { - return stripos(PHP_OS, 'WIN') === 0; + return stripos(PHP_OS, 'WIN') === 0; } //-------------------------------------------------------------------- @@ -436,7 +475,7 @@ * * @return string The color coded string */ - public static function color(string $text, string $foreground, string $background = null, string $format = null) + public static function color(string $text, string $foreground, string $background = null, string $format = null): string { if (static::isWindows() && ! isset($_SERVER['ANSICON'])) { @@ -482,8 +521,12 @@ * * @return integer */ - public static function strlen(string $string): int + public static function strlen(?string $string): int { + if (is_null($string)) + { + return 0; + } foreach (static::$foreground_colors as $color) { $string = strtr($string, ["\033[" . $color . 'm' => '']); @@ -655,7 +698,7 @@ * Parses the command line it was called from and collects all * options and valid segments. * - * I tried to use getopt but had it fail occassionally to find any + * I tried to use getopt but had it fail occasionally to find any * options but argc has always had our back. We don't have all of the power * of getopt but this does us just fine. */ @@ -706,7 +749,7 @@ * * @return string */ - public static function getURI() + public static function getURI(): string { return implode('/', static::$segments); } @@ -743,7 +786,7 @@ * * @return array */ - public static function getSegments() + public static function getSegments(): array { return static::$segments; } @@ -779,7 +822,7 @@ * * @return array */ - public static function getOptions() + public static function getOptions(): array { return static::$options; } @@ -819,12 +862,12 @@ //-------------------------------------------------------------------- /** - * Returns a well formated table + * Returns a well formatted table * * @param array $tbody List of rows * @param array $thead List of columns * - * @return string + * @return void */ public static function table(array $tbody, array $thead = []) { diff --git a/system/CLI/CommandRunner.php b/system/CLI/CommandRunner.php index 012c037..3e273db 100644 --- a/system/CLI/CommandRunner.php +++ b/system/CLI/CommandRunner.php @@ -1,5 +1,5 @@ getItem($key); - return is_array($data) ? $data['data'] : false; + return is_array($data) ? $data['data'] : null; } //-------------------------------------------------------------------- diff --git a/system/Cache/Handlers/MemcachedHandler.php b/system/Cache/Handlers/MemcachedHandler.php index 704a8a7..8950257 100644 --- a/system/Cache/Handlers/MemcachedHandler.php +++ b/system/Cache/Handlers/MemcachedHandler.php @@ -1,5 +1,4 @@ -prefix . $key; - $data = $this->memcached->get($key); + if ($this->memcached instanceof \Memcached) + { + $data = $this->memcached->get($key); + + // check for unmatched key + if ($this->memcached->getResultCode()==\Memcached::RES_NOTFOUND) + { + return null; + } + } + elseif ($this->memcached instanceof \Memcache) + { + $flags = false; + $data = $this->memcached->get($key, $flags); + + // check for unmatched key (i.e. $flags is untouched) + if ($flags===false) + { + return null; + } + } return is_array($data) ? $data[0] : $data; } diff --git a/system/Cache/Handlers/PredisHandler.php b/system/Cache/Handlers/PredisHandler.php index f1b1c00..0f643e0 100644 --- a/system/Cache/Handlers/PredisHandler.php +++ b/system/Cache/Handlers/PredisHandler.php @@ -1,5 +1,4 @@ -config; $this->redis = new \Redis(); - - try + if (! $this->redis->connect($config['host'], ($config['host'][0] === '/' ? 0 : $config['port']), $config['timeout'])) { - if (! $this->redis->connect($config['host'], ($config['host'][0] === '/' ? 0 : $config['port']), $config['timeout']) - ) - { - // log_message('error', 'Cache: Redis connection failed. Check your configuration.'); - } - - if (isset($config['password']) && ! $this->redis->auth($config['password'])) - { - log_message('error', 'Cache: Redis authentication failed.'); - } - - if (isset($config['database']) && ! $this->redis->select($config['database'])) - { - log_message('error', 'Cache: Redis select database failed.'); - } + log_message('error', 'Cache: Redis connection failed. Check your configuration.'); } - catch (\RedisException $e) + + if (isset($config['password']) && ! $this->redis->auth($config['password'])) { - throw new CriticalError('Cache: Redis connection refused (' . $e->getMessage() . ')'); + log_message('error', 'Cache: Redis authentication failed.'); + } + + if (isset($config['database']) && ! $this->redis->select($config['database'])) + { + log_message('error', 'Cache: Redis select database failed.'); } } @@ -148,7 +143,7 @@ if (! isset($data['__ci_type'], $data['__ci_value']) || $data['__ci_value'] === false) { - return false; + return null; } switch ($data['__ci_type']) @@ -161,10 +156,10 @@ case 'double': // Yes, 'double' is returned and NOT 'float' case 'string': case 'NULL': - return settype($data['__ci_value'], $data['__ci_type']) ? $data['__ci_value'] : false; + return settype($data['__ci_value'], $data['__ci_type']) ? $data['__ci_value'] : null; case 'resource': default: - return false; + return null; } } @@ -304,7 +299,7 @@ $value = $this->get($key); - if ($value !== false) + if ($value !== null) { $time = time(); return [ @@ -314,7 +309,7 @@ ]; } - return false; + return null; } //-------------------------------------------------------------------- diff --git a/system/Cache/Handlers/WincacheHandler.php b/system/Cache/Handlers/WincacheHandler.php index 3040a51..c619f26 100644 --- a/system/Cache/Handlers/WincacheHandler.php +++ b/system/Cache/Handlers/WincacheHandler.php @@ -1,4 +1,5 @@ -handleRequest($routes, $cacheConfig, $returnResponse); } - catch (Router\RedirectException $e) + catch (FilterException $e) { $logger = Services::logger(); $logger->info('REDIRECTED ROUTE at ' . $e->getMessage()); @@ -278,7 +281,7 @@ * @param boolean $returnResponse * * @return \CodeIgniter\HTTP\RequestInterface|\CodeIgniter\HTTP\Response|\CodeIgniter\HTTP\ResponseInterface|mixed - * @throws \CodeIgniter\Filters\Exceptions\FilterException + * @throws \CodeIgniter\Router\RedirectException */ protected function handleRequest(RouteCollectionInterface $routes = null, $cacheConfig, bool $returnResponse = false) { @@ -533,7 +536,7 @@ * * @throws \Exception * - * @return boolean + * @return boolean|\CodeIgniter\HTTP\ResponseInterface */ public function displayCache($config) { @@ -564,7 +567,9 @@ $this->response->setBody($output); return $this->response; - }; + } + + return false; } //-------------------------------------------------------------------- @@ -574,7 +579,7 @@ * * @param integer $time * - * @return $this + * @return void */ public static function cache(int $time) { @@ -611,7 +616,7 @@ * * @return array */ - public function getPerformanceStats() + public function getPerformanceStats(): array { return [ 'startTime' => $this->startTime, @@ -682,6 +687,7 @@ * of the config file. * * @return string + * @throws \CodeIgniter\Router\RedirectException */ protected function tryToRouteIt(RouteCollectionInterface $routes = null) { @@ -845,7 +851,7 @@ { if ($override instanceof \Closure) { - echo $override(); + echo $override($e->getMessage()); } else if (is_array($override)) { diff --git a/system/Commands/Database/CreateMigration.php b/system/Commands/Database/CreateMigration.php index 5a3a75b..dcebb41 100644 --- a/system/Commands/Database/CreateMigration.php +++ b/system/Commands/Database/CreateMigration.php @@ -1,5 +1,4 @@ - 'The PHP Binary [default: "PHP_BINARY"]', '-host' => 'The HTTP Host [default: "localhost"]', '-port' => 'The HTTP Host Port [default: "8080"]', ]; + /** + * Run the server + * + * @param array $params Parameters + * + * @return void + */ public function run(array $params) { // Valid PHP Version? if (phpversion() < $this->minPHPVersion) { - die("You PHP version must be {$this->minPHPVersion} or higher to run CodeIgniter. Current version: " . phpversion()); + die('Your PHP version must be ' . $this->minPHPVersion . + ' or higher to run CodeIgniter. Current version: ' . phpversion()); } - // Collect any user-supplied options and apply them + // Collect any user-supplied options and apply them. $php = CLI::getOption('php') ?? PHP_BINARY; $host = CLI::getOption('host') ?? 'localhost'; $port = CLI::getOption('port') ?? '8080'; - // Get the party started - CLI::write("CodeIgniter development server started on http://{$host}:{$port}", 'green'); + // Get the party started. + CLI::write('CodeIgniter development server started on http://' . $host . ':' . $port, 'green'); CLI::write('Press Control-C to stop.'); - // Set the Front Controller path as Document Root - $docroot = FCPATH; + // Set the Front Controller path as Document Root. + $docroot = escapeshellarg(FCPATH); - // Mimic Apache's mod_rewrite functionality with user settings - $rewrite = __DIR__ . '/rewrite.php'; + // Mimic Apache's mod_rewrite functionality with user settings. + $rewrite = escapeshellarg(__DIR__ . '/rewrite.php'); // Call PHP's built-in webserver, making sure to set our // base path to the public folder, and to use the rewrite file // to ensure our environment is set and it simulates basic mod_rewrite. - passthru("{$php} -S {$host}:{$port} -t {$docroot} {$rewrite}"); + passthru($php . ' -S ' . $host . ':' . $port . ' -t ' . $docroot . ' ' . $rewrite); } } diff --git a/system/Commands/Sessions/CreateMigration.php b/system/Commands/Sessions/CreateMigration.php index 0a9391e..ac8a90b 100644 --- a/system/Commands/Sessions/CreateMigration.php +++ b/system/Commands/Sessions/CreateMigration.php @@ -1,4 +1,4 @@ - $to) { - $tbody[] = [ - $from, - $method, - $to, - ]; + // filter for strings, as callbacks aren't displayable + if (is_string($to)) + { + $tbody[] = [ + $from, + $method, + $to, + ]; + } } } diff --git a/system/Common.php b/system/Common.php index 4291e60..f0907f7 100644 --- a/system/Common.php +++ b/system/Common.php @@ -36,6 +36,7 @@ * @filesource */ +use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Config\Services; @@ -148,7 +149,7 @@ * * @return string */ - function view(string $name, array $data = [], array $options = []) + function view(string $name, array $data = [], array $options = []): string { /** * @var CodeIgniter\View\View $renderer @@ -182,7 +183,7 @@ * * @return string */ - function view_cell(string $library, $params = null, int $ttl = 0, string $cacheName = null) + function view_cell(string $library, $params = null, int $ttl = 0, string $cacheName = null): string { return Services::viewcell() ->render($library, $params, $ttl, $cacheName); @@ -254,8 +255,9 @@ * @param string $encoding * * @return string|array + * @throws \InvalidArgumentException */ - function esc($data, $context = 'html', $encoding = null) + function esc($data, string $context = 'html', string $encoding = null) { if (is_array($data)) { @@ -325,7 +327,7 @@ * * @return \CodeIgniter\Session\Session|mixed|null */ - function session($val = null) + function session(string $val = null) { $session = Services::session(); @@ -431,7 +433,7 @@ * * @return string */ - function lang(string $line, array $args = [], string $locale = null) + function lang(string $line, array $args = [], string $locale = null): string { return Services::language($locale) ->getLine($line, $args); @@ -492,7 +494,7 @@ * * @return boolean */ - function is_cli() + function is_cli(): bool { return (PHP_SAPI === 'cli' || defined('STDIN')); } @@ -515,7 +517,7 @@ * * @return false|string */ - function route_to(string $method, ...$params): string + function route_to(string $method, ...$params) { return Services::routes()->reverseRoute($method, ...$params); } @@ -536,7 +538,7 @@ * * @return string */ - function remove_invisible_characters($str, $url_encoded = true) + function remove_invisible_characters(string $str, bool $url_encoded = true): string { $non_displayables = []; @@ -573,7 +575,8 @@ * 2. {namespace}/Helpers * 3. system/Helpers * - * @param string|array $filenames + * @param string|array $filenames + * @throws \CodeIgniter\Files\Exceptions\FileNotFoundException */ function helper($filenames) { @@ -682,7 +685,7 @@ * * @return string */ - function app_timezone() + function app_timezone(): string { $config = config(\Config\App::class); @@ -701,7 +704,7 @@ * * @return string */ - function csrf_token() + function csrf_token(): string { $config = config(\Config\App::class); @@ -720,7 +723,7 @@ * * @return string */ - function csrf_hash() + function csrf_hash(): string { $security = Services::security(null, true); @@ -735,9 +738,11 @@ /** * Generates a hidden input field for use within manually generated forms. * + * @param string|null $id + * * @return string */ - function csrf_field(string $id = null) + function csrf_field(string $id = null): string { return ''; } @@ -820,6 +825,12 @@ */ function old(string $key, $default = null, $escape = 'html') { + // Ensure the session is loaded + if (session_status() === PHP_SESSION_NONE && ENVIRONMENT !== 'testing') + { + session(); + } + $request = Services::request(); $value = $request->getOldInput($key); @@ -861,7 +872,7 @@ * * @return \CodeIgniter\HTTP\RedirectResponse */ - function redirect(string $uri = null) + function redirect(string $uri = null): RedirectResponse { $response = Services::redirectResponse(null, true); @@ -889,7 +900,7 @@ * * @return string */ - function stringify_attributes($attributes, $js = false): string + function stringify_attributes($attributes, bool $js = false): string { $atts = ''; @@ -933,7 +944,7 @@ * * @codeCoverageIgnore Not practical to test, as travis runs on linux */ - function is_really_writable($file) + function is_really_writable(string $file): bool { // If we're on a Unix server with safe_mode off we call is_writable if (DIRECTORY_SEPARATOR === '/' || ! ini_get('safe_mode')) @@ -983,7 +994,7 @@ * @return string|null The configuration item or NULL if * the item doesn't exist */ - function slash_item($item) + function slash_item(string $item): ?string { $config = config(\Config\App::class); $configItem = $config->{$item}; @@ -1014,7 +1025,7 @@ * terminate script execution if a disabled function is executed. * * The above described behavior turned out to be a bug in Suhosin, - * but even though a fix was commited for 0.9.34 on 2012-02-12, + * but even though a fix was committed for 0.9.34 on 2012-02-12, * that version is yet to be released. This function will therefore * be just temporary, but would probably be kept for a few years. * @@ -1025,7 +1036,7 @@ * * @codeCoverageIgnore This is too exotic */ - function function_usable($function_name) + function function_usable(string $function_name): bool { static $_suhosin_func_blacklist; diff --git a/system/ComposerScripts.php b/system/ComposerScripts.php index fb86d20..d7ebade 100644 --- a/system/ComposerScripts.php +++ b/system/ComposerScripts.php @@ -1,4 +1,4 @@ -search('Config/Registrar.php'); + $locator = \Config\Services::locator(); + static::$registrars = $locator->search('Config/Registrar.php'); + static::$didDiscovery = true; } $shortName = (new \ReflectionClass($this))->getShortName(); diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index 62929ce..aadf04d 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -1,5 +1,4 @@ -add('basecontroller(:any)', function() +{ + throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); +}); + // Migrations $routes->cli('migrations/(:segment)/(:segment)', '\CodeIgniter\Commands\MigrationsCommand::$1/$2'); $routes->cli('migrations/(:segment)', '\CodeIgniter\Commands\MigrationsCommand::$1'); $routes->cli('migrations', '\CodeIgniter\Commands\MigrationsCommand::index'); -// CLI Catchall - uses a _remap to +// CLI Catchall - uses a _remap to call Commands $routes->cli('ci(:any)', '\CodeIgniter\CLI\CommandRunner::index/$1'); + +// Prevent access to initController method +$routes->add('(:any)/initController', function() +{ + throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); +}); diff --git a/system/Config/Services.php b/system/Config/Services.php index f22d946..9cba4cb 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -1,4 +1,4 @@ -request, $this->response); } @@ -150,7 +153,7 @@ * * @param integer $time */ - public function cachePage(int $time) + protected function cachePage(int $time) { CodeIgniter::cache($time); } @@ -179,12 +182,12 @@ * A shortcut to performing validation on input data. If validation * is not successful, a $errors property will be set on this class. * - * @param array $rules - * @param array $messages An array of custom error messages + * @param array|string $rules + * @param array $messages An array of custom error messages * * @return boolean */ - public function validate($rules, array $messages = []): bool + protected function validate($rules, array $messages = []): bool { $this->validator = Services::validation(); diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index be8269c..ff67d1f 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -1,4 +1,4 @@ -maxMinAvgSum($select, $alias, 'MAX'); } @@ -332,7 +342,7 @@ * * @return BaseBuilder */ - public function selectMin($select = '', $alias = '') + public function selectMin(string $select = '', string $alias = '') { return $this->maxMinAvgSum($select, $alias, 'MIN'); } @@ -349,7 +359,7 @@ * * @return BaseBuilder */ - public function selectAvg($select = '', $alias = '') + public function selectAvg(string $select = '', string $alias = '') { return $this->maxMinAvgSum($select, $alias, 'AVG'); } @@ -366,7 +376,7 @@ * * @return BaseBuilder */ - public function selectSum($select = '', $alias = '') + public function selectSum(string $select = '', string $alias = '') { return $this->maxMinAvgSum($select, $alias, 'SUM'); } @@ -386,13 +396,19 @@ * @param string $type * * @return BaseBuilder + * @throws \CodeIgniter\Database\Exceptions\DataException * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - protected function maxMinAvgSum($select = '', $alias = '', $type = 'MAX') + protected function maxMinAvgSum(string $select = '', string $alias = '', string $type = 'MAX') { - if (! is_string($select) || $select === '') + if ($select === '') { - throw new DatabaseException('The query you submitted is not valid.'); + throw DataException::forEmptyInputGiven('Select'); + } + + if (strpos($select, ',') !== false) + { + throw DataException::forInvalidArgument('column name not separated by comma'); } $type = strtoupper($type); @@ -424,7 +440,7 @@ * * @return string */ - protected function createAliasFromTable($item) + protected function createAliasFromTable(string $item): string { if (strpos($item, '.') !== false) { @@ -447,9 +463,9 @@ * * @return BaseBuilder */ - public function distinct($val = true) + public function distinct(bool $val = true) { - $this->QBDistinct = is_bool($val) ? $val : true; + $this->QBDistinct = $val; return $this; } @@ -466,7 +482,7 @@ * * @return BaseBuilder */ - public function from($from, $overwrite = false) + public function from($from, bool $overwrite = false) { if ($overwrite === true) { @@ -508,14 +524,14 @@ * * Generates the JOIN portion of the query * - * @param string $table - * @param string $cond The join condition - * @param string $type The type of join - * @param string $escape Whether not to try to escape identifiers + * @param string $table + * @param string $cond The join condition + * @param string $type The type of join + * @param boolean $escape Whether not to try to escape identifiers * * @return BaseBuilder */ - public function join($table, $cond, $type = '', $escape = null) + public function join(string $table, string $cond, string $type = '', bool $escape = null) { if ($type !== '') { @@ -603,7 +619,7 @@ * * @return BaseBuilder */ - public function where($key, $value = null, $escape = null) + public function where($key, $value = null, bool $escape = null) { return $this->whereHaving('QBWhere', $key, $value, 'AND ', $escape); } @@ -622,7 +638,7 @@ * * @return BaseBuilder */ - public function orWhere($key, $value = null, $escape = null) + public function orWhere($key, $value = null, bool $escape = null) { return $this->whereHaving('QBWhere', $key, $value, 'OR ', $escape); } @@ -645,7 +661,7 @@ * * @return BaseBuilder */ - protected function whereHaving($qb_key, $key, $value = null, $type = 'AND ', $escape = null) + protected function whereHaving(string $qb_key, $key, $value = null, string $type = 'AND ', bool $escape = null) { if (! is_array($key)) { @@ -661,8 +677,21 @@ if ($v !== null) { - $op = $this->getOperator($k); - $k = trim(str_replace($op, '', $k)); + $op = $this->getOperator($k, true); + + if (! empty($op)) + { + $k = trim($k); + + end($op); + + $op = trim(current($op)); + + if (substr($k, -1 * strlen($op)) === $op) + { + $k = rtrim(strrev(preg_replace(strrev('/' . $op . '/'), strrev(''), strrev($k), 1))); + } + } $bind = $this->setBind($k, $v, $escape); @@ -674,6 +703,8 @@ { $k .= $op; } + + $v = " :$bind:"; } elseif (! $this->hasOperator($k)) { @@ -685,8 +716,6 @@ $k = substr($k, 0, $match[0][1]) . ($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL'); } - $v = ! is_null($v) ? " :$bind:" : $v; - $this->{$qb_key}[] = [ 'condition' => $prefix . $k . $v, 'escape' => $escape, @@ -710,7 +739,7 @@ * * @return BaseBuilder */ - public function whereIn($key = null, $values = null, $escape = null) + public function whereIn(string $key = null, array $values = null, bool $escape = null) { return $this->_whereIn($key, $values, false, 'AND ', $escape); } @@ -729,7 +758,7 @@ * * @return BaseBuilder */ - public function orWhereIn($key = null, $values = null, $escape = null) + public function orWhereIn(string $key = null, array $values = null, bool $escape = null) { return $this->_whereIn($key, $values, false, 'OR ', $escape); } @@ -748,7 +777,7 @@ * * @return BaseBuilder */ - public function whereNotIn($key = null, $values = null, $escape = null) + public function whereNotIn(string $key = null, array $values = null, bool $escape = null) { return $this->_whereIn($key, $values, true, 'AND ', $escape); } @@ -767,7 +796,7 @@ * * @return BaseBuilder */ - public function orWhereNotIn($key = null, $values = null, $escape = null) + public function orWhereNotIn(string $key = null, array $values = null, bool $escape = null) { return $this->_whereIn($key, $values, true, 'OR ', $escape); } @@ -790,18 +819,13 @@ * * @return BaseBuilder */ - protected function _whereIn($key = null, $values = null, $not = false, $type = 'AND ', $escape = null) + protected function _whereIn(string $key = null, array $values = null, bool $not = false, string $type = 'AND ', bool $escape = null) { if ($key === null || $values === null) { return $this; } - if (! is_array($values)) - { - $values = [$values]; - } - is_bool($escape) || $escape = $this->db->protectIdentifiers; $ok = $key; @@ -844,7 +868,7 @@ * * @return BaseBuilder */ - public function like($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + public function like($field, string $match = '', string $side = 'both', bool $escape = null, bool $insensitiveSearch = false) { return $this->_like($field, $match, 'AND ', $side, '', $escape, $insensitiveSearch); } @@ -865,7 +889,7 @@ * * @return BaseBuilder */ - public function notLike($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + public function notLike($field, string $match = '', string $side = 'both', bool $escape = null, bool $insensitiveSearch = false) { return $this->_like($field, $match, 'AND ', $side, 'NOT', $escape, $insensitiveSearch); } @@ -886,7 +910,7 @@ * * @return BaseBuilder */ - public function orLike($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + public function orLike($field, string $match = '', string $side = 'both', bool $escape = null, bool $insensitiveSearch = false) { return $this->_like($field, $match, 'OR ', $side, '', $escape, $insensitiveSearch); } @@ -907,7 +931,7 @@ * * @return BaseBuilder */ - public function orNotLike($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + public function orNotLike($field, string $match = '', string $side = 'both', bool $escape = null, bool $insensitiveSearch = false) { return $this->_like($field, $match, 'OR ', $side, 'NOT', $escape, $insensitiveSearch); } @@ -932,7 +956,7 @@ * * @return BaseBuilder */ - protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = null, $insensitiveSearch = false) + protected function _like($field, string $match = '', string $type = 'AND ', string $side = 'both', string $not = '', bool $escape = null, bool $insensitiveSearch = false) { if (! is_array($field)) { @@ -992,17 +1016,16 @@ /** * Platform independent LIKE statement builder. * - * @param string|null $prefix - * @param string $column - * @param string|null $not - * @param string $bind - * @param boolean $insensitiveSearch + * @param string $prefix + * @param string $column + * @param string $not + * @param string $bind + * @param boolean $insensitiveSearch * * @return string $like_statement */ public function _like_statement(string $prefix = null, string $column, string $not = null, string $bind, bool $insensitiveSearch = false): string { - // TODO fmertins: $column param seems to require a default value? Because $prefix has... $like_statement = "{$prefix} {$column} {$not} LIKE :{$bind}:"; if ($insensitiveSearch === true) @@ -1023,7 +1046,7 @@ * * @return BaseBuilder */ - public function groupStart($not = '', $type = 'AND ') + public function groupStart(string $not = '', string $type = 'AND ') { $type = $this->groupGetType($type); @@ -1109,11 +1132,11 @@ * * @return string */ - protected function groupGetType($type) + protected function groupGetType(string $type): string { if ($this->QBWhereGroupStarted) { - $type = ''; + $type = ''; $this->QBWhereGroupStarted = false; } @@ -1125,12 +1148,12 @@ /** * GROUP BY * - * @param string $by - * @param boolean $escape + * @param string|array $by + * @param boolean $escape * * @return BaseBuilder */ - public function groupBy($by, $escape = null) + public function groupBy($by, bool $escape = null) { is_bool($escape) || $escape = $this->db->protectIdentifiers; @@ -1164,13 +1187,13 @@ * * Separates multiple calls with 'AND'. * - * @param string $key - * @param string $value - * @param boolean $escape + * @param string|array $key + * @param mixed $value + * @param boolean $escape * * @return BaseBuilder */ - public function having($key, $value = null, $escape = null) + public function having($key, $value = null, bool $escape = null) { return $this->whereHaving('QBHaving', $key, $value, 'AND ', $escape); } @@ -1182,13 +1205,13 @@ * * Separates multiple calls with 'OR'. * - * @param string $key - * @param string $value - * @param boolean $escape + * @param string|array $key + * @param mixed $value + * @param boolean $escape * * @return BaseBuilder */ - public function orHaving($key, $value = null, $escape = null) + public function orHaving($key, $value = null, bool $escape = null) { return $this->whereHaving('QBHaving', $key, $value, 'OR ', $escape); } @@ -1198,13 +1221,13 @@ /** * ORDER BY * - * @param string $orderby + * @param string $orderBy * @param string $direction ASC, DESC or RANDOM * @param boolean $escape * * @return BaseBuilder */ - public function orderBy($orderby, $direction = '', $escape = null) + public function orderBy(string $orderBy, string $direction = '', bool $escape = null) { $direction = strtoupper(trim($direction)); @@ -1213,9 +1236,9 @@ $direction = ''; // Do we have a seed value? - $orderby = ctype_digit((string) $orderby) ? sprintf($this->randomKeyword[1], $orderby) : $this->randomKeyword[0]; + $orderBy = ctype_digit((string) $orderBy) ? sprintf($this->randomKeyword[1], $orderBy) : $this->randomKeyword[0]; } - elseif (empty($orderby)) + elseif (empty($orderBy)) { return $this; } @@ -1229,7 +1252,7 @@ if ($escape === false) { $qb_orderby[] = [ - 'field' => $orderby, + 'field' => $orderBy, 'direction' => $direction, 'escape' => false, ]; @@ -1237,18 +1260,21 @@ else { $qb_orderby = []; - foreach (explode(',', $orderby) as $field) + foreach (explode(',', $orderBy) as $field) { - $qb_orderby[] = ($direction === '' && - preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE)) ? [ - 'field' => ltrim(substr($field, 0, $match[0][1])), - 'direction' => ' ' . $match[1][0], - 'escape' => true, - ] : [ - 'field' => trim($field), - 'direction' => $direction, - 'escape' => true, - ]; + $qb_orderby[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE)) + ? + [ + 'field' => ltrim(substr($field, 0, $match[0][1])), + 'direction' => ' ' . $match[1][0], + 'escape' => true, + ] + : + [ + 'field' => trim($field), + 'direction' => $direction, + 'escape' => true, + ]; } } @@ -1291,7 +1317,7 @@ * * @return BaseBuilder */ - public function offset($offset) + public function offset(int $offset) { if (! empty($offset)) { @@ -1312,7 +1338,7 @@ * * @return string */ - protected function _limit($sql) + protected function _limit(string $sql): string { return $sql . ' LIMIT ' . ($this->QBOffset ? $this->QBOffset . ', ' : '') . $this->QBLimit; } @@ -1330,7 +1356,7 @@ * * @return BaseBuilder */ - public function set($key, $value = '', $escape = null) + public function set($key, string $value = '', bool $escape = null) { $key = $this->objectToArray($key); @@ -1345,7 +1371,7 @@ { if ($escape) { - $bind = $this->setBind($k, $v, $escape); + $bind = $this->setBind($k, $v, $escape); $this->QBSet[$this->db->protectIdentifiers($k, false, $escape)] = ":$bind:"; } else @@ -1367,7 +1393,7 @@ * * @return array */ - public function getSetData(bool $clean = false) + public function getSetData(bool $clean = false): array { $data = $this->QBSet; @@ -1390,7 +1416,7 @@ * * @return string */ - public function getCompiledSelect($reset = true) + public function getCompiledSelect(bool $reset = true): string { $select = $this->compileSelect(); @@ -1410,7 +1436,7 @@ * * @param string $sql * - * @return mixed|string + * @return string */ protected function compileFinalQuery(string $sql): string { @@ -1438,7 +1464,7 @@ * * @return ResultInterface */ - public function get(int $limit = null, int $offset = 0, $returnSQL = false, $reset = true) + public function get(int $limit = null, int $offset = 0, bool $returnSQL = false, bool $reset = true) { if (! is_null($limit)) { @@ -1471,9 +1497,9 @@ * @param boolean $reset Are we want to clear query builder values? * @param boolean $test Are we running automated tests? * - * @return integer + * @return integer|string when $test = true */ - public function countAll($reset = true, $test = false) + public function countAll(bool $reset = true, bool $test = false) { $table = $this->QBFrom[0]; @@ -1512,22 +1538,25 @@ * @param boolean $reset * @param boolean $test The reset clause * - * @return integer + * @return integer|string when $test = true */ - public function countAllResults($reset = true, $test = false) + public function countAllResults(bool $reset = true, bool $test = false) { // ORDER BY usage is often problematic here (most notably // on Microsoft SQL Server) and ultimately unnecessary // for selecting COUNT(*) ... - $orderby = []; + $orderBy = []; if (! empty($this->QBOrderBy)) { - $orderby = $this->QBOrderBy; + $orderBy = $this->QBOrderBy; $this->QBOrderBy = null; } - $sql = ($this->QBDistinct === true) ? $this->countString . $this->db->protectIdentifiers('numrows') . "\nFROM (\n" . - $this->compileSelect() . "\n) CI_count_all_results" : $this->compileSelect($this->countString . $this->db->protectIdentifiers('numrows')); + $sql = ($this->QBDistinct === true) + ? + $this->countString . $this->db->protectIdentifiers('numrows') . "\nFROM (\n" . $this->compileSelect() . "\n) CI_count_all_results" + : + $this->compileSelect($this->countString . $this->db->protectIdentifiers('numrows')); if ($test) { @@ -1543,7 +1572,7 @@ // If we've previously reset the QBOrderBy values, get them back elseif (! isset($this->QBOrderBy)) { - $this->QBOrderBy = $orderby ?? []; + $this->QBOrderBy = $orderBy ?? []; } $row = (! $result instanceof ResultInterface) @@ -1565,13 +1594,13 @@ * * Allows the where clause, limit and offset to be added directly * - * @param string $where - * @param integer $limit - * @param integer $offset + * @param string|array $where + * @param integer $limit + * @param integer $offset * * @return ResultInterface */ - public function getWhere($where = null, $limit = null, $offset = null) + public function getWhere($where = null, int $limit = null, int $offset = null) { if ($where !== null) { @@ -1605,7 +1634,7 @@ * @return integer Number of rows inserted or FALSE on failure * @throws DatabaseException */ - public function insertBatch($set = null, $escape = null, $batchSize = 100, $testing = false) + public function insertBatch(array $set = null, bool $escape = null, int $batchSize = 100, bool $testing = false) { if ($set === null) { @@ -1674,7 +1703,7 @@ * * @return string */ - protected function _insertBatch($table, $keys, $values) + protected function _insertBatch(string $table, array $keys, array $values): string { return 'INSERT INTO ' . $table . ' (' . implode(', ', $keys) . ') VALUES ' . implode(', ', $values); } @@ -1688,9 +1717,9 @@ * @param string $value * @param boolean $escape * - * @return BaseBuilder + * @return BaseBuilder|null */ - public function setInsertBatch($key, $value = '', $escape = null) + public function setInsertBatch($key, string $value = '', bool $escape = null) { $key = $this->batchObjectToArray($key); @@ -1712,7 +1741,7 @@ // batch function above returns an error on an empty array $this->QBSet[] = []; - return; + return null; } ksort($row); // puts $row in the same order as our keys @@ -1747,7 +1776,7 @@ * * @return string */ - public function getCompiledInsert($reset = true) + public function getCompiledInsert(bool $reset = true): string { if ($this->validateInsert() === false) { @@ -1781,7 +1810,7 @@ * * @return BaseResult|Query|false */ - public function insert($set = null, $escape = null, $test = false) + public function insert(array $set = null, bool $escape = null, bool $test = false) { if ($set !== null) { @@ -1810,6 +1839,8 @@ return $result; } + + return false; } //-------------------------------------------------------------------- @@ -1821,10 +1852,10 @@ * validate that the there data is actually being set and that table * has been chosen to be inserted into. * - * @return string + * @return bool * @throws DatabaseException */ - protected function validateInsert() + protected function validateInsert(): bool { if (empty($this->QBSet)) { @@ -1852,7 +1883,7 @@ * * @return string */ - protected function _insert($table, array $keys, array $unescapedKeys) + protected function _insert(string $table, array $keys, array $unescapedKeys): string { return 'INSERT INTO ' . $table . ' (' . implode(', ', $keys) . ') VALUES (' . implode(', ', $unescapedKeys) . ')'; } @@ -1870,7 +1901,7 @@ * @return BaseResult|Query|string|false * @throws DatabaseException */ - public function replace($set = null, $returnSQL = false) + public function replace(array $set = null, bool $returnSQL = false) { if ($set !== null) { @@ -1908,7 +1939,7 @@ * * @return string */ - protected function _replace($table, $keys, $values) + protected function _replace(string $table, array $keys, array $values): string { return 'REPLACE INTO ' . $table . ' (' . implode(', ', $keys) . ') VALUES (' . implode(', ', $values) . ')'; } @@ -1925,7 +1956,7 @@ * * @return string */ - protected function _fromTables() + protected function _fromTables(): string { return implode(', ', $this->QBFrom); } @@ -1941,7 +1972,7 @@ * * @return string */ - public function getCompiledUpdate($reset = true) + public function getCompiledUpdate(bool $reset = true): string { if ($this->validateUpdate() === false) { @@ -1972,7 +2003,7 @@ * * @return boolean TRUE on success, FALSE on failure */ - public function update($set = null, $where = null, int $limit = null, $test = false) + public function update(array $set = null, $where = null, int $limit = null, bool $test = false): bool { if ($set !== null) { @@ -2031,8 +2062,10 @@ * * @return string */ - protected function _update($table, $values) + protected function _update(string $table, array $values): string { + $valstr = []; + foreach ($values as $key => $val) { $valstr[] = $key . ' = ' . $val; @@ -2056,7 +2089,7 @@ * @return boolean * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - protected function validateUpdate() + protected function validateUpdate(): bool { if (empty($this->QBSet)) { @@ -2086,7 +2119,7 @@ * @return mixed Number of rows affected, SQL string, or FALSE on failure * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function updateBatch($set = null, $index = null, $batchSize = 100, $returnSQL = false) + public function updateBatch(array $set = null, string $index = null, int $batchSize = 100, bool $returnSQL = false) { if ($index === null) { @@ -2127,7 +2160,7 @@ // Batch this baby $affected_rows = 0; - $savedSQL = []; + $savedSQL = []; for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batchSize) { $sql = $this->_updateBatch($table, array_slice($this->QBSet, $i, $batchSize), $this->db->protectIdentifiers($index) @@ -2164,9 +2197,10 @@ * * @return string */ - protected function _updateBatch($table, $values, $index) + protected function _updateBatch(string $table, array $values, string $index): string { $ids = []; + $final = []; foreach ($values as $key => $val) { $ids[] = $val[$index]; @@ -2198,20 +2232,20 @@ /** * The "setUpdateBatch" function. Allows key/value pairs to be set for batch updating * - * @param array $key - * @param string $index - * @param boolean $escape + * @param array|object $key + * @param string $index + * @param boolean $escape * - * @return BaseBuilder + * @return BaseBuilder|null * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function setUpdateBatch($key, $index = '', $escape = null) + public function setUpdateBatch($key, string $index = '', bool $escape = null) { $key = $this->batchObjectToArray($key); if (! is_array($key)) { - return; + return null; } is_bool($escape) || $escape = $this->db->protectIdentifiers; @@ -2253,7 +2287,7 @@ * @param boolean $test * @return boolean TRUE on success, FALSE on failure */ - public function emptyTable($test = false) + public function emptyTable(bool $test = false) { $table = $this->QBFrom[0]; @@ -2282,7 +2316,7 @@ * * @return boolean TRUE on success, FALSE on failure */ - public function truncate($test = false) + public function truncate(bool $test = false) { $table = $this->QBFrom[0]; @@ -2312,7 +2346,7 @@ * * @return string */ - protected function _truncate($table) + protected function _truncate(string $table): string { return 'TRUNCATE ' . $table; } @@ -2328,13 +2362,11 @@ * * @return string */ - public function getCompiledDelete($reset = true) + public function getCompiledDelete(bool $reset = true): string { $table = $this->QBFrom[0]; - $this->returnDeleteSQL = true; - $sql = $this->delete($table, '', null, $reset); - $this->returnDeleteSQL = false; + $sql = $this->delete($table, '', $reset, true); return $this->compileFinalQuery($sql); } @@ -2347,14 +2379,14 @@ * Compiles a delete string and runs the query * * @param mixed $where The where clause - * @param mixed $limit The limit clause + * @param integer $limit The limit clause * @param boolean $reset_data * @param boolean $returnSQL * * @return mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function delete($where = '', $limit = null, $reset_data = true, $returnSQL = false) + public function delete($where = '', int $limit = null, bool $reset_data = true, bool $returnSQL = false) { $table = $this->db->protectIdentifiers($this->QBFrom[0], true, null, false); @@ -2447,7 +2479,7 @@ * * @return string */ - protected function _delete($table) + protected function _delete(string $table): string { return 'DELETE FROM ' . $table . $this->compileWhereHaving('QBWhere') . ($this->QBLimit ? ' LIMIT ' . $this->QBLimit : ''); @@ -2460,9 +2492,9 @@ * * Used to track SQL statements written with aliased tables. * - * @param string $table The table to inspect + * @param string|array $table The table to inspect * - * @return string + * @return string|void */ protected function trackAliases($table) { @@ -2472,8 +2504,6 @@ { $this->trackAliases($t); } - - return; } // Does the string contain a comma? If so, we need to separate @@ -2505,11 +2535,11 @@ * Generates a query string based on which functions were used. * Should not be called directly. * - * @param boolean $select_override + * @param mixed $select_override * * @return string */ - protected function compileSelect($select_override = false) + protected function compileSelect($select_override = false): string { // Write the "select" portion of the query if ($select_override !== false) @@ -2579,7 +2609,7 @@ * * @return string SQL statement */ - protected function compileWhereHaving($qb_key) + protected function compileWhereHaving(string $qb_key): string { if (! empty($this->$qb_key)) { @@ -2621,7 +2651,6 @@ if (! empty($matches[4])) { - // $this->isLiteral($matches[4]) OR $matches[4] = $this->db->protectIdentifiers(trim($matches[4])); $matches[4] = ' ' . $matches[4]; } @@ -2646,13 +2675,13 @@ * * Escapes identifiers in GROUP BY statements at execution time. * - * Required so that aliases are tracked properly, regardless of wether + * Required so that aliases are tracked properly, regardless of whether * groupBy() is called prior to from(), join() and prefixTable is added * only if needed. * * @return string SQL statement */ - protected function compileGroupBy() + protected function compileGroupBy(): string { if (! empty($this->QBGroupBy)) { @@ -2681,13 +2710,13 @@ * * Escapes identifiers in ORDER BY statements at execution time. * - * Required so that aliases are tracked properly, regardless of wether + * Required so that aliases are tracked properly, regardless of whether * orderBy() is called prior to from(), join() and prefixTable is added * only if needed. * * @return string SQL statement */ - protected function compileOrderBy() + protected function compileOrderBy(): string { if (is_array($this->QBOrderBy) && ! empty($this->QBOrderBy)) { @@ -2718,9 +2747,9 @@ * * Takes an object as input and converts the class variables to array key/vals * - * @param object $object + * @param mixed $object * - * @return array + * @return mixed */ protected function objectToArray($object) { @@ -2749,9 +2778,9 @@ * * Takes an object as input and converts the class variables to array key/vals * - * @param object $object + * @param mixed $object * - * @return array + * @return mixed */ protected function batchObjectToArray($object) { @@ -2791,7 +2820,7 @@ * * @return boolean */ - protected function isLiteral($str) + protected function isLiteral(string $str): bool { $str = trim($str); @@ -2835,8 +2864,10 @@ * Resets the query builder values. Called by the get() function * * @param array $qb_reset_items An array of fields to reset + * + * @return void */ - protected function resetRun($qb_reset_items) + protected function resetRun(array $qb_reset_items) { foreach ($qb_reset_items as $item => $default_value) { @@ -2898,7 +2929,7 @@ * * @return boolean */ - protected function hasOperator($str) + protected function hasOperator(string $str): bool { return (bool) preg_match('/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', trim($str)); } @@ -2908,11 +2939,12 @@ /** * Returns the SQL string operator * - * @param string $str + * @param string $str + * @param boolean $list * - * @return string + * @return mixed */ - protected function getOperator($str) + protected function getOperator(string $str, bool $list = false) { static $_operators; @@ -2935,7 +2967,7 @@ ]; } - return preg_match('/' . implode('|', $_operators) . '/i', $str, $match) ? $match[0] : false; + return preg_match_all('/' . implode('|', $_operators) . '/i', $str, $match) ? ($list ? $match[0] : $match[0][count($match[0]) - 1]) : false; } // -------------------------------------------------------------------- @@ -2947,12 +2979,12 @@ * arrays instead, so lets take advantage of that here. * * @param string $key - * @param null $value + * @param mixed $value * @param boolean $escape * * @return string */ - protected function setBind(string $key, $value = null, bool $escape = true) + protected function setBind(string $key, $value = null, bool $escape = true): string { if (! array_key_exists($key, $this->binds)) { diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 477574d..7e16330 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1,4 +1,4 @@ -pretend) { - // Let others do somethign with this query + // Let others do something with this query Events::trigger('DBQuery', $query); } @@ -1093,10 +1095,10 @@ // This is basically a bug fix for queries that use MAX, MIN, etc. // If a parenthesis is found we know that we do not need to // escape the data or add a prefix. There's probably a more graceful - // way to deal with this, but I'm not thinking of it -- Rick + // way to deal with this, but I'm not thinking of it // // Added exception for single quotes as well, we don't want to alter - // literal strings. -- Narf + // literal strings. if (strcspn($item, "()'") !== strlen($item)) { return $item; diff --git a/system/Database/BasePreparedQuery.php b/system/Database/BasePreparedQuery.php index 9c9681f..887f906 100644 --- a/system/Database/BasePreparedQuery.php +++ b/system/Database/BasePreparedQuery.php @@ -1,5 +1,4 @@ -createDatabaseStr === false) { @@ -242,7 +244,7 @@ * @return boolean * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function dropDatabase($db_name) + public function dropDatabase(string $db_name): bool { if ($this->dropDatabaseStr === false) { @@ -392,7 +394,7 @@ * @return \CodeIgniter\Database\Forge * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function addForeignKey($fieldName = '', $tableName = '', $tableField = '', $onUpdate = false, $onDelete = false) + public function addForeignKey(string $fieldName = '', string $tableName = '', string $tableField = '', bool $onUpdate = false, bool $onDelete = false) { if (! isset($this->fields[$fieldName])) { @@ -420,7 +422,7 @@ * @return boolean|\CodeIgniter\Database\BaseResult|\CodeIgniter\Database\Query|false|mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function dropForeignKey($table, $foreign_name) + public function dropForeignKey(string $table, string $foreign_name) { $sql = sprintf($this->dropConstraintStr, $this->db->escapeIdentifiers($this->db->DBPrefix . $table), $this->db->escapeIdentifiers($this->db->DBPrefix . $foreign_name)); @@ -447,10 +449,10 @@ * @param boolean $if_not_exists Whether to add IF NOT EXISTS condition * @param array $attributes Associative array of table attributes * - * @return boolean + * @return mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function createTable($table, $if_not_exists = false, array $attributes = []) + public function createTable(string $table, bool $if_not_exists = false, array $attributes = []) { if ($table === '') { @@ -482,7 +484,7 @@ if (($result = $this->db->query($sql)) !== false) { - empty($this->db->dataCache['table_names']) || $this->db->dataCache['table_names'][] = $table; + empty($this->db->dataCache['table_names']) || ($this->db->dataCache['table_names'][] = $table); // Most databases don't support creating indexes from within the CREATE TABLE statement if (! empty($this->keys)) @@ -510,7 +512,7 @@ * * @return mixed */ - protected function _createTable($table, $if_not_exists, $attributes) + protected function _createTable(string $table, bool $if_not_exists, array $attributes) { // For any platforms that don't support Create If Not Exists... if ($if_not_exists === true && $this->createTableIfStr === false) @@ -560,7 +562,7 @@ * * @return string */ - protected function _createTableAttributes($attributes) + protected function _createTableAttributes(array $attributes): string { $sql = ''; @@ -587,7 +589,7 @@ * @return mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function dropTable($table_name, $if_exists = false, $cascade = false) + public function dropTable(string $table_name, bool $if_exists = false, bool $cascade = false) { if ($table_name === '') { @@ -639,7 +641,7 @@ * * @return string */ - protected function _dropTable($table, $if_exists, $cascade) + protected function _dropTable(string $table, bool $if_exists, bool $cascade): string { $sql = 'DROP TABLE'; @@ -674,7 +676,7 @@ * @return mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function renameTable($table_name, $new_table_name) + public function renameTable(string $table_name, string $new_table_name) { if ($table_name === '' || $new_table_name === '') { @@ -713,13 +715,13 @@ /** * Column Add * - * @param string $table Table name - * @param array $field Column definition + * @param string $table Table name + * @param string|array $field Column definition * * @return boolean * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function addColumn($table, $field) + public function addColumn(string $table, $field): bool { // Work-around for literal column definitions is_array($field) || $field = [$field]; @@ -760,10 +762,10 @@ * @param string $table Table name * @param string $column_name Column name * - * @return boolean + * @return mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function dropColumn($table, $column_name) + public function dropColumn(string $table, string $column_name) { $sql = $this->_alterTable('DROP', $this->db->DBPrefix . $table, $column_name); if ($sql === false) @@ -784,13 +786,13 @@ /** * Column Modify * - * @param string $table Table name - * @param string $field Column definition + * @param string $table Table name + * @param string|array $field Column definition * * @return boolean * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function modifyColumn($table, $field) + public function modifyColumn(string $table, $field): bool { // Work-around for literal column definitions is_array($field) || $field = [$field]; @@ -842,7 +844,7 @@ * * @return string|string[] */ - protected function _alterTable($alter_type, $table, $field) + protected function _alterTable(string $alter_type, string $table, $field) { $sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table) . ' '; @@ -873,7 +875,7 @@ * * @return array */ - protected function _processFields($create_table = false) + protected function _processFields(bool $create_table = false): array { $fields = []; @@ -973,7 +975,7 @@ * * @return string */ - protected function _processColumn($field) + protected function _processColumn(array $field): string { return $this->db->escapeIdentifiers($field['name']) . ' ' . $field['type'] . $field['length'] @@ -995,7 +997,7 @@ * * @return void */ - protected function _attributeType(&$attributes) + protected function _attributeType(array &$attributes) { // Usually overridden by drivers } @@ -1017,9 +1019,9 @@ * @param array &$attributes * @param array &$field * - * @return void + * @return null|void */ - protected function _attributeUnsigned(&$attributes, &$field) + protected function _attributeUnsigned(array &$attributes, array &$field) { if (empty($attributes['UNSIGNED']) || $attributes['UNSIGNED'] !== true) { @@ -1061,9 +1063,9 @@ * @param array &$attributes * @param array &$field * - * @return void + * @return null|void */ - protected function _attributeDefault(&$attributes, &$field) + protected function _attributeDefault(array &$attributes, array &$field) { if ($this->default === false) { @@ -1097,7 +1099,7 @@ * * @return void */ - protected function _attributeUnique(&$attributes, &$field) + protected function _attributeUnique(array &$attributes, array &$field) { if (! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === true) { @@ -1115,7 +1117,7 @@ * * @return void */ - protected function _attributeAutoIncrement(&$attributes, &$field) + protected function _attributeAutoIncrement(array &$attributes, array &$field) { if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true && stripos($field['type'], 'int') !== false @@ -1134,7 +1136,7 @@ * * @return string */ - protected function _processPrimaryKeys($table) + protected function _processPrimaryKeys(string $table): string { $sql = ''; @@ -1164,7 +1166,7 @@ * * @return array */ - protected function _processIndexes($table) + protected function _processIndexes(string $table) { $sqls = []; @@ -1209,7 +1211,7 @@ * * @return string */ - protected function _processForeignKeys($table) + protected function _processForeignKeys(string $table): string { $sql = ''; diff --git a/system/Database/Migration.php b/system/Database/Migration.php index 3efef03..eb27199 100644 --- a/system/Database/Migration.php +++ b/system/Database/Migration.php @@ -1,4 +1,4 @@ -checkMigrations($migrations, $method, $targetVersion); // loop migration for each namespace (module) + + $migrationStatus = false; foreach ($migrations as $version => $migration) { // Only include migrations within the scoop - if (($method === 'up' && $version > $currentVersion && $version <= $targetVersion) || ( $method === 'down' && $version <= $currentVersion && $version > $targetVersion) - ) + if (($method === 'up' && $version > $currentVersion && $version <= $targetVersion) || ( $method === 'down' && $version <= $currentVersion && $version > $targetVersion)) { + $migrationStatus = false; include_once $migration->path; // Get namespaced class name $class = $this->namespace . '\Database\Migrations\Migration_' . ($migration->name); @@ -288,10 +291,12 @@ { $this->removeHistory($migration->version); } + + $migrationStatus = true; } } - return true; + return ($migrationStatus) ? $targetVersion : false; } //-------------------------------------------------------------------- @@ -347,11 +352,10 @@ $this->setGroup($group); } - // Get all namespaces form PSR4 paths. - $config = config('Autoload'); - $namespaces = $config->psr4; + // Get all namespaces from the autoloader + $namespaces = Services::autoloader()->getNamespace(); - foreach ($namespaces as $namespace => $path) + foreach ($namespaces as $namespace => $paths) { $this->setNamespace($namespace); $migrations = $this->findMigrations(); @@ -401,36 +405,30 @@ //-------------------------------------------------------------------- /** - * Retrieves list of available migration scripts + * Retrieves list of available migration scripts for one namespace * * @return array list of migrations as $version for one namespace */ public function findMigrations() { $migrations = []; - helper('filesystem'); // If $this->path contains a valid directory use it. if (! empty($this->path)) { - $dir = rtrim($this->path, DIRECTORY_SEPARATOR) . '/'; + helper('filesystem'); + $dir = rtrim($this->path, DIRECTORY_SEPARATOR) . '/'; + $files = get_filenames($dir, true); } - // Otherwise, get namespace location form PSR4 paths - // and add Database/Migrations for a standard loation. + // Otherwise use FileLocator to search files in the subdirectory of the namespace else { - $config = config('Autoload'); - - $location = $config->psr4[$this->namespace]; - - // Setting migration directories. - $dir = rtrim($location, DIRECTORY_SEPARATOR) . '/Database/Migrations/'; + $locator = Services::locator(true); + $files = $locator->listNamespaceFiles($this->namespace, '/Database/Migrations/'); } // Load all *_*.php files in the migrations path // We can't use glob if we want it to be testable.... - $files = get_filenames($dir, true); - foreach ($files as $file) { if (substr($file, -4) !== '.php') @@ -695,7 +693,7 @@ /** * Retrieves current schema version * - * @return string Current migration version + * @return array Current migration version */ public function getCliMessages() { diff --git a/system/Database/MySQLi/Builder.php b/system/Database/MySQLi/Builder.php index 07cd6c4..264171e 100644 --- a/system/Database/MySQLi/Builder.php +++ b/system/Database/MySQLi/Builder.php @@ -1,4 +1,4 @@ -mysqli->client_info, '5.6', '>=')) { - $this->mysqli->options(MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT, true); + $client_flags += MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; } } - $client_flags |= MYSQLI_CLIENT_SSL; + $client_flags += MYSQLI_CLIENT_SSL; $this->mysqli->ssl_set( $ssl['key'] ?? null, $ssl['cert'] ?? null, $ssl['ca'] ?? null, $ssl['capath'] ?? null, $ssl['cipher'] ?? null diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index bd5be18..188625e 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -1,4 +1,4 @@ -db->escapeIdentifiers($field['after']) : ''; @@ -229,7 +231,7 @@ * @param string $table (ignored) * @return string */ - protected function _processIndexes($table) + protected function _processIndexes(string $table): string { $sql = ''; @@ -257,7 +259,7 @@ $unique = in_array($i, $this->uniqueKeys) ? 'UNIQUE ' : ''; $sql .= ",\n\t{$unique}KEY " . $this->db->escapeIdentifiers(implode('_', $this->keys[$i])) - . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')'; + . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')'; } $this->keys = []; diff --git a/system/Database/MySQLi/PreparedQuery.php b/system/Database/MySQLi/PreparedQuery.php index d17e5b5..668df92 100644 --- a/system/Database/MySQLi/PreparedQuery.php +++ b/system/Database/MySQLi/PreparedQuery.php @@ -1,4 +1,4 @@ - 1 ? "0.{$orderby}" : $orderby); + $orderBy = (float) ($orderBy > 1 ? "0.{$orderBy}" : $orderBy); } - if (is_float($orderby)) + if (is_float($orderBy)) { - $this->db->simpleQuery("SET SEED {$orderby}"); + $this->db->simpleQuery("SET SEED {$orderBy}"); } - $orderby = $this->randomKeyword[0]; + $orderBy = $this->randomKeyword[0]; $direction = ''; $escape = false; } - return parent::orderBy($orderby, $direction, $escape); + return parent::orderBy($orderBy, $direction, $escape); } //-------------------------------------------------------------------- @@ -144,7 +145,7 @@ * @throws DatabaseException * @internal param true $bool returns the generated SQL, false executes the query. */ - public function replace($set = null, $returnSQL = false) + public function replace(array $set = null, bool $returnSQL = false) { if ($set !== null) { @@ -199,8 +200,8 @@ * * Compiles a delete string and runs the query * - * @param string $where - * @param null $limit + * @param mixed $where + * @param integer $limit * @param boolean $reset_data * @param boolean $returnSQL * @@ -210,7 +211,7 @@ * @internal param the $mixed limit clause * @internal param $bool */ - public function delete($where = '', $limit = null, $reset_data = true, $returnSQL = false) + public function delete($where = '', int $limit = null, bool $reset_data = true, bool $returnSQL = false) { if (! empty($limit) || ! empty($this->QBLimit)) { @@ -231,7 +232,7 @@ * * @return string */ - protected function _limit($sql) + protected function _limit(string $sql): string { return $sql . ' LIMIT ' . $this->QBLimit . ($this->QBOffset ? " OFFSET {$this->QBOffset}" : ''); } @@ -251,7 +252,7 @@ * @internal param the $string table name * @internal param the $array update data */ - protected function _update($table, $values) + protected function _update(string $table, array $values): string { if (! empty($this->QBLimit)) { @@ -275,7 +276,7 @@ * * @return string */ - protected function _updateBatch($table, $values, $index) + protected function _updateBatch(string $table, array $values, string $index): string { $ids = []; foreach ($values as $key => $val) @@ -315,7 +316,7 @@ * * @return string */ - protected function _delete($table) + protected function _delete(string $table): string { $this->QBLimit = false; return parent::_delete($table); @@ -335,7 +336,7 @@ * * @return string */ - protected function _truncate($table) + protected function _truncate(string $table): string { return 'TRUNCATE ' . $table . ' RESTART IDENTITY'; } @@ -350,11 +351,11 @@ * * @see https://www.postgresql.org/docs/9.2/static/functions-matching.html * - * @param string|null $prefix - * @param string $column - * @param string|null $not - * @param string $bind - * @param boolean $insensitiveSearch + * @param string $prefix + * @param string $column + * @param string $not + * @param string $bind + * @param boolean $insensitiveSearch * * @return string $like_statement */ diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 1c046e9..56aec4f 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -1,5 +1,4 @@ -db->escapeIdentifiers($field['name']) . ' ' . $field['type'] . $field['length'] @@ -177,7 +179,7 @@ * * @return void */ - protected function _attributeType(&$attributes) + protected function _attributeType(array &$attributes) { // Reset field lengths for data types that don't support it if (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== false) @@ -190,15 +192,16 @@ case 'TINYINT': $attributes['TYPE'] = 'SMALLINT'; $attributes['UNSIGNED'] = false; - return; + break; case 'MEDIUMINT': $attributes['TYPE'] = 'INTEGER'; $attributes['UNSIGNED'] = false; - return; + break; case 'DATETIME': $attributes['TYPE'] = 'TIMESTAMP'; + break; default: - return; + break; } } @@ -212,7 +215,7 @@ * * @return void */ - protected function _attributeAutoIncrement(&$attributes, &$field) + protected function _attributeAutoIncrement(array &$attributes, array &$field) { if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true) { @@ -233,7 +236,7 @@ * * @return string */ - protected function _dropTable($table, $if_exists, $cascade) + protected function _dropTable(string $table, bool $if_exists, bool $cascade): string { $sql = parent::_dropTable($table, $if_exists, $cascade); diff --git a/system/Database/Postgre/PreparedQuery.php b/system/Database/Postgre/PreparedQuery.php index 2864985..fdd0aa4 100644 --- a/system/Database/Postgre/PreparedQuery.php +++ b/system/Database/Postgre/PreparedQuery.php @@ -1,4 +1,4 @@ -endTime - $this->startTime), $decimals); } @@ -265,7 +266,7 @@ * @param integer $code * @param string $error * - * @return Query + * @return $this */ public function setError(int $code, string $error) { @@ -332,7 +333,7 @@ * @param string $orig * @param string $swap * - * @return mixed + * @return $this */ public function swapPrefix(string $orig, string $swap) { @@ -350,7 +351,7 @@ * * @return string */ - public function getOriginalQuery() + public function getOriginalQuery(): string { return $this->originalQueryString; } @@ -359,6 +360,8 @@ /** * Escapes and inserts any binds into the finalQueryString object. + * + * @return null|void */ protected function compileBinds() { @@ -416,7 +419,7 @@ * @param array $binds * @return string */ - protected function matchNamedBinds(string $sql, array $binds) + protected function matchNamedBinds(string $sql, array $binds): string { $replacers = []; @@ -452,7 +455,7 @@ * @param integer $ml * @return string */ - protected function matchSimpleBinds(string $sql, array $binds, int $bindCount, int $ml) + protected function matchSimpleBinds(string $sql, array $binds, int $bindCount, int $ml): string { // Make sure not to replace a chunk inside a string that happens to match the bind marker if ($c = preg_match_all("/'[^']*'/i", $sql, $matches)) @@ -473,8 +476,8 @@ do { - $c --; - $escapedValue = $binds[$c][1] ? $this->db->escape($binds[$c][0]) : $binds[$c[0]]; + $c--; + $escapedValue = $binds[$c][1] ? $this->db->escape($binds[$c][0]) : $binds[$c][0]; if (is_array($escapedValue)) { $escapedValue = '(' . implode(',', $escapedValue) . ')'; @@ -491,9 +494,9 @@ /** * Return text representation of the query * - * @return mixed|string + * @return string */ - public function __toString() + public function __toString(): string { return $this->getQuery(); } diff --git a/system/Database/QueryInterface.php b/system/Database/QueryInterface.php index fe24a8c..7f5cfb1 100644 --- a/system/Database/QueryInterface.php +++ b/system/Database/QueryInterface.php @@ -1,4 +1,4 @@ -viewDirectory . '/errors/'; + $path = $paths->viewDirectory . '/errors/'; } $path = is_cli() @@ -298,7 +300,7 @@ * * @return array */ - protected function collectVars(\Throwable $exception, int $statusCode) + protected function collectVars(\Throwable $exception, int $statusCode): array { return [ 'title' => get_class($exception), @@ -356,7 +358,7 @@ * * @return string */ - public static function cleanPath($file) + public static function cleanPath(string $file): string { if (strpos($file, APPPATH) === 0) { @@ -403,13 +405,13 @@ /** * Creates a syntax-highlighted version of a PHP file. * - * @param $file - * @param $lineNumber - * @param integer $lines + * @param string $file + * @param integer $lineNumber + * @param integer $lines * * @return boolean|string */ - public static function highlightFile($file, $lineNumber, $lines = 15) + public static function highlightFile(string $file, int $lineNumber, int $lines = 15) { if (empty($file) || ! is_readable($file)) { diff --git a/system/Debug/Iterator.php b/system/Debug/Iterator.php index a8c0e33..2a8d332 100644 --- a/system/Debug/Iterator.php +++ b/system/Debug/Iterator.php @@ -1,5 +1,4 @@ -results)) { diff --git a/system/Debug/Timer.php b/system/Debug/Timer.php index b124a38..11c228c 100644 --- a/system/Debug/Timer.php +++ b/system/Debug/Timer.php @@ -1,4 +1,4 @@ -timers; @@ -172,7 +174,7 @@ * * @return boolean */ - public function has(string $name) + public function has(string $name): bool { return array_key_exists(strtolower($name), $this->timers); } diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 2cf3e70..f67bffb 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -1,5 +1,4 @@ - $value) { + // Replace the binary data with string to avoid json_encode failure. + if (preg_match('~[^\x20-\x7E\t\r\n]~', $value)) + { + $value = 'binary data'; + } + $data['vars']['session'][esc($key)] = is_string($value) ? esc($value) : print_r($value, true); } } @@ -273,7 +280,7 @@ * * @return array */ - protected function collectVarData()// : array + protected function collectVarData(): array { $data = []; @@ -300,7 +307,7 @@ * * @return float */ - protected function roundTo($number, $increments = 5) + protected function roundTo($number, $increments = 5): float { $increments = 1 / $increments; @@ -440,7 +447,7 @@ * * @return string */ - protected function format(string $data, string $format = 'html') + protected function format(string $data, string $format = 'html'): string { $data = json_decode($data, true); diff --git a/system/Debug/Toolbar/Collectors/BaseCollector.php b/system/Debug/Toolbar/Collectors/BaseCollector.php index dd06de6..0785f13 100644 --- a/system/Debug/Toolbar/Collectors/BaseCollector.php +++ b/system/Debug/Toolbar/Collectors/BaseCollector.php @@ -1,5 +1,4 @@ -hasVarData; } @@ -249,7 +250,7 @@ * * @return string */ - public function cleanPath($file) + public function cleanPath(string $file): string { if (strpos($file, APPPATH) === 0) { @@ -284,7 +285,7 @@ * * @return boolean */ - public function isEmpty() + public function isEmpty(): bool { return false; } @@ -302,7 +303,7 @@ return ''; } - public function getAsArray() + public function getAsArray(): array { return [ 'title' => $this->getTitle(), diff --git a/system/Debug/Toolbar/Collectors/Config.php b/system/Debug/Toolbar/Collectors/Config.php index cd23cc7..7ab1e3d 100644 --- a/system/Debug/Toolbar/Collectors/Config.php +++ b/system/Debug/Toolbar/Collectors/Config.php @@ -1,12 +1,53 @@ - $time, - 'datetime' => date('Y-m-d H:i:s', $time), - 'active' => $time === $current, - 'status' => $contents->vars->response->statusCode, - 'method' => $contents->method, - 'url' => $contents->url, - 'isAJAX' => $contents->isAJAX ? 'Yes' : 'No', - 'contentType' => $contents->vars->response->contentType, - ]; + // Debugbar files shown in History Collector + $files[] = [ + 'time' => $time, + 'datetime' => date('Y-m-d H:i:s', $time), + 'active' => $time === $current, + 'status' => $contents->vars->response->statusCode, + 'method' => $contents->method, + 'url' => $contents->url, + 'isAJAX' => $contents->isAJAX ? 'Yes' : 'No', + 'contentType' => $contents->vars->response->contentType, + ]; + } } $this->files = $files; @@ -144,12 +149,12 @@ * * @return integer */ - public function getBadgeValue() + public function getBadgeValue(): int { return count($this->files); } - public function isEmpty() + public function isEmpty(): bool { return empty($this->files); } diff --git a/system/Debug/Toolbar/Collectors/Logs.php b/system/Debug/Toolbar/Collectors/Logs.php index 5e0c7e9..7077765 100644 --- a/system/Debug/Toolbar/Collectors/Logs.php +++ b/system/Debug/Toolbar/Collectors/Logs.php @@ -1,4 +1,4 @@ -collectLogs(); diff --git a/system/Debug/Toolbar/Collectors/Routes.php b/system/Debug/Toolbar/Collectors/Routes.php index b4176dc..bc9c595 100644 --- a/system/Debug/Toolbar/Collectors/Routes.php +++ b/system/Debug/Toolbar/Collectors/Routes.php @@ -1,5 +1,4 @@ - $this->viewer->getData(), @@ -170,7 +172,7 @@ * * @return integer */ - public function getBadgeValue() + public function getBadgeValue(): int { return count($this->viewer->getPerformanceData()); } diff --git a/system/Entity.php b/system/Entity.php index fa54c2d..bdada1b 100644 --- a/system/Entity.php +++ b/system/Entity.php @@ -1,6 +1,4 @@ -_original[$key] === null && $value === null) || $this->_original[$key] === $value); } @@ -253,6 +261,7 @@ * @param string $key * * @return mixed + * @throws \Exception */ public function __get(string $key) { @@ -373,6 +382,8 @@ * attribute will be reset to that default value. * * @param string $key + * + * @throws \ReflectionException */ public function __unset(string $key) { @@ -449,6 +460,7 @@ * @param $value * * @return \CodeIgniter\I18n\Time + * @throws \Exception */ protected function mutateDate($value) { @@ -479,12 +491,13 @@ /** * Provides the ability to cast an item as a specific data type. - * Add ? at the beginning of $type (i.e. ?string) to get NULL instead of castig $value if $value === null + * Add ? at the beginning of $type (i.e. ?string) to get NULL instead of casting $value if $value === null * * @param $value * @param string $type * * @return mixed + * @throws \Exception */ protected function castAs($value, string $type) @@ -554,6 +567,7 @@ * @param boolean $asArray * * @return mixed + * @throws \CodeIgniter\Exceptions\CastException */ private function castAsJson($value, bool $asArray = false) { diff --git a/system/Events/Events.php b/system/Events/Events.php index bde2866..f92f42a 100644 --- a/system/Events/Events.php +++ b/system/Events/Events.php @@ -1,7 +1,4 @@ getBaseName(); $destination = $overwrite ? $targetPath . $name : $this->getDestination($targetPath . $name); - if (! @rename($this->getPath(), $destination)) + $oldName = empty($this->getRealPath()) ? $this->getPath() : $this->getRealPath(); + + if (! @rename($oldName, $destination)) { $error = error_get_last(); throw FileException::forUnableToMove($this->getBasename(), $targetPath, strip_tags($error['message'])); @@ -172,7 +181,7 @@ @chmod($targetPath, 0777 & ~umask()); - return true; + return new File($destination); } //-------------------------------------------------------------------- diff --git a/system/Filters/CSRF.php b/system/Filters/CSRF.php index c9a47d5..1f1c2a1 100644 --- a/system/Filters/CSRF.php +++ b/system/Filters/CSRF.php @@ -1,4 +1,4 @@ -initialize(strtolower($uri)); @@ -142,7 +147,7 @@ if ($position === 'before') { - $result = $class->before($this->request, $this->arguments[$alias] ?? null); + $result = $class->before($this->request); if ($result instanceof RequestInterface) { diff --git a/system/Filters/Honeypot.php b/system/Filters/Honeypot.php index e0c55f3..e1c84d6 100644 --- a/system/Filters/Honeypot.php +++ b/system/Filters/Honeypot.php @@ -1,5 +1,4 @@ -getDownloadFileName(); @@ -377,7 +377,7 @@ $this->setContentTypeByMimeType(); } - $this->setHeader('Content-Disposition', $this->getContentDisponsition()); + $this->setHeader('Content-Disposition', $this->getContentDisposition()); $this->setHeader('Expires-Disposition', '0'); $this->setHeader('Content-Transfer-Encoding', 'binary'); $this->setHeader('Content-Length', (string)$this->getContentLength()); diff --git a/system/HTTP/Exceptions/HTTPException.php b/system/HTTP/Exceptions/HTTPException.php index 0f3b222..49e83f3 100644 --- a/system/HTTP/Exceptions/HTTPException.php +++ b/system/HTTP/Exceptions/HTTPException.php @@ -1,9 +1,49 @@ $value) + foreach ($iterator as $key => $val) { array_splice($stack, $iterator->getDepth() + 1); $pointer = &$stack[count($stack) - 1]; @@ -251,7 +253,7 @@ $stack[] = &$pointer; if (! $iterator->hasChildren()) { - $pointer[$field] = $value; + $pointer[$field] = $val; } } } @@ -270,7 +272,7 @@ * * @return mixed */ - protected function getValueDotNotationSyntax($index, $value) + protected function getValueDotNotationSyntax(array $index, array $value) { if (is_array($index) && ! empty($index)) { diff --git a/system/HTTP/Files/UploadedFile.php b/system/HTTP/Files/UploadedFile.php index 5c61f76..f219d28 100644 --- a/system/HTTP/Files/UploadedFile.php +++ b/system/HTTP/Files/UploadedFile.php @@ -1,5 +1,5 @@ lang('HTTP.uploadErrOk'), @@ -273,7 +275,7 @@ UPLOAD_ERR_NO_FILE => lang('HTTP.uploadErrNoFile'), UPLOAD_ERR_CANT_WRITE => lang('HTTP.uploadErrCantWrite'), UPLOAD_ERR_NO_TMP_DIR => lang('HTTP.uploadErrNoTmpDir'), - UPLOAD_ERR_EXTENSION => lang('HTTP.uploadErrExtension') + UPLOAD_ERR_EXTENSION => lang('HTTP.uploadErrExtension'), ]; $error = is_null($this->error) ? UPLOAD_ERR_OK : $this->error; @@ -393,7 +395,7 @@ * @param string $fileName the name to rename the file to. * @return string file full path */ - public function store($folderName = null, $fileName = null): string + public function store(string $folderName = null, string $fileName = null): string { $folderName = $folderName ?? date('Ymd'); $fileName = $fileName ?? $this->getRandomName(); diff --git a/system/HTTP/Files/UploadedFileInterface.php b/system/HTTP/Files/UploadedFileInterface.php index 04bcaad..76c0069 100644 --- a/system/HTTP/Files/UploadedFileInterface.php +++ b/system/HTTP/Files/UploadedFileInterface.php @@ -1,4 +1,4 @@ -uri->setPath($this->detectPath($protocol)); @@ -614,7 +613,7 @@ * * @return string */ - public function detectPath($protocol = '') + public function detectPath(string $protocol = ''): string { if (empty($protocol)) { @@ -650,7 +649,7 @@ * * @return string */ - public function negotiate(string $type, array $supported, bool $strictMatch = false) + public function negotiate(string $type, array $supported, bool $strictMatch = false): string { if (is_null($this->negotiator)) { @@ -775,7 +774,7 @@ * * @return string */ - protected function removeRelativeDirectory($uri) + protected function removeRelativeDirectory(string $uri): string { $uris = []; $tok = strtok($uri, '/'); diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index f82f375..7114750 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -1,7 +1,4 @@ globals[$method][$index] ?? null; } diff --git a/system/HTTP/RequestInterface.php b/system/HTTP/RequestInterface.php index b0b4e2b..bb507ab 100644 --- a/system/HTTP/RequestInterface.php +++ b/system/HTTP/RequestInterface.php @@ -1,4 +1,4 @@ -host; } @@ -539,15 +544,15 @@ /** * Builds a representation of the string from the component parts. * - * @param $scheme - * @param $authority - * @param $path - * @param $query - * @param $fragment + * @param string $scheme + * @param string $authority + * @param string $path + * @param string $query + * @param string $fragment * * @return string */ - public static function createURIString($scheme = null, $authority = null, $path = null, $query = null, $fragment = null) + public static function createURIString(string $scheme = null, string $authority = null, string $path = null, string $query = null, string $fragment = null): string { $uri = ''; if (! empty($scheme)) @@ -962,9 +967,9 @@ /** * Saves our parts from a parse_url call. * - * @param $parts + * @param array $parts */ - protected function applyParts($parts) + protected function applyParts(array $parts) { if (! empty($parts['host'])) { @@ -1107,7 +1112,7 @@ * * @return string */ - protected function mergePaths(URI $base, URI $reference) + protected function mergePaths(URI $base, URI $reference): string { if (! empty($base->getAuthority()) && empty($base->getPath())) { diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 1b8ecdb..6bd2309 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -1,7 +1,49 @@ -isBrowser) { @@ -139,7 +181,7 @@ * * @return boolean */ - public function isRobot($key = null) + public function isRobot(string $key = null): bool { if (! $this->isRobot) { @@ -165,7 +207,7 @@ * * @return boolean */ - public function isMobile($key = null) + public function isMobile(string $key = null): bool { if (! $this->isMobile) { @@ -189,7 +231,7 @@ * * @return boolean */ - public function isReferral() + public function isReferral(): bool { if (! isset($this->referrer)) { @@ -216,7 +258,7 @@ * * @return string */ - public function getAgentString() + public function getAgentString(): string { return $this->agent; } @@ -228,7 +270,7 @@ * * @return string */ - public function getPlatform() + public function getPlatform(): string { return $this->platform; } @@ -240,7 +282,7 @@ * * @return string */ - public function getBrowser() + public function getBrowser(): string { return $this->browser; } @@ -252,7 +294,7 @@ * * @return string */ - public function getVersion() + public function getVersion(): string { return $this->version; } @@ -264,7 +306,7 @@ * * @return string */ - public function getRobot() + public function getRobot(): string { return $this->robot; } @@ -275,7 +317,7 @@ * * @return string */ - public function getMobile() + public function getMobile(): string { return $this->mobile; } @@ -285,9 +327,9 @@ /** * Get the referrer * - * @return boolean + * @return string */ - public function getReferrer() + public function getReferrer(): string { return empty($_SERVER['HTTP_REFERER']) ? '' : trim($_SERVER['HTTP_REFERER']); } @@ -301,7 +343,7 @@ * * @return void */ - public function parse($string) + public function parse(string $string) { // Reset values $this->isBrowser = false; @@ -325,7 +367,7 @@ /** * Compile the User Agent Data * - * @return boolean + * @return void */ protected function compileData() { @@ -347,7 +389,7 @@ * * @return boolean */ - protected function setPlatform() + protected function setPlatform(): bool { if (is_array($this->config->platforms) && $this->config->platforms) { @@ -374,7 +416,7 @@ * * @return boolean */ - protected function setBrowser() + protected function setBrowser(): bool { if (is_array($this->config->browsers) && $this->config->browsers) { @@ -402,7 +444,7 @@ * * @return boolean */ - protected function setRobot() + protected function setRobot(): bool { if (is_array($this->config->robots) && $this->config->robots) { @@ -429,7 +471,7 @@ * * @return boolean */ - protected function setMobile() + protected function setMobile(): bool { if (is_array($this->config->mobiles) && $this->config->mobiles) { @@ -455,7 +497,7 @@ * * @return string */ - public function __toString() + public function __toString(): string { return $this->getAgentString(); } diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index ea8bbc7..5215173 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -1,4 +1,49 @@ 'text', @@ -905,12 +915,12 @@ * * Helper function used by some of the form helpers * - * @param array $attributes List of attributes - * @param array $default Default values + * @param string|array $attributes List of attributes + * @param array $default Default values * * @return string */ - function parse_form_attributes($attributes, $default): string + function parse_form_attributes($attributes, array $default): string { if (is_array($attributes)) { diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 8002bc1..a1cc95e 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -38,7 +38,7 @@ // -------------------------------------------------------------------- /** - * CodeIgniter HTML Helper + * CodeIgniter HTML Helpers * * @package CodeIgniter * @subpackage Helpers @@ -331,7 +331,7 @@ /** * Video * - * Geneartes a video element to embed videos. The video element can + * Generates a video element to embed videos. The video element can * contain one or more video sources * * @param mixed $src Either a source string or an array of sources @@ -461,6 +461,17 @@ if (! function_exists('_media')) { + /** + * Generate media based tag + * + * @param string $name + * @param array $types + * @param string $unsupportedMessage + * @param string $attributes + * @param array $tracks + * + * @return string + */ function _media(string $name, array $types = [], string $unsupportedMessage = '', string $attributes = '', array $tracks = []): string { $media = '<' . $name; @@ -687,7 +698,12 @@ if (! function_exists('_has_protocol')) { - function _has_protocol($url) + /** + * @param string $url + * + * @return false|integer + */ + function _has_protocol(string $url) { return preg_match('#^([a-z]+:)?//#i', $url); } @@ -697,7 +713,12 @@ if (! function_exists('_space_indent')) { - function _space_indent($depth = 2) + /** + * @param integer $depth + * + * @return string + */ + function _space_indent($depth = 2): string { return str_repeat(' ', $depth); } diff --git a/system/Helpers/number_helper.php b/system/Helpers/number_helper.php index 805f8a9..35126f8 100644 --- a/system/Helpers/number_helper.php +++ b/system/Helpers/number_helper.php @@ -27,7 +27,7 @@ * THE SOFTWARE. * * @package CodeIgniter - * @author EllisLab Dev Team + * @author CodeIgniter Dev Team * @copyright 2008-2014 EllisLab, Inc. (https://ellislab.com/) * @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/) * @license https://opensource.org/licenses/MIT MIT License @@ -36,6 +36,16 @@ * @filesource */ +/** + * CodeIgniter Number Helpers + * + * @package CodeIgniter + * @subpackage Helpers + * @category Helpers + * @author CodeIgniter Dev Team + * @link https://codeigniter.com/user_guide/helpers/cookie_helper.html + */ + if (! function_exists('number_to_size')) { /** @@ -257,11 +267,11 @@ /** * Convert a number to a roman numeral. * - * @param integer $num it will convert to int + * @param string $num it will convert to int * * @return string|null */ - function number_to_roman($num) + function number_to_roman(string $num): ?string { $num = (int) $num; if ($num < 1 || $num > 3999) diff --git a/system/Helpers/security_helper.php b/system/Helpers/security_helper.php index 7e67cf2..04aa284 100644 --- a/system/Helpers/security_helper.php +++ b/system/Helpers/security_helper.php @@ -38,6 +38,16 @@ use Config\Services; +/** + * CodeIgniter Security Helpers + * + * @package CodeIgniter + * @subpackage Helpers + * @category Helpers + * @author CodeIgniter Dev Team + * @link https://codeigniter.com/user_guide/helpers/cookie_helper.html + */ + if (! function_exists('sanitize_filename')) { /** diff --git a/system/Helpers/text_helper.php b/system/Helpers/text_helper.php index 52f61b2..8a49361 100755 --- a/system/Helpers/text_helper.php +++ b/system/Helpers/text_helper.php @@ -28,7 +28,7 @@ * THE SOFTWARE. * * @package CodeIgniter - * @author EllisLab Dev Team + * @author CodeIgniter Dev Team * @copyright 2008-2014 EllisLab, Inc. (https://ellislab.com/) * @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/) * @license https://opensource.org/licenses/MIT MIT License @@ -43,7 +43,7 @@ * @package CodeIgniter * @subpackage Helpers * @category Helpers - * @author EllisLab Dev Team + * @author CodeIgniter Dev Team * @link https://codeigniter.com/user_guide/helpers/text_helper.html */ //-------------------------------------------------------------------- @@ -264,14 +264,14 @@ * word you've submitted. * * @param string $str the text string - * @param string $censored the array of censored words + * @param array $censored the array of censored words * @param string $replacement the optional replacement value * * @return string */ - function word_censor(string $str, $censored, string $replacement = ''): string + function word_censor(string $str, array $censored, string $replacement = ''): string { - if (! is_array($censored)) + if (empty($censored)) { return $str; } @@ -460,7 +460,7 @@ * Anything placed between {unwrap}{/unwrap} will not be word wrapped, nor * will URLs. * - * @param string $str the text string + * @param string $str the text string * @param integer $charlim = 76 the number of characters to wrap at * * @return string @@ -604,7 +604,7 @@ * * Removes slashes contained in a string or in an array * - * @param mixed $str string or array + * @param mixed $str string or array * * @return mixed string or array */ diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index 4fbaaae..0184b25 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -35,23 +35,33 @@ * @filesource */ +/** + * CodeIgniter URL Helpers + * + * @package CodeIgniter + * @subpackage Helpers + * @category Helpers + * @author CodeIgniter Dev Team + * @link https://codeigniter.com/user_guide/helpers/cookie_helper.html + */ + if (! function_exists('site_url')) { /** * Return a site URL to use in views * - * @param string|array $path - * @param string|null $scheme + * @param mixed $uri URI string or array of URI segments + * @param string|null $protocol * @param \Config\App|null $altConfig Alternate configuration to use * * @return string */ - function site_url($path = '', string $scheme = null, \Config\App $altConfig = null): string + function site_url($uri = '', string $protocol = null, \Config\App $altConfig = null): string { // convert segment array to string - if (is_array($path)) + if (is_array($uri)) { - $path = implode('/', $path); + $uri = implode('/', $uri); } // use alternate config if provided, else default one @@ -64,17 +74,17 @@ { $fullPath .= rtrim($config->indexPage, '/'); } - if (! empty($path)) + if (! empty($uri)) { - $fullPath .= '/' . $path; + $fullPath .= '/' . $uri; } $url = new \CodeIgniter\HTTP\URI($fullPath); // allow the scheme to be over-ridden; else, use default - if (! empty($scheme)) + if (! empty($protocol)) { - $url->setScheme($scheme); + $url->setScheme($protocol); } return (string) $url; @@ -88,16 +98,16 @@ /** * Return the base URL to use in views * - * @param string|array $path - * @param string $scheme + * @param mixed $uri URI string or array of URI segments + * @param string $protocol * @return string */ - function base_url($path = '', string $scheme = null): string + function base_url($uri = '', string $protocol = null): string { // convert segment array to string - if (is_array($path)) + if (is_array($uri)) { - $path = implode('/', $path); + $uri = implode('/', $uri); } // We should be using the configured baseURL that the user set; @@ -108,21 +118,21 @@ unset($config); // Merge in the path set by the user, if any - if (! empty($path)) + if (! empty($uri)) { - $url = $url->resolveRelativeURI($path); + $url = $url->resolveRelativeURI($uri); } // If the scheme wasn't provided, check to // see if it was a secure request - if (empty($scheme) && \CodeIgniter\Config\Services::request()->isSecure()) + if (empty($protocol) && \CodeIgniter\Config\Services::request()->isSecure()) { - $scheme = 'https'; + $protocol = 'https'; } - if (! empty($scheme)) + if (! empty($protocol)) { - $url->setScheme($scheme); + $url->setScheme($protocol); } return (string) $url; @@ -223,7 +233,7 @@ * * Creates an anchor based on the local URL. * - * @param string $uri The URL + * @param mixed $uri URI string or array of URI segments * @param string $title The link title * @param mixed $attributes Any attributes * @param \Config\App|null $altConfig Alternate configuration to use @@ -332,7 +342,7 @@ * * @return string */ - function mailto($email, string $title = '', $attributes = ''): string + function mailto(string $email, string $title = '', $attributes = ''): string { if (trim($title) === '') { @@ -358,7 +368,7 @@ * * @return string */ - function safe_mailto($email, string $title = '', $attributes = ''): string + function safe_mailto(string $email, string $title = '', $attributes = ''): string { if (trim($title) === '') { @@ -470,7 +480,7 @@ * * @return string */ - function auto_link($str, $type = 'both', $popup = false): string + function auto_link(string $str, string $type = 'both', bool $popup = false): string { // Find and replace any URLs. if ($type !== 'email' && preg_match_all('#(\w*://|www\.)[^\s()<>;]+\w#i', $str, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) @@ -522,7 +532,7 @@ * @param string the URL * @return string */ - function prep_url($str = ''): string + function prep_url(string $str = ''): string { if ($str === 'http://' || $str === '') { @@ -556,7 +566,7 @@ * @param boolean $lowercase Whether to transform the output string to lowercase * @return string */ - function url_title($str, $separator = '-', $lowercase = false): string + function url_title(string $str, string $separator = '-', bool $lowercase = false): string { $q_separator = preg_quote($separator, '#'); diff --git a/system/Helpers/xml_helper.php b/system/Helpers/xml_helper.php index 3848ece..e18c579 100644 --- a/system/Helpers/xml_helper.php +++ b/system/Helpers/xml_helper.php @@ -35,16 +35,26 @@ * @filesource */ +/** + * CodeIgniter XML Helpers + * + * @package CodeIgniter + * @subpackage Helpers + * @category Helpers + * @author CodeIgniter Dev Team + * @link https://codeigniter.com/user_guide/helpers/cookie_helper.html + */ + if (! function_exists('xml_convert')) { /** * Convert Reserved XML characters to Entities * - * @param string - * @param boolean + * @param string $str + * @param boolean $protect_all * @return string */ - function xml_convert(string $str, $protect_all = false): string + function xml_convert(string $str, bool $protect_all = false): string { $temp = '__TEMP_AMPERSANDS__'; diff --git a/system/Honeypot/Honeypot.php b/system/Honeypot/Honeypot.php index b2ba4ee..90ef152 100644 --- a/system/Honeypot/Honeypot.php +++ b/system/Honeypot/Honeypot.php @@ -1,6 +1,5 @@ config->label, $template); $template = str_ireplace('{name}', $this->config->name, $template); diff --git a/system/I18n/Time.php b/system/I18n/Time.php index 45114b6..f5d03cd 100644 --- a/system/I18n/Time.php +++ b/system/I18n/Time.php @@ -1,5 +1,4 @@ toLocalizedString('Y'); } @@ -406,7 +416,7 @@ * * @return string */ - public function getMonth() + public function getMonth(): string { return $this->toLocalizedString('M'); } @@ -418,7 +428,7 @@ * * @return string */ - public function getDay() + public function getDay(): string { return $this->toLocalizedString('d'); } @@ -430,7 +440,7 @@ * * @return string */ - public function getHour() + public function getHour(): string { return $this->toLocalizedString('H'); } @@ -442,7 +452,7 @@ * * @return string */ - public function getMinute() + public function getMinute(): string { return $this->toLocalizedString('m'); } @@ -454,7 +464,7 @@ * * @return string */ - public function getSecond() + public function getSecond(): string { return $this->toLocalizedString('s'); } @@ -466,7 +476,7 @@ * * @return string */ - public function getDayOfWeek() + public function getDayOfWeek(): string { return $this->toLocalizedString('c'); } @@ -478,7 +488,7 @@ * * @return string */ - public function getDayOfYear() + public function getDayOfYear(): string { return $this->toLocalizedString('D'); } @@ -490,7 +500,7 @@ * * @return string */ - public function getWeekOfMonth() + public function getWeekOfMonth(): string { return $this->toLocalizedString('W'); } @@ -502,7 +512,7 @@ * * @return string */ - public function getWeekOfYear() + public function getWeekOfYear(): string { return $this->toLocalizedString('w'); } @@ -528,7 +538,7 @@ * * @return string */ - public function getQuarter() + public function getQuarter(): string { return $this->toLocalizedString('Q'); } @@ -562,7 +572,7 @@ * Returns boolean whether the passed timezone is the same as * the local timezone. */ - public function getLocal() + public function getLocal(): bool { $local = date_default_timezone_get(); @@ -574,17 +584,17 @@ /** * Returns boolean whether object is in UTC. */ - public function getUtc() + public function getUtc(): bool { return $this->getOffset() === 0; } /** - * Reeturns the name of the current timezone. + * Returns the name of the current timezone. * * @return string */ - public function getTimezoneName() + public function getTimezoneName(): string { return $this->timezone->getName(); } @@ -709,6 +719,7 @@ * @param $value * * @return \CodeIgniter\I18n\Time + * @throws \Exception */ protected function setValue(string $name, $value) { @@ -976,6 +987,7 @@ * @param string|null $format * * @return string + * @throws \Exception */ public function toLocalizedString(?string $format = null) { @@ -999,6 +1011,7 @@ * @param string|null $timezone * * @return boolean + * @throws \Exception */ public function equals($testTime, string $timezone = null): bool { @@ -1124,7 +1137,7 @@ { $before = $days < 0; - // Yesterday/Tommorrow special cases + // Yesterday/Tomorrow special cases if (abs($days) === 1) { return $before ? lang('Time.yesterday') : lang('Time.tomorrow'); @@ -1240,8 +1253,9 @@ * Outputs a short format version of the datetime. * * @return string + * @throws \Exception */ - public function __toString() + public function __toString(): string { return IntlDateFormatter::formatObject($this->toDateTime(), $this->toStringFormat, $this->locale); } diff --git a/system/I18n/TimeDifference.php b/system/I18n/TimeDifference.php index 4a4ac3a..b1c19bf 100644 --- a/system/I18n/TimeDifference.php +++ b/system/I18n/TimeDifference.php @@ -1,7 +1,48 @@ - '{0} is not a valid Model Event callback.', - 'invalidArgument' => 'You must provide a valid {0}.', - 'invalidAllowedFields' => 'Allowed fields must be specified for model: {0}', - 'emptyDataset' => 'There is no data to {0}.', - 'failGetFieldData' => 'Failed to get field data from database.', - 'failGetIndexData' => 'Failed to get index data from database.', - 'failGetForeignKeyData' => 'Failed to get foreign key data from database.', - 'parseStringFail' => 'Parsing key string failed.', - 'featureUnavailable' => 'This feature is not available for the database you are using.', - 'tableNotFound' => 'Table `{0}` was not found in the current database.', + 'invalidEvent' => '{0} is not a valid Model Event callback.', + 'invalidArgument' => 'You must provide a valid {0}.', + 'invalidAllowedFields' => 'Allowed fields must be specified for model: {0}', + 'emptyDataset' => 'There is no data to {0}.', + 'failGetFieldData' => 'Failed to get field data from database.', + 'failGetIndexData' => 'Failed to get index data from database.', + 'failGetForeignKeyData' => 'Failed to get foreign key data from database.', + 'parseStringFail' => 'Parsing key string failed.', + 'featureUnavailable' => 'This feature is not available for the database you are using.', + 'tableNotFound' => 'Table `{0}` was not found in the current database.', + 'noPrimaryKey' => '`{0}` model class does not specify a Primary Key.', + 'forEmptyInputGiven' => 'Empty statement is given for the field `{0}`', + 'forFindColumnHaveMultipleColumns' => 'Only single column allowed in Column name.', ]; diff --git a/system/Language/en/HTTP.php b/system/Language/en/HTTP.php index eb0b29c..6b423bb 100644 --- a/system/Language/en/HTTP.php +++ b/system/Language/en/HTTP.php @@ -64,14 +64,14 @@ 'alreadyMoved' => 'The uploaded file has already been moved.', 'invalidFile' => 'The original file is not a valid file.', 'moveFailed' => 'Could not move file {0} to {1} ({2})', - - 'uploadErrOk' => 'The file uploaded with success.', - 'uploadErrIniSize' => 'The file "%s" exceeds your upload_max_filesize ini directive.', - 'uploadErrFormSize' => 'The file "%s" exceeds the upload limit defined in your form.', - 'uploadErrPartial' => 'The file "%s" was only partially uploaded.', - 'uploadErrNoFile' => 'No file was uploaded.', - 'uploadErrCantWrite' => 'The file "%s" could not be written on disk.', - 'uploadErrNoTmpDir' => 'File could not be uploaded: missing temporary directory.', - 'uploadErrExtension' => 'File upload was stopped by a PHP extension.', - 'uploadErrUnknown' => 'The file "%s" was not uploaded due to an unknown error.' + + 'uploadErrOk' => 'The file uploaded with success.', + 'uploadErrIniSize' => 'The file "%s" exceeds your upload_max_filesize ini directive.', + 'uploadErrFormSize' => 'The file "%s" exceeds the upload limit defined in your form.', + 'uploadErrPartial' => 'The file "%s" was only partially uploaded.', + 'uploadErrNoFile' => 'No file was uploaded.', + 'uploadErrCantWrite' => 'The file "%s" could not be written on disk.', + 'uploadErrNoTmpDir' => 'File could not be uploaded: missing temporary directory.', + 'uploadErrExtension' => 'File upload was stopped by a PHP extension.', + 'uploadErrUnknown' => 'The file "%s" was not uploaded due to an unknown error.', ]; diff --git a/system/Language/en/Images.php b/system/Language/en/Images.php index f7ee1ad..6417d53 100644 --- a/system/Language/en/Images.php +++ b/system/Language/en/Images.php @@ -21,7 +21,7 @@ 'gifNotSupported' => 'GIF images are often not supported due to licensing restrictions. You may have to use JPG or PNG images instead.', 'jpgNotSupported' => 'JPG images are not supported.', 'pngNotSupported' => 'PNG images are not supported.', - 'unsupportedImagecreate' => 'Your server does not support the GD function required to process this type of image.', + 'unsupportedImageCreate' => 'Your server does not support the GD function required to process this type of image.', 'jpgOrPngRequired' => 'The image resize protocol specified in your preferences only works with JPEG or PNG image types.', 'rotateUnsupported' => 'Image rotation does not appear to be supported by your server.', 'libPathInvalid' => 'The path to your image library is not correct. Please set the correct path in your image preferences. {0, string)', diff --git a/system/Log/Handlers/BaseHandler.php b/system/Log/Handlers/BaseHandler.php index 7fc66fa..cc55ea6 100644 --- a/system/Log/Handlers/BaseHandler.php +++ b/system/Log/Handlers/BaseHandler.php @@ -1,4 +1,4 @@ -tempReturnType = $this->returnType; $this->tempUseSoftDeletes = $this->useSoftDeletes; - $this->reset(); - return $row['data']; } //-------------------------------------------------------------------- /** + * Fetches the column of database from $this->table + * + * @param string $column_name Column name + * + * @return array|null The resulting row of data, or null if no data found. + * + * @throws \CodeIgniter\Database\Exceptions\DataException + */ + public function findColumn(string $columnName) + { + if (strpos($columnName, ',') !== false) + { + throw DataException::forFindColumnHaveMultipleColumns(); + } + + $resultSet = $this->select($columnName) + ->asArray() + ->find(); + + return (!empty($resultSet)) ? array_column($resultSet, $columnName) : null; + } + + //-------------------------------------------------------------------- + + /** * Works with the current Query Builder instance to return * all results, while optionally limiting them. * @@ -385,7 +411,7 @@ /** * Returns the first row of the result set. Will take any previous - * Query Builder calls into account when determing the result set. + * Query Builder calls into account when determining the result set. * * @return array|object|null */ @@ -424,13 +450,13 @@ * data here. This allows it to be used with any of the other * builder methods and still get validated data, like replace. * - * @param $key + * @param mixed $key * @param string $value * @param boolean|null $escape * * @return $this */ - public function set($key, $value = '', bool $escape = null) + public function set($key, string $value = '', bool $escape = null) { $data = is_array($key) ? $key @@ -456,7 +482,7 @@ * @return boolean * @throws \ReflectionException */ - public function save($data) + public function save($data): bool { // If $data is using a custom class with public or protected // properties representing the table elements, we need to grab @@ -481,7 +507,12 @@ } else { - $response = $this->insert($data); + $response = $this->insert($data, false); + // call insert directly if you want the ID or the record object + if ($response !== false) + { + $response = true; + } } return $response; @@ -565,7 +596,7 @@ * * @return integer */ - public function getInsertID() + public function getInsertID(): int { return $this->insertID; } @@ -629,10 +660,16 @@ // strip out created_at values. $data = $this->doProtectFields($data); - if ($this->useTimestamps && ! array_key_exists($this->createdField, $data)) + // Set created_at and updated_at with same time + $date = $this->setDate(); + + if ($this->useTimestamps && ! empty($this->createdField) && ! array_key_exists($this->createdField, $data)) { - $date = $this->setDate(); $data[$this->createdField] = $date; + } + + if ($this->useTimestamps && ! empty($this->updatedField) && ! array_key_exists($this->updatedField, $data)) + { $data[$this->updatedField] = $date; } @@ -673,9 +710,9 @@ * @param integer $batchSize * @param boolean $testing * - * @return integer Number of rows inserted or FALSE on failure + * @return integer|boolean Number of rows inserted or FALSE on failure */ - public function insertBatch($set = null, $escape = null, $batchSize = 100, $testing = false) + public function insertBatch(array $set = null, bool $escape = null, int $batchSize = 100, bool $testing = false) { if (is_array($set) && $this->skipValidation === false) { @@ -703,7 +740,7 @@ * @return boolean * @throws \ReflectionException */ - public function update($id = null, $data = null) + public function update($id = null, $data = null): bool { $escape = null; @@ -753,7 +790,7 @@ // strip out updated_at values. $data = $this->doProtectFields($data); - if ($this->useTimestamps && ! array_key_exists($this->updatedField, $data)) + if ($this->useTimestamps && ! empty($this->updatedField) && ! array_key_exists($this->updatedField, $data)) { $data[$this->updatedField] = $this->setDate(); } @@ -797,7 +834,7 @@ * @return mixed Number of rows affected or FALSE on failure * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function updateBatch($set = null, $index = null, $batchSize = 100, $returnSQL = false) + public function updateBatch(array $set = null, string $index = null, int $batchSize = 100, bool $returnSQL = false) { if (is_array($set) && $this->skipValidation === false) { @@ -825,7 +862,7 @@ * @return mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function delete($id = null, $purge = false) + public function delete($id = null, bool $purge = false) { if (! empty($id) && is_numeric($id)) { @@ -844,7 +881,7 @@ { $set[$this->deletedField] = 1; - if ($this->useTimestamps) + if ($this->useTimestamps && ! empty($this->updatedField)) { $set[$this->updatedField] = $this->setDate(); } @@ -928,7 +965,7 @@ * * @return boolean TRUE on success, FALSE on failure */ - public function replace($data = null, $returnSQL = false) + public function replace($data = null, bool $returnSQL = false): bool { // Validate data before saving. if (! empty($data) && $this->skipValidation === false) @@ -989,7 +1026,7 @@ * * @throws \CodeIgniter\Database\Exceptions\DataException */ - public function chunk($size = 100, \Closure $userFunc) + public function chunk(int $size, \Closure $userFunc) { $total = $this->builder() ->countAllResults(false); @@ -1007,7 +1044,7 @@ throw DataException::forEmptyDataset('chunk'); } - $rows = $rows->getResult(); + $rows = $rows->getResult($this->tempReturnType); $offset += $size; @@ -1090,6 +1127,14 @@ return $this->builder; } + // We're going to force a primary key to exist + // so we don't have overly convoluted code, + // and future features are likely to require them. + if (empty($this->primaryKey)) + { + throw ModelException::forNoPrimaryKey(get_class($this)); + } + $table = empty($table) ? $this->table : $table; // Ensure we have a good db connection @@ -1117,7 +1162,7 @@ * @return array * @throws \CodeIgniter\Database\Exceptions\DataException */ - protected function doProtectFields($data) + protected function doProtectFields(array $data): array { if ($this->protectFields === false) { @@ -1160,7 +1205,7 @@ * * @return mixed */ - protected function setDate($userData = null) + protected function setDate(int $userData = null) { $currentDate = is_numeric($userData) ? (int) $userData : time(); @@ -1246,6 +1291,36 @@ //-------------------------------------------------------------------- /** + * Allows to set validation messages. + * It could be used when you have to change default or override current validate messages. + * + * @param array $validationMessages + * + * @return void + */ + public function setValidationMessages(array $validationMessages) + { + $this->validationMessages = $validationMessages; + } + //-------------------------------------------------------------------- + + /** + * Allows to set field wise validation message. + * It could be used when you have to change default or override current validate messages. + * + * @param string $field + * @param array $fieldMessages + * + * @return void + */ + public function setValidationMessage(string $field, array $fieldMessages) + { + $this->validationMessages[$field] = $fieldMessages; + } + + //-------------------------------------------------------------------- + + /** * Validate the data against the validation rules (or the validation group) * specified in the class property, $validationRules. * @@ -1302,11 +1377,13 @@ * currently so that rules don't block updating when only updating * a partial row. * - * @param array $rules + * @param array $rules + * + * @param array|null $data * * @return array */ - protected function cleanValidationRules($rules, array $data = null) + protected function cleanValidationRules(array $rules, array $data = null): array { if (empty($data)) { @@ -1344,7 +1421,7 @@ * * @return array */ - protected function fillPlaceholders(array $rules, array $data) + protected function fillPlaceholders(array $rules, array $data): array { $replacements = []; @@ -1386,9 +1463,11 @@ * Returns the model's defined validation rules so that they * can be used elsewhere, if needed. * + * @param array $options + * * @return array */ - public function getValidationRules(array $options = []) + public function getValidationRules(array $options = []): array { $rules = $this->validationRules; @@ -1412,7 +1491,7 @@ * * @return array */ - public function getValidationMessages() + public function getValidationMessages(): array { return $this->validationMessages; } @@ -1488,7 +1567,7 @@ * * @param string $name * - * @return null + * @return mixed */ public function __get(string $name) { @@ -1519,7 +1598,7 @@ * * @return Model|null */ - public function __call($name, array $params) + public function __call(string $name, array $params) { $result = null; diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index ee09e31..2af6ac7 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -1,4 +1,4 @@ -routesOptions[$from] ?? [] : $this->routesOptions; } @@ -887,10 +889,10 @@ * Example: * $route->match( ['get', 'post'], 'users/(:num)', 'users/$1); * - * @param array $verbs - * @param $from - * @param $to - * @param array $options + * @param array $verbs + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -911,9 +913,9 @@ /** * Specifies a route that is only available to GET requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -929,9 +931,9 @@ /** * Specifies a route that is only available to POST requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -947,9 +949,9 @@ /** * Specifies a route that is only available to PUT requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -965,9 +967,9 @@ /** * Specifies a route that is only available to DELETE requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -983,9 +985,9 @@ /** * Specifies a route that is only available to HEAD requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -1001,9 +1003,9 @@ /** * Specifies a route that is only available to PATCH requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -1019,9 +1021,9 @@ /** * Specifies a route that is only available to OPTIONS requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -1037,9 +1039,9 @@ /** * Specifies a route that is only available to command-line requests. * - * @param $from - * @param $to - * @param array $options + * @param string $from + * @param string|array $to + * @param array $options * * @return \CodeIgniter\Router\RouteCollectionInterface */ @@ -1225,10 +1227,10 @@ * the request method(s) that this route will work for. They can be separated * by a pipe character "|" if there is more than one. * - * @param string $verb - * @param string $from - * @param $to - * @param array|null $options + * @param string $verb + * @param string $from + * @param string|array $to + * @param array $options */ protected function create(string $verb, string $from, $to, array $options = null) { @@ -1238,7 +1240,7 @@ $from = filter_var($prefix . $from, FILTER_SANITIZE_STRING); // While we want to add a route within a group of '/', - // it doens't work with matching, so remove them... + // it doesn't work with matching, so remove them... if ($from !== '/') { $from = trim($from, '/'); @@ -1341,11 +1343,11 @@ * Compares the subdomain(s) passed in against the current subdomain * on this page request. * - * @param $subdomains + * @param mixed $subdomains * * @return boolean */ - private function checkSubdomains($subdomains) + private function checkSubdomains($subdomains): bool { // CLI calls can't be on subdomain. if (! isset($_SERVER['HTTP_HOST'])) diff --git a/system/Router/RouteCollectionInterface.php b/system/Router/RouteCollectionInterface.php index 2413ccf..b61ffcf 100644 --- a/system/Router/RouteCollectionInterface.php +++ b/system/Router/RouteCollectionInterface.php @@ -1,4 +1,4 @@ - $val) { + $key = $key === '/' + ? $key + : ltrim($key, '/ '); + // Are we dealing with a locale? if (strpos($key, '{locale}') !== false) { - $localeSegment = array_search('{locale}', explode('/', $key)); + $localeSegment = array_search('{locale}', preg_split('/[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+/m', $key)); // Replace it with a regex so it // will actually match. @@ -471,7 +477,15 @@ } elseif (strpos($val, '/') !== false) { - $val = str_replace('/', '\\', $val); + [ + $controller, + $method, + ] = explode( '::', $val ); + + // Only replace slashes in the controller, not in the method. + $controller = str_replace('/', '\\', $controller); + + $val = $controller . '::' . $method; } // Is this route supposed to redirect to another? diff --git a/system/Router/RouterInterface.php b/system/Router/RouterInterface.php index 85491b3..67d2827 100644 --- a/system/Router/RouterInterface.php +++ b/system/Router/RouterInterface.php @@ -1,4 +1,4 @@ -CSRFHash; } @@ -276,7 +278,7 @@ * * @return string */ - public function getCSRFTokenName() + public function getCSRFTokenName(): string { return $this->CSRFTokenName; } @@ -287,8 +289,9 @@ * Sets the CSRF Hash and cookie. * * @return string + * @throws \Exception */ - protected function CSRFSetHash() + protected function CSRFSetHash(): string { if ($this->CSRFHash === null) { diff --git a/system/Session/Handlers/BaseHandler.php b/system/Session/Handlers/BaseHandler.php index 7605b4f..5f15462 100644 --- a/system/Session/Handlers/BaseHandler.php +++ b/system/Session/Handlers/BaseHandler.php @@ -1,4 +1,4 @@ -savePath); diff --git a/system/Session/Handlers/DatabaseHandler.php b/system/Session/Handlers/DatabaseHandler.php index 6d04569..2acdb16 100644 --- a/system/Session/Handlers/DatabaseHandler.php +++ b/system/Session/Handlers/DatabaseHandler.php @@ -1,4 +1,4 @@ -lockSession($sessionID) === false) { diff --git a/system/Session/Handlers/FileHandler.php b/system/Session/Handlers/FileHandler.php index d14c01d..ac9bd10 100644 --- a/system/Session/Handlers/FileHandler.php +++ b/system/Session/Handlers/FileHandler.php @@ -1,4 +1,4 @@ -keyPrefix .= $this->ipAddress . ':'; } - - if(!empty($this->keyPrefix)) + + if (! empty($this->keyPrefix)) { ini_set('memcached.sess_prefix', $this->keyPrefix); } @@ -115,7 +116,7 @@ * * @return boolean */ - public function open($save_path, $name) + public function open($save_path, $name): bool { $this->memcached = new \Memcached(); $this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); // required for touch() usage @@ -176,7 +177,7 @@ * * @return string Serialized session data */ - public function read($sessionID) + public function read($sessionID): string { if (isset($this->memcached) && $this->lockSession($sessionID)) { @@ -204,7 +205,7 @@ * * @return boolean */ - public function write($sessionID, $sessionData) + public function write($sessionID, $sessionData): bool { if (! isset($this->memcached)) { @@ -253,7 +254,7 @@ * * @return boolean */ - public function close() + public function close(): bool { if (isset($this->memcached)) { @@ -283,7 +284,7 @@ * * @return boolean */ - public function destroy($session_id) + public function destroy($session_id): bool { if (isset($this->memcached, $this->lockKey)) { @@ -306,7 +307,7 @@ * * @return boolean */ - public function gc($maxlifetime) + public function gc($maxlifetime): bool { // Not necessary, Memcached takes care of that. return true; diff --git a/system/Session/Handlers/RedisHandler.php b/system/Session/Handlers/RedisHandler.php index 5132785..1d50ba8 100644 --- a/system/Session/Handlers/RedisHandler.php +++ b/system/Session/Handlers/RedisHandler.php @@ -1,4 +1,4 @@ -sessionExpiration); } - - if(!empty($this->sessionSavePath)) + + if (! empty($this->sessionSavePath)) { ini_set('session.save_path', $this->sessionSavePath); } diff --git a/system/Session/SessionInterface.php b/system/Session/SessionInterface.php index 4e9a47b..498a586 100644 --- a/system/Session/SessionInterface.php +++ b/system/Session/SessionInterface.php @@ -1,4 +1,4 @@ -dom, $function)) diff --git a/system/Test/ControllerTester.php b/system/Test/ControllerTester.php index 0dad1b8..f132973 100644 --- a/system/Test/ControllerTester.php +++ b/system/Test/ControllerTester.php @@ -1,4 +1,5 @@ -response = new Response($this->appConfig); } - $this->controller = new $name($this->request, $this->response); + if (empty($this->logger)) + { + $this->logger = Services::logger(); + } + + $this->controller = new $name(); + $this->controller->initController($this->request, $this->response, $this->logger); return $this; } @@ -110,7 +117,7 @@ * @param string $method * @param array $params * - * @return \CodeIgniter\TestControllerResponse + * @return \CodeIgniter\Test\ControllerResponse|\InvalidArgumentException */ public function execute(string $method, ...$params) { @@ -124,9 +131,10 @@ helper('url'); $result = (new ControllerResponse()) - ->setRequest($this->request) - ->setResponse($this->response); + ->setRequest($this->request) + ->setResponse($this->response); + $response = null; try { ob_start(); @@ -136,21 +144,33 @@ catch (\Throwable $e) { $result->response() - ->setStatusCode($e->getCode()); + ->setStatusCode($e->getCode()); } finally { $output = ob_get_clean(); - // If the controller returned a redirect response - // then we need to use that... + // If the controller returned a response, use it if (isset($response) && $response instanceof Response) { $result->setResponse($response); } - $result->response()->setBody($output); - $result->setBody($output); + // check if controller returned a view rather than echoing it + if (is_string($response)) + { + $output = $response; + $result->response()->setBody($output); + $result->setBody($output); + } + elseif (! empty($response) && ! empty($response->getBody())) + { + $result->setBody($response->getBody()); + } + else + { + $result->setBody(''); + } } // If not response code has been sent, assume a success @@ -199,6 +219,18 @@ } /** + * @param mixed $logger + * + * @return mixed + */ + public function withLogger($logger) + { + $this->logger = $logger; + + return $this; + } + + /** * @param string $uri * * @return mixed diff --git a/system/Test/DOMParser.php b/system/Test/DOMParser.php index 55bd10e..09befc8 100644 --- a/system/Test/DOMParser.php +++ b/system/Test/DOMParser.php @@ -1,4 +1,4 @@ -dom = new \DOMDocument('1.0', 'utf-8'); @@ -80,7 +88,11 @@ if (! $this->dom->loadHTML($content)) { + // unclear how we would get here, given that we are trapping libxml errors + // @codeCoverageIgnoreStart + libxml_clear_errors(); throw new \BadMethodCallException('Invalid HTML'); + // @codeCoverageIgnoreEnd } // ignore the whitespace. @@ -248,7 +260,7 @@ { foreach ($selector['attr'] as $key => $value) { - $path .= "[{$key}={$value}]"; + $path .= "[@{$key}=\"{$value}\"]"; } } diff --git a/system/Test/FeatureResponse.php b/system/Test/FeatureResponse.php index 3a60672..2b8b75a 100644 --- a/system/Test/FeatureResponse.php +++ b/system/Test/FeatureResponse.php @@ -1,9 +1,49 @@ -tokenTime; } @@ -123,7 +125,7 @@ * @return boolean * @internal param int $maxRequests */ - public function check(string $key, int $capacity, int $seconds, int $cost = 1) + public function check(string $key, int $capacity, int $seconds, int $cost = 1): bool { $tokenName = $this->prefix . $key; @@ -193,7 +195,7 @@ * * @return integer */ - public function time() + public function time(): int { return $this->testTime ?? time(); } diff --git a/system/Throttle/ThrottlerInterface.php b/system/Throttle/ThrottlerInterface.php index f1dfd0d..6bbbc59 100644 --- a/system/Throttle/ThrottlerInterface.php +++ b/system/Throttle/ThrottlerInterface.php @@ -1,5 +1,4 @@ -valid_email(trim($str)); - } - foreach (explode(',', $str) as $email) { $email = trim($email); diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 4aa20a3..bc37610 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -1,4 +1,4 @@ -= mb_strlen($str)); } @@ -254,11 +256,6 @@ */ public function min_length(string $str = null, string $val, array $data): bool { - if (! is_numeric($val)) - { - return false; - } - return ($val <= mb_strlen($str)); } @@ -284,7 +281,7 @@ //-------------------------------------------------------------------- /** - * The field is required when any of the other fields are present + * The field is required when any of the other required fields are present * in the data. * * Example (field is required when the password field is present): @@ -336,8 +333,8 @@ //-------------------------------------------------------------------- /** - * The field is required when all of the other fields are not present - * in the data. + * The field is required when all of the other fields are present + * in the data but not required. * * Example (field is required when the id or email field is missing): * diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index 29ca043..3191580 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -1,4 +1,4 @@ -rules; } @@ -540,13 +545,13 @@ * * @param string|null $group * - * @return array|void + * @return array|ValidationException|null */ public function loadRuleGroup(string $group = null) { if (empty($group)) { - return; + return null; } if (! isset($this->config->$group)) @@ -615,7 +620,7 @@ /** * Returns the array of errors that were encountered during - * a run() call. The array should be in the followig format: + * a run() call. The array should be in the following format: * * [ * 'field1' => 'error message', diff --git a/system/Validation/ValidationInterface.php b/system/Validation/ValidationInterface.php index d7c1003..9da71b2 100644 --- a/system/Validation/ValidationInterface.php +++ b/system/Validation/ValidationInterface.php @@ -1,4 +1,4 @@ - 'error message', diff --git a/system/Validation/Views/list.php b/system/Validation/Views/list.php index 50f0571..cc95156 100644 --- a/system/Validation/Views/list.php +++ b/system/Validation/Views/list.php @@ -1,5 +1,5 @@ -