diff --git a/app/Config/Events.php b/app/Config/Events.php index 62a7b86d..946285b8 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -44,10 +44,10 @@ */ if (CI_DEBUG && ! is_cli()) { Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); - Services::toolbar()->respond(); + service('toolbar')->respond(); // Hot Reload route - for framework use on the hot reloader. if (ENVIRONMENT === 'development') { - Services::routes()->get('__hot-reload', static function (): void { + service('routes')->get('__hot-reload', static function (): void { (new HotReloader())->run(); }); } diff --git a/app/Config/Format.php b/app/Config/Format.php index 3de98d7a..2838f55e 100644 --- a/app/Config/Format.php +++ b/app/Config/Format.php @@ -72,6 +72,6 @@ class Format extends BaseConfig */ public function getFormatter(string $mime) { - return Services::format()->getFormatter($mime); + return service('format')->getFormatter($mime); } } diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index 8b435dab..689405bd 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -53,6 +53,6 @@ public function initController(RequestInterface $request, ResponseInterface $res // Preload any models, libraries, etc, here. - // E.g.: $this->session = \Config\Services::session(); + // E.g.: $this->session = service('session'); } } diff --git a/app/Views/errors/cli/error_exception.php b/app/Views/errors/cli/error_exception.php index 9f47d251..2bf1459d 100644 --- a/app/Views/errors/cli/error_exception.php +++ b/app/Views/errors/cli/error_exception.php @@ -52,7 +52,7 @@ $args = implode(', ', array_map(static fn ($value) => match (true) { is_object($value) => 'Object(' . $value::class . ')', - is_array($value) => count($value) ? '[...]' : '[]', + is_array($value) => $value !== [] ? '[...]' : '[]', $value === null => 'null', // return the lowercased version default => var_export($value, true), }, array_values($error['args'] ?? []))); diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php index 44d74989..d5e0c2ec 100644 --- a/app/Views/errors/html/error_exception.php +++ b/app/Views/errors/html/error_exception.php @@ -1,6 +1,5 @@
- + @@ -343,7 +342,7 @@ setStatusCode(http_response_code()); ?>
diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 28c183f1..b338027c 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -19,7 +19,6 @@ use Config\Autoload; use Config\Kint as KintConfig; use Config\Modules; -use Config\Services; use InvalidArgumentException; use Kint; use Kint\Renderer\CliRenderer; @@ -367,6 +366,9 @@ public function sanitizeFilename(string $filename): string return $cleanFilename; } + /** + * @param array{only?: list, exclude?: list} $composerPackages + */ private function loadComposerNamespaces(ClassLoader $composer, array $composerPackages): void { $namespacePaths = $composer->getPrefixesPsr4(); @@ -380,7 +382,7 @@ private function loadComposerNamespaces(ClassLoader $composer, array $composerPa } } - if (! method_exists(InstalledVersions::class, 'getAllRawData')) { + if (! method_exists(InstalledVersions::class, 'getAllRawData')) { // @phpstan-ignore function.alreadyNarrowedType throw new RuntimeException( 'Your Composer version is too old.' . ' Please update Composer (run `composer self-update`) to v2.0.14 or later' @@ -537,7 +539,7 @@ private function configureKint(): void Kint::$plugins = $config->plugins; } - $csp = Services::csp(); + $csp = service('csp'); if ($csp->enabled()) { RichRenderer::$js_nonce = $csp->getScriptNonce(); RichRenderer::$css_nonce = $csp->getStyleNonce(); diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 14b9a136..8e2564e0 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -145,10 +145,11 @@ public function getClassname(string $file): string if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] === 'phpnamespace' || $tokens[$i - 2][1] === 'namespace')) || ($dlm && $tokens[$i - 1][0] === T_NS_SEPARATOR && $token[0] === T_STRING)) { if (! $dlm) { - $namespace = 0; + $namespace = ''; } + if (isset($token[1])) { - $namespace = $namespace ? $namespace . '\\' . $token[1] : $token[1]; + $namespace = $namespace !== '' ? $namespace . '\\' . $token[1] : $token[1]; $dlm = true; } } elseif ($dlm && ($token[0] !== T_NS_SEPARATOR) && ($token[0] !== T_STRING)) { @@ -194,8 +195,9 @@ public function search(string $path, string $ext = 'php', bool $prioritizeApp = foreach ($this->getNamespaces() as $namespace) { if (isset($namespace['path']) && is_file($namespace['path'] . $path)) { - $fullPath = $namespace['path'] . $path; - $fullPath = realpath($fullPath) ?: $fullPath; + $fullPath = $namespace['path'] . $path; + $resolvedPath = realpath($fullPath); + $fullPath = $resolvedPath !== false ? $resolvedPath : $fullPath; if ($prioritizeApp) { $foundPaths[] = $fullPath; @@ -272,14 +274,16 @@ protected function getNamespaces() */ public function findQualifiedNameFromPath(string $path) { - $path = realpath($path) ?: $path; + $resolvedPath = realpath($path); + $path = $resolvedPath !== false ? $resolvedPath : $path; if (! is_file($path)) { return false; } foreach ($this->getNamespaces() as $namespace) { - $namespace['path'] = realpath($namespace['path']) ?: $namespace['path']; + $resolvedNamespacePath = realpath($namespace['path']); + $namespace['path'] = $resolvedNamespacePath !== false ? $resolvedNamespacePath : $namespace['path']; if ($namespace['path'] === '') { continue; @@ -331,8 +335,9 @@ public function listFiles(string $path): array helper('filesystem'); foreach ($this->getNamespaces() as $namespace) { - $fullPath = $namespace['path'] . $path; - $fullPath = realpath($fullPath) ?: $fullPath; + $fullPath = $namespace['path'] . $path; + $resolvedPath = realpath($fullPath); + $fullPath = $resolvedPath !== false ? $resolvedPath : $fullPath; if (! is_dir($fullPath)) { continue; @@ -365,8 +370,9 @@ public function listNamespaceFiles(string $prefix, string $path): array // autoloader->getNamespace($prefix) returns an array of paths for that namespace foreach ($this->autoloader->getNamespace($prefix) as $namespacePath) { - $fullPath = rtrim($namespacePath, '/') . '/' . $path; - $fullPath = realpath($fullPath) ?: $fullPath; + $fullPath = rtrim($namespacePath, '/') . '/' . $path; + $resolvedPath = realpath($fullPath); + $fullPath = $resolvedPath !== false ? $resolvedPath : $fullPath; if (! is_dir($fullPath)) { continue; @@ -392,8 +398,9 @@ public function listNamespaceFiles(string $prefix, string $path): array */ protected function legacyLocate(string $file, ?string $folder = null) { - $path = APPPATH . ($folder === null ? $file : $folder . '/' . $file); - $path = realpath($path) ?: $path; + $path = APPPATH . ($folder === null ? $file : $folder . '/' . $file); + $resolvedPath = realpath($path); + $path = $resolvedPath !== false ? $resolvedPath : $path; if (is_file($path)) { return $path; diff --git a/system/Autoloader/FileLocatorCached.php b/system/Autoloader/FileLocatorCached.php index adf45330..8b173994 100644 --- a/system/Autoloader/FileLocatorCached.php +++ b/system/Autoloader/FileLocatorCached.php @@ -36,6 +36,8 @@ final class FileLocatorCached implements FileLocatorInterface * [ * 'search' => [$path => $foundPaths], * ] + * + * @var array> */ private array $cache = []; @@ -114,6 +116,9 @@ public function getClassname(string $file): string return $classname; } + /** + * @return list + */ public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array { if (isset($this->cache['search'][$path][$ext][$prioritizeApp])) { diff --git a/system/Autoloader/FileLocatorInterface.php b/system/Autoloader/FileLocatorInterface.php index 3f7355a8..3a1112dd 100644 --- a/system/Autoloader/FileLocatorInterface.php +++ b/system/Autoloader/FileLocatorInterface.php @@ -53,6 +53,8 @@ public function getClassname(string $file): string; * 'app/Modules/foo/Config/Routes.php', * 'app/Modules/bar/Config/Routes.php', * ] + * + * @return list */ public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array; diff --git a/system/BaseModel.php b/system/BaseModel.php index 2b364bdc..05c5459d 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -26,7 +26,6 @@ use CodeIgniter\Pager\Pager; use CodeIgniter\Validation\ValidationInterface; use Config\Feature; -use Config\Services; use InvalidArgumentException; use ReflectionClass; use ReflectionException; @@ -47,7 +46,7 @@ * - process various callbacks * - allow intermingling calls to the db connection * - * @phpstan-type row_array array + * @phpstan-type row_array array * @phpstan-type event_data_beforeinsert array{data: row_array} * @phpstan-type event_data_afterinsert array{id: int|string, data: row_array, result: bool} * @phpstan-type event_data_beforefind array{id?: int|string, method: string, singleton: bool, limit?: int, offset?: int} @@ -571,8 +570,8 @@ abstract public function countAllResults(bool $reset = true, bool $test = false) * Loops over records in batches, allowing you to operate on them. * This method works only with dbCalls. * - * @param int $size Size - * @param Closure $userFunc Callback Function + * @param int $size Size + * @param Closure(array|object): mixed $userFunc Callback Function * * @return void * @@ -640,7 +639,7 @@ public function findColumn(string $columnName) $resultSet = $this->doFindColumn($columnName); - return $resultSet ? array_column($resultSet, $columnName) : null; + return $resultSet !== null ? array_column($resultSet, $columnName) : null; } /** @@ -1138,7 +1137,7 @@ public function delete($id = null, bool $purge = false) throw new InvalidArgumentException('delete(): argument #1 ($id) should not be boolean.'); } - if ($id && (is_numeric($id) || is_string($id))) { + if (! in_array($id, [null, 0, '0'], true) && (is_numeric($id) || is_string($id))) { $id = [$id]; } @@ -1251,7 +1250,7 @@ public function errors(bool $forceDB = false) } // Do we have validation errors? - if (! $forceDB && ! $this->skipValidation && ($errors = $this->validation->getErrors())) { + if (! $forceDB && ! $this->skipValidation && ($errors = $this->validation->getErrors()) !== []) { return $errors; } @@ -1609,7 +1608,7 @@ public function getValidationRules(array $options = []): array protected function ensureValidation(): void { if ($this->validation === null) { - $this->validation = Services::validation(null, false); + $this->validation = service('validation', null, false); } } @@ -1800,8 +1799,6 @@ protected function objectToRawArray($object, bool $onlyChanged = true, bool $rec // Loop over each property, // saving the name/value in a new array we can return. foreach ($props as $prop) { - // Must make protected values accessible. - $prop->setAccessible(true); $properties[$prop->getName()] = $prop->getValue($object); } } diff --git a/system/Boot.php b/system/Boot.php index 54468a5c..4b2d20ca 100644 --- a/system/Boot.php +++ b/system/Boot.php @@ -196,7 +196,16 @@ protected static function definePathConstants(Paths $paths): void // The path to the writable directory. if (! defined('WRITEPATH')) { - define('WRITEPATH', realpath(rtrim($paths->writableDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); + $writePath = realpath(rtrim($paths->writableDirectory, '\\/ ')); + + if ($writePath === false) { + header('HTTP/1.1 503 Service Unavailable.', true, 503); + echo 'The WRITEPATH is not set correctly.'; + + // EXIT_ERROR is not yet defined + exit(1); + } + define('WRITEPATH', $writePath . DIRECTORY_SEPARATOR); } // The path to the tests directory @@ -246,12 +255,12 @@ protected static function loadAutoloader(): void protected static function autoloadHelpers(): void { - Services::autoloader()->loadHelpers(); + service('autoloader')->loadHelpers(); } protected static function setExceptionHandler(): void { - Services::exceptions()->initialize(); + service('exceptions')->initialize(); } protected static function checkMissingExtensions(): void @@ -290,7 +299,7 @@ protected static function checkMissingExtensions(): void protected static function initializeKint(): void { - Services::autoloader()->initializeKint(CI_DEBUG); + service('autoloader')->initializeKint(CI_DEBUG); } protected static function loadConfigCache(): FactoriesCache @@ -308,7 +317,7 @@ protected static function loadConfigCache(): FactoriesCache */ protected static function initializeCodeIgniter(): CodeIgniter { - $app = Config\Services::codeigniter(); + $app = service('codeigniter'); $app->initialize(); $context = is_cli() ? 'php-cli' : 'web'; $app->setContext($context); diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index bfe1511e..0b5b01b1 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -14,7 +14,6 @@ namespace CodeIgniter\CLI; use CodeIgniter\CLI\Exceptions\CLIException; -use Config\Services; use InvalidArgumentException; use Throwable; @@ -226,12 +225,12 @@ public static function prompt(string $field, $options = null, $validation = null $extraOutput = ''; $default = ''; - if ($validation && ! is_array($validation) && ! is_string($validation)) { + if (isset($validation) && ! is_array($validation) && ! is_string($validation)) { throw new InvalidArgumentException('$rules can only be of type string|array'); } if (! is_array($validation)) { - $validation = $validation ? explode('|', $validation) : []; + $validation = ($validation !== null) ? explode('|', $validation) : []; } if (is_string($options)) { @@ -349,7 +348,7 @@ public static function promptByMultipleKeys(string $text, array $options): array // return the prompt again if $input contain(s) non-numeric character, except a comma. // And if max from $options less than max from input, // it means user tried to access null value in $options - if (! $pattern || $maxOptions < $maxInput) { + if ($pattern === 0 || $maxOptions < $maxInput) { static::error('Please select correctly.'); CLI::newLine(); @@ -416,7 +415,7 @@ protected static function validate(string $field, string $value, $rules): bool { $label = $field; $field = 'temp'; - $validation = Services::validation(null, false); + $validation = service('validation', null, false); $validation->setRules([ $field => [ 'label' => $label, @@ -442,7 +441,7 @@ protected static function validate(string $field, string $value, $rules): bool */ public static function print(string $text = '', ?string $foreground = null, ?string $background = null) { - if ($foreground || $background) { + if ((string) $foreground !== '' || (string) $background !== '') { $text = static::color($text, $foreground, $background); } @@ -458,7 +457,7 @@ public static function print(string $text = '', ?string $foreground = null, ?str */ public static function write(string $text = '', ?string $foreground = null, ?string $background = null) { - if ($foreground || $background) { + if ((string) $foreground !== '' || (string) $background !== '') { $text = static::color($text, $foreground, $background); } @@ -481,7 +480,7 @@ public static function error(string $text, string $foreground = 'light_red', ?st $stdout = static::$isColored; static::$isColored = static::hasColorSupport(STDERR); - if ($foreground || $background) { + if ($foreground !== '' || (string) $background !== '') { $text = static::color($text, $foreground, $background); } @@ -514,7 +513,7 @@ public static function beep(int $num = 1) */ public static function wait(int $seconds, bool $countdown = false) { - if ($countdown === true) { + if ($countdown) { $time = $seconds; while ($time > 0) { @@ -590,7 +589,7 @@ public static function color(string $text, string $foreground, ?string $backgrou throw CLIException::forInvalidColor('foreground', $foreground); } - if ($background !== null && ! array_key_exists($background, static::$background_colors)) { + if ((string) $background !== '' && ! array_key_exists($background, static::$background_colors)) { throw CLIException::forInvalidColor('background', $background); } @@ -638,7 +637,7 @@ private static function getColoredText(string $text, string $foreground, ?string { $string = "\033[" . static::$foreground_colors[$foreground] . 'm'; - if ($background !== null) { + if ((string) $background !== '') { $string .= "\033[" . static::$background_colors[$background] . 'm'; } @@ -655,7 +654,7 @@ private static function getColoredText(string $text, string $foreground, ?string */ public static function strlen(?string $string): int { - if ($string === null) { + if ((string) $string === '') { return 0; } @@ -769,7 +768,7 @@ public static function generateDimensions() // Look for the next lines ending in ": " // Searching for "Columns:" or "Lines:" will fail on non-English locales - if ($return === 0 && $output && preg_match('/:\s*(\d+)\n[^:]+:\s*(\d+)\n/', implode("\n", $output), $matches)) { + if ($return === 0 && $output !== [] && preg_match('/:\s*(\d+)\n[^:]+:\s*(\d+)\n/', implode("\n", $output), $matches)) { static::$height = (int) $matches[1]; static::$width = (int) $matches[2]; } @@ -836,7 +835,7 @@ public static function showProgress($thisStep = 1, int $totalSteps = 10) */ public static function wrap(?string $string = null, int $max = 0, int $padLeft = 0): string { - if ($string === null || $string === '') { + if ((string) $string === '') { return ''; } diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index 43d316f8..2e35864f 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -67,7 +67,7 @@ public static function validateKey($key, $prefix = ''): string } $reserved = config(Cache::class)->reservedCharacters ?? self::RESERVED_CHARACTERS; - if ($reserved && strpbrk($key, $reserved) !== false) { + if ($reserved !== '' && strpbrk($key, $reserved) !== false) { throw new InvalidArgumentException('Cache key contains reserved characters ' . $reserved); } diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index cf45a654..525616a7 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -308,7 +308,7 @@ protected function deleteFiles(string $path, bool $delDir = false, bool $htdocs if ($filename !== '.' && $filename !== '..') { if (is_dir($path . DIRECTORY_SEPARATOR . $filename) && $filename[0] !== '.') { $this->deleteFiles($path . DIRECTORY_SEPARATOR . $filename, $delDir, $htdocs, $_level + 1); - } elseif ($htdocs !== true || ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) { + } elseif (! $htdocs || preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename) !== 1) { @unlink($path . DIRECTORY_SEPARATOR . $filename); } } @@ -316,7 +316,7 @@ protected function deleteFiles(string $path, bool $delDir = false, bool $htdocs closedir($currentDir); - return ($delDir === true && $_level > 0) ? @rmdir($path) : true; + return ($delDir && $_level > 0) ? @rmdir($path) : true; } /** @@ -337,13 +337,13 @@ protected function getDirFileInfo(string $sourceDir, bool $topLevelOnly = true, $relativePath = $sourceDir; if ($fp = @opendir($sourceDir)) { - // reset the array and make sure $source_dir has a trailing slash on the initial call + // reset the array and make sure $sourceDir has a trailing slash on the initial call if ($_recursion === false) { $_filedata = []; $sourceDir = rtrim(realpath($sourceDir) ?: $sourceDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; } - // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast + // Used to be foreach (scandir($sourceDir, 1) as $file), but scandir() is simply not as fast while (false !== ($file = readdir($fp))) { if (is_dir($sourceDir . $file) && $file[0] !== '.' && $topLevelOnly === false) { $this->getDirFileInfo($sourceDir . $file . DIRECTORY_SEPARATOR, $topLevelOnly, true); diff --git a/system/Cache/Handlers/PredisHandler.php b/system/Cache/Handlers/PredisHandler.php index 59e35aa4..fc018503 100644 --- a/system/Cache/Handlers/PredisHandler.php +++ b/system/Cache/Handlers/PredisHandler.php @@ -19,6 +19,7 @@ use Exception; use Predis\Client; use Predis\Collection\Iterator\Keyspace; +use Predis\Response\Status; /** * Predis cache handler @@ -121,7 +122,7 @@ public function save(string $key, $value, int $ttl = 60) return false; } - if (! $this->redis->hmset($key, ['__ci_type' => $dataType, '__ci_value' => $value])) { + if (! $this->redis->hmset($key, ['__ci_type' => $dataType, '__ci_value' => $value]) instanceof Status) { return false; } diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index 22f4e435..6cd43506 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -108,7 +108,7 @@ public function initialize() public function get(string $key) { $key = static::validateKey($key, $this->prefix); - $data = $this->redis->hMGet($key, ['__ci_type', '__ci_value']); + $data = $this->redis->hMget($key, ['__ci_type', '__ci_value']); if (! isset($data['__ci_type'], $data['__ci_value']) || $data['__ci_value'] === false) { return null; @@ -147,7 +147,7 @@ public function save(string $key, $value, int $ttl = 60) return false; } - if (! $this->redis->hMSet($key, ['__ci_type' => $dataType, '__ci_value' => $value])) { + if (! $this->redis->hMset($key, ['__ci_type' => $dataType, '__ci_value' => $value])) { return false; } diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 48fbc9d7..af1ef4ed 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -56,7 +56,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.5.5'; + public const CI_VERSION = '4.5.6'; /** * App startup time. @@ -819,7 +819,7 @@ protected function tryToRouteIt(?RouteCollectionInterface $routes = null) { $this->benchmark->start('routing'); - if ($routes === null) { + if (! $routes instanceof RouteCollectionInterface) { $routes = service('routes')->loadRoutes(); } diff --git a/system/Commands/ListCommands.php b/system/Commands/ListCommands.php index 3411b103..8761e2bb 100644 --- a/system/Commands/ListCommands.php +++ b/system/Commands/ListCommands.php @@ -71,6 +71,8 @@ class ListCommands extends BaseCommand /** * Displays the help for the spark cli script itself. + * + * @return int */ public function run(array $params) { @@ -78,7 +80,7 @@ public function run(array $params) ksort($commands); // Check for 'simple' format - return array_key_exists('simple', $params) || CLI::getOption('simple') + return array_key_exists('simple', $params) || CLI::getOption('simple') === true ? $this->listSimple($commands) : $this->listFull($commands); } @@ -86,7 +88,7 @@ public function run(array $params) /** * Lists the commands with accompanying info. * - * @return void + * @return int */ protected function listFull(array $commands) { @@ -124,17 +126,21 @@ protected function listFull(array $commands) CLI::newLine(); } } + + return EXIT_SUCCESS; } /** * Lists the commands only. * - * @return void + * @return int */ protected function listSimple(array $commands) { foreach (array_keys($commands) as $title) { CLI::write($title); } + + return EXIT_SUCCESS; } } diff --git a/system/Commands/Server/Serve.php b/system/Commands/Server/Serve.php index 82e58998..594e4e58 100644 --- a/system/Commands/Server/Serve.php +++ b/system/Commands/Server/Serve.php @@ -110,7 +110,7 @@ public function run(array $params) // to ensure our environment is set and it simulates basic mod_rewrite. passthru($php . ' -S ' . $host . ':' . $port . ' -t ' . $docroot . ' ' . $rewrite, $status); - if ($status && $this->portOffset < $this->tries) { + if ($status !== EXIT_SUCCESS && $this->portOffset < $this->tries) { $this->portOffset++; $this->run($params); diff --git a/system/Commands/Utilities/Routes.php b/system/Commands/Utilities/Routes.php index 9adcc1bc..a0f81f6b 100644 --- a/system/Commands/Utilities/Routes.php +++ b/system/Commands/Utilities/Routes.php @@ -86,7 +86,7 @@ public function run(array $params) $host = $params['host'] ?? null; // Set HTTP_HOST - if ($host) { + if ($host !== null) { $request = service('request'); $_SERVER = $request->getServer(); $_SERVER['HTTP_HOST'] = $host; @@ -96,7 +96,7 @@ public function run(array $params) $collection = service('routes')->loadRoutes(); // Reset HTTP_HOST - if ($host) { + if ($host !== null) { unset($_SERVER['HTTP_HOST']); } @@ -139,7 +139,9 @@ public function run(array $params) $autoRoutes = $autoRouteCollector->get(); // Check for Module Routes. - if ($routingConfig = config(Routing::class)) { + $routingConfig = config(Routing::class); + + if ($routingConfig instanceof Routing) { foreach ($routingConfig->moduleRoutes as $uri => $namespace) { $autoRouteCollector = new AutoRouteCollectorImproved( $namespace, @@ -188,7 +190,7 @@ public function run(array $params) usort($tbody, static fn ($handler1, $handler2) => strcmp($handler1[3], $handler2[3])); } - if ($host) { + if ($host !== null) { CLI::write('Host: ' . $host); } diff --git a/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php b/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php index 57996934..f6a4f371 100644 --- a/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php +++ b/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php @@ -36,7 +36,7 @@ public function __construct( private readonly string $defaultMethod, private readonly array $httpMethods, private readonly array $protectedControllers, - private string $prefix = '' + private readonly string $prefix = '' ) { } @@ -122,8 +122,10 @@ private function addFilters($routes) $filtersShortest = $filterCollector->get($route['method'], $routePath . $sampleUri); // Get common array elements - $filters['before'] = array_intersect($filtersLongest['before'], $filtersShortest['before']); - $filters['after'] = array_intersect($filtersLongest['after'], $filtersShortest['after']); + $filters = [ + 'before' => array_intersect($filtersLongest['before'], $filtersShortest['before']), + 'after' => array_intersect($filtersLongest['after'], $filtersShortest['after']), + ]; $route['before'] = implode(' ', array_map(class_basename(...), $filters['before'])); $route['after'] = implode(' ', array_map(class_basename(...), $filters['after'])); diff --git a/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php b/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php index e08a16ff..2755a389 100644 --- a/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php +++ b/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php @@ -221,7 +221,7 @@ private function getRouteForDefaultController( if ($classShortname === $defaultController) { $pattern = '#' . preg_quote(lcfirst($defaultController), '#') . '\z#'; $routeWithoutController = rtrim(preg_replace($pattern, '', $uriByClass), '/'); - $routeWithoutController = $routeWithoutController ?: '/'; + $routeWithoutController = $routeWithoutController !== '' && $routeWithoutController !== '0' ? $routeWithoutController : '/'; [$params, $routeParams] = $this->getParameters($method); diff --git a/system/Commands/Utilities/Routes/ControllerMethodReader.php b/system/Commands/Utilities/Routes/ControllerMethodReader.php index c443b669..b40a832b 100644 --- a/system/Commands/Utilities/Routes/ControllerMethodReader.php +++ b/system/Commands/Utilities/Routes/ControllerMethodReader.php @@ -161,7 +161,7 @@ private function getRouteWithoutController( $pattern = '#' . preg_quote(lcfirst($defaultController), '#') . '\z#'; $routeWithoutController = rtrim(preg_replace($pattern, '', $uriByClass), '/'); - $routeWithoutController = $routeWithoutController ?: '/'; + $routeWithoutController = $routeWithoutController !== '' && $routeWithoutController !== '0' ? $routeWithoutController : '/'; return [[ 'route' => $routeWithoutController, diff --git a/system/Commands/Utilities/Routes/FilterCollector.php b/system/Commands/Utilities/Routes/FilterCollector.php index 002a529b..b646dcb2 100644 --- a/system/Commands/Utilities/Routes/FilterCollector.php +++ b/system/Commands/Utilities/Routes/FilterCollector.php @@ -13,7 +13,6 @@ namespace CodeIgniter\Commands\Utilities\Routes; -use CodeIgniter\Config\Services; use CodeIgniter\Filters\Filters; use CodeIgniter\HTTP\Method; use CodeIgniter\HTTP\Request; @@ -68,7 +67,7 @@ public function get(string $method, string $uri): array ]; } - $request = Services::incomingrequest(null, false); + $request = service('incomingrequest', null, false); $request->setMethod($method); $router = $this->createRouter($request); @@ -86,7 +85,7 @@ public function get(string $method, string $uri): array */ public function getRequiredFilters(): array { - $request = Services::incomingrequest(null, false); + $request = service('incomingrequest', null, false); $request->setMethod(Method::GET); $router = $this->createRouter($request); diff --git a/system/Common.php b/system/Common.php index dcc9487c..b647c9d2 100644 --- a/system/Common.php +++ b/system/Common.php @@ -441,7 +441,7 @@ function esc($data, string $context = 'html', ?string $encoding = null) $escaper = new Escaper($encoding); } - if ($encoding && $escaper->getEncoding() !== $encoding) { + if ($encoding !== null && $escaper->getEncoding() !== $encoding) { $escaper = new Escaper($encoding); } @@ -580,8 +580,8 @@ function helper($filenames): void foreach ($filenames as $filename) { // Store our system and application helper // versions so that we can control the load ordering. - $systemHelper = null; - $appHelper = null; + $systemHelper = ''; + $appHelper = ''; $localIncludes = []; if (! str_contains($filename, '_helper')) { @@ -598,7 +598,7 @@ function helper($filenames): void if (str_contains($filename, '\\')) { $path = $loader->locateFile($filename, 'Helpers'); - if (empty($path)) { + if ($path !== '') { throw FileNotFoundException::forFileNotFound($filename); } @@ -620,7 +620,7 @@ function helper($filenames): void } // App-level helpers should override all others - if (! empty($appHelper)) { + if ($appHelper !== '') { $includes[] = $appHelper; $loaded[] = $filename; } @@ -629,7 +629,7 @@ function helper($filenames): void $includes = [...$includes, ...$localIncludes]; // And the system default one should be added in last. - if (! empty($systemHelper)) { + if ($systemHelper !== '') { $includes[] = $systemHelper; $loaded[] = $filename; } @@ -739,13 +739,13 @@ function lang(string $line, array $args = [], ?string $locale = null) // Get active locale $activeLocale = $language->getLocale(); - if ($locale && $locale !== $activeLocale) { + if ((string) $locale !== '' && $locale !== $activeLocale) { $language->setLocale($locale); } $lines = $language->getLine($line, $args); - if ($locale && $locale !== $activeLocale) { + if ((string) $locale !== '' && $locale !== $activeLocale) { // Reset to active locale $language->setLocale($activeLocale); } @@ -849,7 +849,7 @@ function redirect(?string $route = null): RedirectResponse { $response = service('redirectresponse'); - if ($route !== null) { + if ((string) $route !== '') { return $response->route($route); } @@ -869,7 +869,7 @@ function _solidus(?DocTypes $docTypesConfig = null): string { static $docTypes = null; - if ($docTypesConfig !== null) { + if ($docTypesConfig instanceof DocTypes) { $docTypes = $docTypesConfig; } @@ -1092,7 +1092,7 @@ function stringify_attributes($attributes, bool $js = false): string { $atts = ''; - if (empty($attributes)) { + if ($attributes === '' || $attributes === [] || $attributes === null) { return $atts; } diff --git a/system/Config/DotEnv.php b/system/Config/DotEnv.php index db7152fd..d4a7d9e8 100644 --- a/system/Config/DotEnv.php +++ b/system/Config/DotEnv.php @@ -94,7 +94,7 @@ public function parse(): ?array */ protected function setVariable(string $name, string $value = '') { - if (! getenv($name, true)) { + if (getenv($name, true) === false) { putenv("{$name}={$value}"); } diff --git a/system/Config/Factories.php b/system/Config/Factories.php index d98664a2..b845d582 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -37,7 +37,7 @@ final class Factories * * @var array> */ - private static $options = []; + private static array $options = []; /** * Explicit options for the Config @@ -65,7 +65,7 @@ final class Factories * * @var array> */ - private static $aliases = []; + private static array $aliases = []; /** * Store for instances of any component that @@ -78,7 +78,7 @@ final class Factories * * @var array> */ - private static $instances = []; + private static array $instances = []; /** * Whether the component instances are updated? @@ -87,7 +87,7 @@ final class Factories * * @internal For caching only */ - private static $updated = []; + private static array $updated = []; /** * Define the class to load. You can *override* the concrete class. @@ -162,7 +162,7 @@ public static function __callStatic(string $component, array $arguments) } // Try to locate the class - if (! $class = self::locateClass($options, $alias)) { + if (($class = self::locateClass($options, $alias)) === null) { return null; } @@ -213,7 +213,7 @@ private static function getDefinedInstance(array $options, string $alias, array } // Try to locate the class - if (! $class = self::locateClass($options, $alias)) { + if (($class = self::locateClass($options, $alias)) === null) { return null; } @@ -310,7 +310,7 @@ class_exists($alias, false) } // No namespace? Search for it // Check all namespaces, prioritizing App and modules - elseif (! $files = $locator->search($options['path'] . DIRECTORY_SEPARATOR . $alias)) { + elseif (($files = $locator->search($options['path'] . DIRECTORY_SEPARATOR . $alias)) === []) { return null; } diff --git a/system/Config/Services.php b/system/Config/Services.php index e37b1278..aa8c180a 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -202,7 +202,7 @@ public static function csp(?CSPConfig $config = null, bool $getShared = true) */ public static function curlrequest(array $options = [], ?ResponseInterface $response = null, ?App $config = null, bool $getShared = true) { - if ($getShared === true) { + if ($getShared) { return static::getSharedInstance('curlrequest', $options, $response, $config); } @@ -211,7 +211,7 @@ public static function curlrequest(array $options = [], ?ResponseInterface $resp return new CURLRequest( $config, - new URI($options['base_uri'] ?? null), + new URI($options['baseURI'] ?? null), $response, $options ); @@ -230,7 +230,7 @@ public static function email($config = null, bool $getShared = true) return static::getSharedInstance('email', $config); } - if (empty($config) || ! (is_array($config) || $config instanceof EmailConfig)) { + if (empty($config) || (! is_array($config) && ! $config instanceof EmailConfig)) { $config = config(EmailConfig::class); } @@ -345,7 +345,7 @@ public static function image(?string $handler = null, ?Images $config = null, bo $config ??= config(Images::class); assert($config instanceof Images); - $handler = $handler ?: $config->defaultHandler; + $handler = $handler !== null && $handler !== '' && $handler !== '0' ? $handler : $config->defaultHandler; $class = $config->handlers[$handler]; return new $class($config); @@ -385,7 +385,7 @@ public static function language(?string $locale = null, bool $getShared = true) } // Use '?:' for empty string check - $locale = $locale ?: $requestLocale; + $locale = $locale !== null && $locale !== '' && $locale !== '0' ? $locale : $requestLocale; return new Language($locale); } @@ -484,7 +484,7 @@ public static function parser(?string $viewPath = null, ?ViewConfig $config = nu return static::getSharedInstance('parser', $viewPath, $config); } - $viewPath = $viewPath ?: (new Paths())->viewDirectory; + $viewPath = $viewPath !== null && $viewPath !== '' && $viewPath !== '0' ? $viewPath : (new Paths())->viewDirectory; $config ??= config(ViewConfig::class); return new Parser($config, $viewPath, AppServices::get('locator'), CI_DEBUG, AppServices::get('logger')); @@ -503,7 +503,7 @@ public static function renderer(?string $viewPath = null, ?ViewConfig $config = return static::getSharedInstance('renderer', $viewPath, $config); } - $viewPath = $viewPath ?: (new Paths())->viewDirectory; + $viewPath = $viewPath !== null && $viewPath !== '' && $viewPath !== '0' ? $viewPath : (new Paths())->viewDirectory; $config ??= config(ViewConfig::class); return new View($config, $viewPath, AppServices::get('locator'), CI_DEBUG, AppServices::get('logger')); @@ -705,7 +705,7 @@ public static function session(?SessionConfig $config = null, bool $getShared = // See https://www.php.net/manual/en/function.session-cache-limiter.php. // The headers are not managed by CI's Response class. // So, we remove CI's default Cache-Control header. - AppServices::response()->removeHeader('Cache-Control'); + AppServices::get('response')->removeHeader('Cache-Control'); $session->start(); } diff --git a/system/Controller.php b/system/Controller.php index fc0c41bf..9c6e7ee3 100644 --- a/system/Controller.php +++ b/system/Controller.php @@ -15,6 +15,7 @@ use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\Exceptions\HTTPException; +use CodeIgniter\HTTP\Exceptions\RedirectException; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; @@ -77,7 +78,7 @@ class Controller * * @return void * - * @throws HTTPException + * @throws HTTPException|RedirectException */ public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) { @@ -105,7 +106,7 @@ public function initController(RequestInterface $request, ResponseInterface $res * * @return void * - * @throws HTTPException + * @throws HTTPException|RedirectException */ protected function forceHTTPS(int $duration = 31_536_000) { diff --git a/system/Cookie/Cookie.php b/system/Cookie/Cookie.php index 01b10db1..d75cce26 100644 --- a/system/Cookie/Cookie.php +++ b/system/Cookie/Cookie.php @@ -482,7 +482,7 @@ public function withNeverExpiring() */ public function withPath(?string $path) { - $path = $path ?: self::$defaults['path']; + $path = $path !== null && $path !== '' && $path !== '0' ? $path : self::$defaults['path']; $this->validatePrefix($this->prefix, $this->secure, $path, $this->domain); $cookie = clone $this; diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 9d5a02db..e738a331 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -579,7 +579,7 @@ public function distinct(bool $val = true) */ public function from($from, bool $overwrite = false): self { - if ($overwrite === true) { + if ($overwrite) { $this->QBFrom = []; $this->db->setAliasedTables([]); } @@ -769,7 +769,7 @@ protected function whereHaving(string $qbKey, $key, $value = null, string $type foreach ($keyValue as $k => $v) { $prefix = empty($this->{$qbKey}) ? $this->groupGetType('') : $this->groupGetType($type); - if ($rawSqlOnly === true) { + if ($rawSqlOnly) { $k = ''; $op = ''; } elseif ($v !== null) { @@ -836,7 +836,7 @@ protected function whereHaving(string $qbKey, $key, $value = null, string $type * Generates a WHERE field IN('item', 'item') SQL query, * joined with 'AND' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -849,7 +849,7 @@ public function whereIn(?string $key = null, $values = null, ?bool $escape = nul * Generates a WHERE field IN('item', 'item') SQL query, * joined with 'OR' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -862,7 +862,7 @@ public function orWhereIn(?string $key = null, $values = null, ?bool $escape = n * Generates a WHERE field NOT IN('item', 'item') SQL query, * joined with 'AND' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -875,7 +875,7 @@ public function whereNotIn(?string $key = null, $values = null, ?bool $escape = * Generates a WHERE field NOT IN('item', 'item') SQL query, * joined with 'OR' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -888,7 +888,7 @@ public function orWhereNotIn(?string $key = null, $values = null, ?bool $escape * Generates a HAVING field IN('item', 'item') SQL query, * joined with 'AND' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -901,7 +901,7 @@ public function havingIn(?string $key = null, $values = null, ?bool $escape = nu * Generates a HAVING field IN('item', 'item') SQL query, * joined with 'OR' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -914,7 +914,7 @@ public function orHavingIn(?string $key = null, $values = null, ?bool $escape = * Generates a HAVING field NOT IN('item', 'item') SQL query, * joined with 'AND' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder):BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -927,7 +927,7 @@ public function havingNotIn(?string $key = null, $values = null, ?bool $escape = * Generates a HAVING field NOT IN('item', 'item') SQL query, * joined with 'OR' if appropriate. * - * @param array|BaseBuilder|Closure|string $values The values searched on, or anonymous function with subquery + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this */ @@ -942,8 +942,8 @@ public function orHavingNotIn(?string $key = null, $values = null, ?bool $escape * @used-by whereNotIn() * @used-by orWhereNotIn() * - * @param non-empty-string|null $key - * @param array|BaseBuilder|Closure|null $values The values searched on, or anonymous function with subquery + * @param non-empty-string|null $key + * @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery * * @return $this * @@ -1150,8 +1150,8 @@ protected function _like($field, string $match = '', string $type = 'AND ', stri $keyValue = ! is_array($field) ? [$field => $match] : $field; foreach ($keyValue as $k => $v) { - if ($insensitiveSearch === true) { - $v = strtolower($v); + if ($insensitiveSearch) { + $v = mb_strtolower($v, 'UTF-8'); } $prefix = empty($this->{$clause}) ? $this->groupGetType('') : $this->groupGetType($type); @@ -1187,7 +1187,7 @@ protected function _like($field, string $match = '', string $type = 'AND ', stri */ protected function _like_statement(?string $prefix, string $column, ?string $not, string $bind, bool $insensitiveSearch = false): string { - if ($insensitiveSearch === true) { + if ($insensitiveSearch) { return "{$prefix} LOWER(" . $this->db->escapeIdentifiers($column) . ") {$not} LIKE :{$bind}:"; } @@ -1197,7 +1197,7 @@ protected function _like_statement(?string $prefix, string $column, ?string $not /** * Add UNION statement * - * @param BaseBuilder|Closure $union + * @param BaseBuilder|Closure(BaseBuilder): BaseBuilder $union * * @return $this */ @@ -1209,7 +1209,7 @@ public function union($union) /** * Add UNION ALL statement * - * @param BaseBuilder|Closure $union + * @param BaseBuilder|Closure(BaseBuilder): BaseBuilder $union * * @return $this */ @@ -1222,7 +1222,7 @@ public function unionAll($union) * @used-by union() * @used-by unionAll() * - * @param BaseBuilder|Closure $union + * @param BaseBuilder|Closure(BaseBuilder): BaseBuilder $union * * @return $this */ @@ -1599,7 +1599,7 @@ public function getCompiledSelect(bool $reset = true): string { $select = $this->compileSelect(); - if ($reset === true) { + if ($reset) { $this->resetSelect(); } @@ -1643,7 +1643,7 @@ public function get(?int $limit = null, int $offset = 0, bool $reset = true) ? $this->getCompiledSelect($reset) : $this->db->query($this->compileSelect(), $this->binds, false); - if ($reset === true) { + if ($reset) { $this->resetSelect(); // Clear our binds so we don't eat up memory @@ -1678,7 +1678,7 @@ public function countAll(bool $reset = true) $query = $query->getRow(); - if ($reset === true) { + if ($reset) { $this->resetSelect(); } @@ -1727,7 +1727,7 @@ public function countAllResults(bool $reset = true) $result = $this->db->query($sql, $this->binds, false); - if ($reset === true) { + if ($reset) { $this->resetSelect(); } elseif (! isset($this->QBOrderBy)) { $this->QBOrderBy = $orderBy; @@ -1736,7 +1736,7 @@ public function countAllResults(bool $reset = true) // Restore the LIMIT setting $this->QBLimit = $limit; - $row = ! $result instanceof ResultInterface ? null : $result->getRow(); + $row = $result instanceof ResultInterface ? $result->getRow() : null; if (empty($row)) { return 0; @@ -1781,7 +1781,7 @@ public function getWhere($where = null, ?int $limit = null, ?int $offset = 0, bo ? $this->getCompiledSelect($reset) : $this->db->query($this->compileSelect(), $this->binds, false); - if ($reset === true) { + if ($reset) { $this->resetSelect(); // Clear our binds so we don't eat up memory @@ -2297,7 +2297,7 @@ public function getCompiledInsert(bool $reset = true) array_values($this->QBSet) ); - if ($reset === true) { + if ($reset) { $this->resetWrite(); } @@ -2466,7 +2466,7 @@ public function getCompiledUpdate(bool $reset = true) $sql = $this->_update($this->QBFrom[0], $this->QBSet); - if ($reset === true) { + if ($reset) { $this->resetWrite(); } @@ -3167,11 +3167,11 @@ protected function compileWhereHaving(string $qbKey): string $op = $this->getOperator($condition); if ( $op === false - || ! preg_match( + || preg_match( '/^(\(?)(.*)(' . preg_quote($op, '/') . ')\s*(.*(?connID) { @@ -610,7 +610,7 @@ abstract protected function execute(string $sql); */ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, string $queryClass = '') { - $queryClass = $queryClass ?: $this->queryClass; + $queryClass = $queryClass !== '' && $queryClass !== '0' ? $queryClass : $this->queryClass; if (empty($this->connID)) { $this->initialize(); @@ -684,7 +684,7 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s // Let others do something with this query. Events::trigger('DBQuery', $query); - if ($exception !== null) { + if ($exception instanceof DatabaseException) { throw new DatabaseException( $exception->getMessage(), $exception->getCode(), @@ -846,9 +846,9 @@ public function transBegin(bool $testMode = false): bool } // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back + // If the $testMode flag is set to TRUE transactions will be rolled back // even if the queries produce a successful result. - $this->transFailure = ($testMode === true); + $this->transFailure = $testMode; if ($this->_transBegin()) { $this->transDepth++; @@ -959,6 +959,8 @@ public function newQuery(): BaseBuilder * ->get(); * }) * + * @param Closure(BaseConnection): mixed $func + * * @return BasePreparedQuery|null */ public function prepare(Closure $func, array $options = []) @@ -1124,7 +1126,7 @@ public function protectIdentifiers($item, bool $prefixSingle = false, ?bool $pro $item = preg_replace('/^' . $this->swapPre . '(\S+?)/', $this->DBPrefix . '\\1', $item); } // Do we prefix an item with no segments? - elseif ($prefixSingle === true && ! str_starts_with($item, $this->DBPrefix)) { + elseif ($prefixSingle && ! str_starts_with($item, $this->DBPrefix)) { $item = $this->DBPrefix . $item; } } @@ -1147,7 +1149,7 @@ private function protectDotItem(string $item, string $alias, bool $protectIdenti // NOTE: The ! empty() condition prevents this method // from breaking when QB isn't enabled. if (! empty($this->aliasedTables) && in_array($parts[0], $this->aliasedTables, true)) { - if ($protectIdentifiers === true) { + if ($protectIdentifiers) { foreach ($parts as $key => $val) { if (! in_array($val, $this->reservedIdentifiers, true)) { $parts[$key] = $this->escapeIdentifiers($val); @@ -1198,7 +1200,7 @@ private function protectDotItem(string $item, string $alias, bool $protectIdenti $item = implode('.', $parts); } - if ($protectIdentifiers === true) { + if ($protectIdentifiers) { $item = $this->escapeIdentifiers($item); } @@ -1372,7 +1374,7 @@ public function escapeString($str, bool $like = false) $str = $this->_escapeString($str); // escape LIKE condition wildcards - if ($like === true) { + if ($like) { return str_replace( [ $this->likeEscapeChar, @@ -1501,7 +1503,7 @@ public function listTables(bool $constrainByPrefix = false) */ public function tableExists(string $tableName, bool $cached = true): bool { - if ($cached === true) { + if ($cached) { return in_array($this->protectIdentifiers($tableName, true, false, false), $this->listTables(), true); } diff --git a/system/Database/BasePreparedQuery.php b/system/Database/BasePreparedQuery.php index 8c4f252c..6ba2f6eb 100644 --- a/system/Database/BasePreparedQuery.php +++ b/system/Database/BasePreparedQuery.php @@ -259,4 +259,12 @@ public function getErrorMessage(): string { return $this->errorString; } + + /** + * Whether the input contain binary data. + */ + protected function isBinary(string $input): bool + { + return mb_detect_encoding($input, 'UTF-8', true) === false; + } } diff --git a/system/Database/Database.php b/system/Database/Database.php index 23b6cd0f..e09dcdd2 100644 --- a/system/Database/Database.php +++ b/system/Database/Database.php @@ -96,7 +96,7 @@ protected function parseDSN(array $params): array { $dsn = parse_url($params['DSN']); - if (! $dsn) { + if ($dsn === 0 || $dsn === '' || $dsn === '0' || $dsn === [] || $dsn === false || $dsn === null) { throw new InvalidArgumentException('Your DSN connection string is invalid.'); } diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 595cd615..fc9aa54e 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -32,9 +32,9 @@ class Forge protected $db; /** - * List of fields. + * List of fields in the form `[name => attributes]` * - * @var array [name => attributes] + * @var array|string> */ protected $fields = []; @@ -449,12 +449,12 @@ public function addForeignKey( */ public function dropKey(string $table, string $keyName, bool $prefixKeyName = true): bool { - $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); + $keyName = $this->db->escapeIdentifiers(($prefixKeyName ? $this->db->DBPrefix : '') . $keyName); $table = $this->db->escapeIdentifiers($this->db->DBPrefix . $table); $dropKeyAsConstraint = $this->dropKeyAsConstraint($table, $keyName); - if ($dropKeyAsConstraint === true) { + if ($dropKeyAsConstraint) { $sql = sprintf( $this->dropConstraintStr, $table, @@ -559,7 +559,7 @@ public function createTable(string $table, bool $ifNotExists = false, array $att } // If table exists lets stop here - if ($ifNotExists === true && $this->db->tableExists($table, false)) { + if ($ifNotExists && $this->db->tableExists($table, false)) { $this->reset(); return true; @@ -573,7 +573,7 @@ public function createTable(string $table, bool $ifNotExists = false, array $att } // Most databases don't support creating indexes from within the CREATE TABLE statement - if (! empty($this->keys)) { + if ($this->keys !== []) { for ($i = 0, $sqls = $this->_processIndexes($table), $c = count($sqls); $i < $c; $i++) { $this->db->query($sqls[$i]); } @@ -650,7 +650,7 @@ public function dropTable(string $tableName, bool $ifExists = false, bool $casca return false; } - if ($this->db->DBPrefix && str_starts_with($tableName, $this->db->DBPrefix)) { + if ($this->db->DBPrefix !== '' && str_starts_with($tableName, $this->db->DBPrefix)) { $tableName = substr($tableName, strlen($this->db->DBPrefix)); } @@ -895,7 +895,7 @@ protected function _processFields(bool $createTable = false): array $attributes = array_change_key_case($attributes, CASE_UPPER); - if ($createTable === true && empty($attributes['TYPE'])) { + if ($createTable && empty($attributes['TYPE'])) { continue; } @@ -942,7 +942,7 @@ protected function _processFields(bool $createTable = false): array } else { $field['null'] = ' NOT ' . $this->null; } - } elseif ($createTable === true) { + } elseif ($createTable) { $field['null'] = ' NOT ' . $this->null; } @@ -1085,7 +1085,7 @@ protected function _processPrimaryKeys(string $table, bool $asQuery = false): st } if (isset($this->primaryKeys['fields']) && $this->primaryKeys['fields'] !== []) { - if ($asQuery === true) { + if ($asQuery) { $sql .= 'ALTER TABLE ' . $this->db->escapeIdentifiers($this->db->DBPrefix . $table) . ' ADD '; } else { $sql .= ",\n\t"; @@ -1108,10 +1108,12 @@ public function processIndexes(string $table): bool $fk = $this->foreignKeys; if ($this->fields === []) { - $this->fields = array_flip(array_map( - static fn ($columnName) => $columnName->name, - $this->db->getFieldData($this->db->DBPrefix . $table) - )); + $fieldData = $this->db->getFieldData($this->db->DBPrefix . $table); + + $this->fields = array_combine( + array_map(static fn ($columnName) => $columnName->name, $fieldData), + array_fill(0, count($fieldData), []) + ); } $fields = $this->fields; @@ -1229,7 +1231,7 @@ protected function _processForeignKeys(string $table, bool $asQuery = false): ar $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); - if ($asQuery === true) { + if ($asQuery) { $sqls[$index] .= 'ALTER TABLE ' . $this->db->escapeIdentifiers($this->db->DBPrefix . $table) . ' ADD '; } else { $sqls[$index] .= ",\n\t"; diff --git a/system/Database/Migration.php b/system/Database/Migration.php index 4386b744..ca9e5e0c 100644 --- a/system/Database/Migration.php +++ b/system/Database/Migration.php @@ -45,7 +45,7 @@ public function __construct(?Forge $forge = null) { if (isset($this->DBGroup)) { $this->forge = Database::forge($this->DBGroup); - } elseif ($forge !== null) { + } elseif ($forge instanceof Forge) { $this->forge = $forge; } else { $this->forge = Database::forge(config(Database::class)->defaultGroup); diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index 055b8ba6..13919c78 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -19,7 +19,6 @@ use CodeIgniter\I18n\Time; use Config\Database; use Config\Migrations as MigrationsConfig; -use Config\Services; use RuntimeException; use stdClass; @@ -390,7 +389,7 @@ public function force(string $path, string $namespace, ?string $group = null) */ public function findMigrations(): array { - $namespaces = $this->namespace ? [$this->namespace] : array_keys(Services::autoloader()->getNamespace()); + $namespaces = $this->namespace !== null ? [$this->namespace] : array_keys(service('autoloader')->getNamespace()); $migrations = []; foreach ($namespaces as $namespace) { @@ -415,7 +414,7 @@ public function findMigrations(): array public function findNamespaceMigrations(string $namespace): array { $migrations = []; - $locator = Services::locator(true); + $locator = service('locator', true); if (! empty($this->path)) { helper('filesystem'); @@ -451,11 +450,11 @@ protected function migrationFromFile(string $path, string $namespace) $filename = basename($path, '.php'); - if (! preg_match($this->regex, $filename)) { + if (preg_match($this->regex, $filename) !== 1) { return false; } - $locator = Services::locator(true); + $locator = service('locator', true); $migration = new stdClass(); @@ -525,7 +524,7 @@ protected function getMigrationNumber(string $migration): string { preg_match($this->regex, $migration, $matches); - return count($matches) ? $matches[1] : '0'; + return $matches !== [] ? $matches[1] : '0'; } /** @@ -540,7 +539,7 @@ protected function getMigrationName(string $migration): string { preg_match($this->regex, $migration, $matches); - return count($matches) ? $matches[2] : ''; + return $matches !== [] ? $matches[2] : ''; } /** @@ -646,7 +645,7 @@ public function getHistory(string $group = 'default'): array } // If a namespace was specified then use it - if ($this->namespace) { + if ($this->namespace !== null) { $builder->where('namespace', $this->namespace); } @@ -701,7 +700,7 @@ public function getLastBatch(): int ->get() ->getResultObject(); - $batch = is_array($batch) && count($batch) + $batch = is_array($batch) && $batch !== [] ? end($batch)->batch : 0; @@ -726,7 +725,7 @@ public function getBatchStart(int $batch): string ->get() ->getResultObject(); - return count($migration) ? $migration[0]->version : '0'; + return $migration !== [] ? $migration[0]->version : '0'; } /** diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 068dfba5..f98f51fb 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -57,7 +57,7 @@ class Connection extends BaseConnection /** * MySQLi object * - * Has to be preserved without being assigned to $conn_id. + * Has to be preserved without being assigned to $connId. * * @var false|mysqli */ @@ -96,7 +96,7 @@ public function connect(bool $persistent = false) $port = null; $socket = $this->hostname; } else { - $hostname = ($persistent === true) ? 'p:' . $this->hostname : $this->hostname; + $hostname = $persistent ? 'p:' . $this->hostname : $this->hostname; $port = empty($this->port) ? null : $this->port; $socket = ''; } @@ -193,7 +193,7 @@ public function connect(bool $persistent = false) $clientFlags )) { // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails - if (($clientFlags & MYSQLI_CLIENT_SSL) && version_compare($this->mysqli->client_info, 'mysqlnd 5.7.3', '<=') + if (($clientFlags & MYSQLI_CLIENT_SSL) !== 0 && version_compare($this->mysqli->client_info, 'mysqlnd 5.7.3', '<=') && empty($this->mysqli->query("SHOW STATUS LIKE 'ssl_cipher'")->fetch_object()->Value) ) { $this->mysqli->close(); @@ -297,7 +297,7 @@ public function getVersion(): string /** * Executes the query against the database. * - * @return false|mysqli_result; + * @return false|mysqli_result */ protected function execute(string $sql) { @@ -395,11 +395,11 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n { $sql = 'SHOW TABLES FROM ' . $this->escapeIdentifier($this->database); - if ($tableName !== null) { + if ((string) $tableName !== '') { return $sql . ' LIKE ' . $this->escape($tableName); } - if ($prefixLimit !== false && $this->DBPrefix !== '') { + if ($prefixLimit && $this->DBPrefix !== '') { return $sql . " LIKE '" . $this->escapeLikeStringDirect($this->DBPrefix) . "%'"; } @@ -462,7 +462,9 @@ protected function _indexData(string $table): array throw new DatabaseException(lang('Database.failGetIndexData')); } - if (! $indexes = $query->getResultArray()) { + $indexes = $query->getResultArray(); + + if ($indexes === []) { return []; } diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index 132bde12..c47ff4de 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -116,11 +116,11 @@ protected function _createTableAttributes(array $attributes): string } } - if ($this->db->charset !== '' && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET')) { + if ($this->db->charset !== '' && ! str_contains($sql, 'CHARACTER SET') && ! str_contains($sql, 'CHARSET')) { $sql .= ' DEFAULT CHARACTER SET = ' . $this->db->escapeString($this->db->charset); } - if ($this->db->DBCollat !== '' && ! strpos($sql, 'COLLATE')) { + if ($this->db->DBCollat !== '' && ! str_contains($sql, 'COLLATE')) { $sql .= ' COLLATE = ' . $this->db->escapeString($this->db->DBCollat); } @@ -222,7 +222,7 @@ protected function _processIndexes(string $table, bool $asQuery = false): array implode('_', $this->keys[$i]['fields']) : $this->keys[$i]['keyName']); - if ($asQuery === true) { + if ($asQuery) { $sqls[$index] = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table) . " ADD {$unique}KEY " . $keyName . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i]['fields'])) . ')'; diff --git a/system/Database/MySQLi/PreparedQuery.php b/system/Database/MySQLi/PreparedQuery.php index e9a6c6d1..34eedc25 100644 --- a/system/Database/MySQLi/PreparedQuery.php +++ b/system/Database/MySQLi/PreparedQuery.php @@ -66,15 +66,19 @@ public function _execute(array $data): bool throw new BadMethodCallException('You must call prepare before trying to execute a prepared statement.'); } - // First off -bind the parameters - $bindTypes = ''; + // First off - bind the parameters + $bindTypes = ''; + $binaryData = []; // Determine the type string - foreach ($data as $item) { + foreach ($data as $key => $item) { if (is_int($item)) { $bindTypes .= 'i'; } elseif (is_numeric($item)) { $bindTypes .= 'd'; + } elseif (is_string($item) && $this->isBinary($item)) { + $bindTypes .= 'b'; + $binaryData[$key] = $item; } else { $bindTypes .= 's'; } @@ -83,6 +87,11 @@ public function _execute(array $data): bool // Bind it $this->statement->bind_param($bindTypes, ...$data); + // Stream binary data + foreach ($binaryData as $key => $value) { + $this->statement->send_long_data($key, $value); + } + try { return $this->statement->execute(); } catch (mysqli_sql_exception $e) { diff --git a/system/Database/OCI8/Builder.php b/system/Database/OCI8/Builder.php index 4423a524..24a6bb53 100644 --- a/system/Database/OCI8/Builder.php +++ b/system/Database/OCI8/Builder.php @@ -109,12 +109,12 @@ protected function _replace(string $table, array $keys, array $values): string { $fieldNames = array_map(static fn ($columnName) => trim($columnName, '"'), $keys); - $uniqueIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames) { + $uniqueIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames): bool { $hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields); return ($index->type === 'PRIMARY') && $hasAllFields; }); - $replaceableFields = array_filter($keys, static function ($columnName) use ($uniqueIndexes) { + $replaceableFields = array_filter($keys, static function ($columnName) use ($uniqueIndexes): bool { foreach ($uniqueIndexes as $index) { if (in_array(trim($columnName, '"'), $index->fields, true)) { return false; @@ -344,7 +344,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri if (empty($constraints)) { $fieldNames = array_map(static fn ($columnName) => trim($columnName, '"'), $keys); - $uniqueIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames) { + $uniqueIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames): bool { $hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields); return ($index->type === 'PRIMARY' || $index->type === 'UNIQUE') && $hasAllFields; diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index 2b870b75..c59d588c 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -274,7 +274,7 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n return $sql . ' WHERE "TABLE_NAME" LIKE ' . $this->escape($tableName); } - if ($prefixLimit !== false && $this->DBPrefix !== '') { + if ($prefixLimit && $this->DBPrefix !== '') { return $sql . ' WHERE "TABLE_NAME" LIKE \'' . $this->escapeLikeString($this->DBPrefix) . "%' " . sprintf($this->likeEscapeStr, $this->likeEscapeChar); } diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 5a10363b..7d81cff8 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -135,7 +135,7 @@ protected function _alterTable(string $alterType, string $table, $processedField $wantToAddNull = ! str_contains($processedFields[$i]['null'], ' NOT'); $currentNullable = $nullableMap[$processedFields[$i]['name']]; - if ($wantToAddNull === true && $currentNullable === true) { + if ($wantToAddNull && $currentNullable === true) { $processedFields[$i]['null'] = ''; } elseif ($processedFields[$i]['null'] === '' && $currentNullable === false) { // Nullable by default @@ -293,7 +293,7 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade) { $sql = parent::_dropTable($table, $ifExists, $cascade); - if ($sql !== true && $cascade === true) { + if ($sql !== true && $cascade) { $sql .= ' CASCADE CONSTRAINTS PURGE'; } elseif ($sql !== true) { $sql .= ' PURGE'; diff --git a/system/Database/OCI8/PreparedQuery.php b/system/Database/OCI8/PreparedQuery.php index c267f806..e1577642 100644 --- a/system/Database/OCI8/PreparedQuery.php +++ b/system/Database/OCI8/PreparedQuery.php @@ -16,6 +16,7 @@ use BadMethodCallException; use CodeIgniter\Database\BasePreparedQuery; use CodeIgniter\Database\Exceptions\DatabaseException; +use OCILob; /** * Prepared query for OCI8 @@ -73,12 +74,24 @@ public function _execute(array $data): bool throw new BadMethodCallException('You must call prepare before trying to execute a prepared statement.'); } + $binaryData = null; + foreach (array_keys($data) as $key) { - oci_bind_by_name($this->statement, ':' . $key, $data[$key]); + if (is_string($data[$key]) && $this->isBinary($data[$key])) { + $binaryData = oci_new_descriptor($this->db->connID, OCI_D_LOB); + $binaryData->writeTemporary($data[$key], OCI_TEMP_BLOB); + oci_bind_by_name($this->statement, ':' . $key, $binaryData, -1, OCI_B_BLOB); + } else { + oci_bind_by_name($this->statement, ':' . $key, $data[$key]); + } } $result = oci_execute($this->statement, $this->db->commitMode); + if ($binaryData instanceof OCILob) { + $binaryData->free(); + } + if ($result && $this->lastInsertTableName !== '') { $this->db->lastInsertedTableName = $this->lastInsertTableName; } @@ -113,7 +126,7 @@ public function parameterize(string $sql): string // Track our current value $count = 0; - return preg_replace_callback('/\?/', static function ($matches) use (&$count) { + return preg_replace_callback('/\?/', static function ($matches) use (&$count): string { return ':' . ($count++); }, $sql); } diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index 0d2dce09..faae0eda 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -293,7 +293,7 @@ protected function _truncate(string $table): string */ protected function _like_statement(?string $prefix, string $column, ?string $not, string $bind, bool $insensitiveSearch = false): string { - $op = $insensitiveSearch === true ? 'ILIKE' : 'LIKE'; + $op = $insensitiveSearch ? 'ILIKE' : 'LIKE'; return "{$prefix} {$column} {$not} {$op} :{$bind}:"; } @@ -368,7 +368,7 @@ protected function _updateBatch(string $table, array $keys, array $values): stri $sql .= 'WHERE ' . implode( ' AND ', array_map( - static function ($key, $value) use ($table, $alias, $that) { + static function ($key, $value) use ($table, $alias, $that): string|RawSql { if ($value instanceof RawSql && is_string($key)) { return $table . '.' . $key . ' = ' . $value; } @@ -463,7 +463,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri $constraints = $this->QBOptions['constraints'] ?? []; if (empty($constraints)) { - $allIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames) { + $allIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames): bool { $hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields); return ($index->type === 'UNIQUE' || $index->type === 'PRIMARY') && $hasAllFields; @@ -575,7 +575,7 @@ protected function _deleteBatch(string $table, array $keys, array $values): stri $sql .= 'WHERE ' . implode( ' AND ', array_map( - static function ($key, $value) use ($table, $alias, $that) { + static function ($key, $value) use ($table, $alias, $that): RawSql|string { if ($value instanceof RawSql) { return $value; } diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index ec886dbd..4ce2b076 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -74,11 +74,11 @@ public function connect(bool $persistent = false) $this->convertDSN(); } - $this->connID = $persistent === true ? pg_pconnect($this->DSN) : pg_connect($this->DSN); + $this->connID = $persistent ? pg_pconnect($this->DSN) : pg_connect($this->DSN); if ($this->connID !== false) { if ( - $persistent === true + $persistent && pg_connection_status($this->connID) === PGSQL_CONNECTION_BAD && pg_ping($this->connID) === false ) { @@ -149,8 +149,9 @@ private function convertDSN() */ public function reconnect() { - if (pg_ping($this->connID) === false) { - $this->connID = false; + if ($this->connID === false || pg_ping($this->connID) === false) { + $this->close(); + $this->initialize(); } } @@ -289,7 +290,7 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n return $sql . ' AND "table_name" LIKE ' . $this->escape($tableName); } - if ($prefixLimit !== false && $this->DBPrefix !== '') { + if ($prefixLimit && $this->DBPrefix !== '') { return $sql . ' AND "table_name" LIKE \'' . $this->escapeLikeString($this->DBPrefix) . "%' " . sprintf($this->likeEscapeStr, $this->likeEscapeChar); @@ -462,7 +463,7 @@ public function error(): array { return [ 'code' => '', - 'message' => pg_last_error($this->connID) ?: '', + 'message' => pg_last_error($this->connID), ]; } diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index 7a61bed5..704f3e57 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -118,7 +118,7 @@ protected function _alterTable(string $alterType, string $table, $processedField $nullable = false; } $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($field['name']) - . ($nullable === true ? ' DROP' : ' SET') . ' NOT NULL'; + . ($nullable ? ' DROP' : ' SET') . ' NOT NULL'; if (! empty($field['new_name'])) { $sqls[] = $sql . ' RENAME COLUMN ' . $this->db->escapeIdentifiers($field['name']) @@ -173,6 +173,10 @@ protected function _attributeType(array &$attributes) $attributes['TYPE'] = 'TIMESTAMP'; break; + case 'BLOB': + $attributes['TYPE'] = 'BYTEA'; + break; + default: break; } @@ -195,7 +199,7 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade): str { $sql = parent::_dropTable($table, $ifExists, $cascade); - if ($cascade === true) { + if ($cascade) { $sql .= ' CASCADE'; } diff --git a/system/Database/Postgre/PreparedQuery.php b/system/Database/Postgre/PreparedQuery.php index fbea6ac1..c55d5d8c 100644 --- a/system/Database/Postgre/PreparedQuery.php +++ b/system/Database/Postgre/PreparedQuery.php @@ -87,6 +87,12 @@ public function _execute(array $data): bool throw new BadMethodCallException('You must call prepare before trying to execute a prepared statement.'); } + foreach ($data as &$item) { + if (is_string($item) && $this->isBinary($item)) { + $item = pg_escape_bytea($this->db->connID, $item); + } + } + $this->result = pg_execute($this->db->connID, $this->name, $data); return (bool) $this->result; @@ -119,7 +125,7 @@ public function parameterize(string $sql): string // Track our current value $count = 0; - return preg_replace_callback('/\?/', static function () use (&$count) { + return preg_replace_callback('/\?/', static function () use (&$count): string { $count++; return "\${$count}"; diff --git a/system/Database/SQLSRV/Builder.php b/system/Database/SQLSRV/Builder.php index a6d0b8e8..c1272cdc 100644 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -518,7 +518,7 @@ public function countAll(bool $reset = true) $query = $query->getRow(); - if ($reset === true) { + if ($reset) { $this->resetSelect(); } @@ -699,7 +699,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri if (empty($constraints)) { $tableIndexes = $this->db->getIndexData($table); - $uniqueIndexes = array_filter($tableIndexes, static function ($index) use ($fieldNames) { + $uniqueIndexes = array_filter($tableIndexes, static function ($index) use ($fieldNames): bool { $hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields); return $index->type === 'PRIMARY' && $hasAllFields; @@ -707,7 +707,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri // if no primary found then look for unique - since indexes have no order if ($uniqueIndexes === []) { - $uniqueIndexes = array_filter($tableIndexes, static function ($index) use ($fieldNames) { + $uniqueIndexes = array_filter($tableIndexes, static function ($index) use ($fieldNames): bool { $hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields); return $index->type === 'UNIQUE' && $hasAllFields; diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index 7e008e27..8ded4cd0 100644 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -215,7 +215,7 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n return $sql .= ' AND [TABLE_NAME] LIKE ' . $this->escape($tableName); } - if ($prefixLimit === true && $this->DBPrefix !== '') { + if ($prefixLimit && $this->DBPrefix !== '') { $sql .= " AND [TABLE_NAME] LIKE '" . $this->escapeLikeString($this->DBPrefix) . "%' " . sprintf($this->likeEscapeStr, $this->likeEscapeChar); } @@ -368,7 +368,11 @@ protected function _fieldData(string $table): array $retVal[$i]->max_length = $query[$i]->CHARACTER_MAXIMUM_LENGTH > 0 ? $query[$i]->CHARACTER_MAXIMUM_LENGTH - : $query[$i]->NUMERIC_PRECISION; + : ( + $query[$i]->CHARACTER_MAXIMUM_LENGTH === -1 + ? 'max' + : $query[$i]->NUMERIC_PRECISION + ); $retVal[$i]->nullable = $query[$i]->IS_NULLABLE !== 'NO'; $retVal[$i]->default = $query[$i]->COLUMN_DEFAULT; diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index cae4eda2..df3d55c7 100644 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -212,10 +212,10 @@ protected function _alterTable(string $alterType, string $table, $processedField $sql = <<db->escapeIdentifiers($field['name']) - . " {$field['type']}{$field['length']} " . ($nullable === true ? '' : 'NOT') . ' NULL'; + . " {$field['type']}{$field['length']} " . ($nullable ? '' : 'NOT') . ' NULL'; if (! empty($field['comment'])) { $sqls[] = 'EXEC sys.sp_addextendedproperty ' @@ -397,6 +397,11 @@ protected function _attributeType(array &$attributes) $attributes['TYPE'] = 'BIT'; break; + case 'BLOB': + $attributes['TYPE'] = 'VARBINARY'; + $attributes['CONSTRAINT'] ??= 'MAX'; + break; + default: break; } diff --git a/system/Database/SQLSRV/PreparedQuery.php b/system/Database/SQLSRV/PreparedQuery.php index f3a4a14c..42555592 100644 --- a/system/Database/SQLSRV/PreparedQuery.php +++ b/system/Database/SQLSRV/PreparedQuery.php @@ -59,7 +59,7 @@ public function _prepare(string $sql, array $options = []): PreparedQuery // Prepare parameters for the query $queryString = $this->getQueryString(); - $parameters = $this->parameterize($queryString); + $parameters = $this->parameterize($queryString, $options); // Prepare the query $this->statement = sqlsrv_prepare($this->db->connID, $sql, $parameters); @@ -120,8 +120,10 @@ protected function _close(): bool /** * Handle parameters. + * + * @param array $options */ - protected function parameterize(string $queryString): array + protected function parameterize(string $queryString, array $options): array { $numberOfVariables = substr_count($queryString, '?'); @@ -129,7 +131,11 @@ protected function parameterize(string $queryString): array for ($c = 0; $c < $numberOfVariables; $c++) { $this->parameters[$c] = null; - $params[] = &$this->parameters[$c]; + if (isset($options[$c])) { + $params[] = [&$this->parameters[$c], SQLSRV_PARAM_IN, $options[$c]]; + } else { + $params[] = &$this->parameters[$c]; + } } return $params; diff --git a/system/Database/SQLite3/Builder.php b/system/Database/SQLite3/Builder.php index a59270bb..15fc2529 100644 --- a/system/Database/SQLite3/Builder.php +++ b/system/Database/SQLite3/Builder.php @@ -145,7 +145,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri if (empty($constraints)) { $fieldNames = array_map(static fn ($columnName) => trim($columnName, '`'), $keys); - $allIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames) { + $allIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames): bool { $hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields); return ($index->type === 'PRIMARY' || $index->type === 'UNIQUE') && $hasAllFields; diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 9945d41d..577e4b24 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -89,7 +89,7 @@ public function connect(bool $persistent = false) $this->database = WRITEPATH . $this->database; } - $sqlite = (! $this->password) + $sqlite = (! isset($this->password) || $this->password !== '') ? new SQLite3($this->database) : new SQLite3($this->database, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->password); @@ -194,7 +194,7 @@ protected function _escapeString(string $str): string */ protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string { - if ($tableName !== null) { + if ((string) $tableName !== '') { return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\'' . ' AND "NAME" NOT LIKE \'sqlite!_%\' ESCAPE \'!\'' . ' AND "NAME" LIKE ' . $this->escape($tableName); @@ -202,7 +202,7 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\'' . ' AND "NAME" NOT LIKE \'sqlite!_%\' ESCAPE \'!\'' - . (($prefixLimit !== false && $this->DBPrefix !== '') + . (($prefixLimit && $this->DBPrefix !== '') ? ' AND "NAME" LIKE \'' . $this->escapeLikeString($this->DBPrefix) . '%\' ' . sprintf($this->likeEscapeStr, $this->likeEscapeChar) : ''); } @@ -359,7 +359,7 @@ protected function _indexData(string $table): array */ protected function _foreignKeyData(string $table): array { - if ($this->supportsForeignKeys() !== true) { + if (! $this->supportsForeignKeys()) { return []; } diff --git a/system/Database/SQLite3/PreparedQuery.php b/system/Database/SQLite3/PreparedQuery.php index 21dc4c2f..0e15b5d6 100644 --- a/system/Database/SQLite3/PreparedQuery.php +++ b/system/Database/SQLite3/PreparedQuery.php @@ -75,6 +75,8 @@ public function _execute(array $data): bool $bindType = SQLITE3_INTEGER; } elseif (is_float($item)) { $bindType = SQLITE3_FLOAT; + } elseif (is_string($item) && $this->isBinary($item)) { + $bindType = SQLITE3_BLOB; } else { $bindType = SQLITE3_TEXT; } diff --git a/system/Database/Seeder.php b/system/Database/Seeder.php index be7cfcdf..c70a34b4 100644 --- a/system/Database/Seeder.php +++ b/system/Database/Seeder.php @@ -105,7 +105,7 @@ public function __construct(Database $config, ?BaseConnection $db = null) */ public static function faker(): ?Generator { - if (self::$faker === null && class_exists(Factory::class)) { + if (! self::$faker instanceof Generator && class_exists(Factory::class)) { self::$faker = Factory::create(); } diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index e5f7ecb0..aa353732 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -22,7 +22,6 @@ use CodeIgniter\HTTP\ResponseInterface; use Config\Exceptions as ExceptionsConfig; use Config\Paths; -use Config\Services; use ErrorException; use Psr\Log\LogLevel; use Throwable; @@ -126,7 +125,7 @@ public function exceptionHandler(Throwable $exception) [$statusCode, $exitCode] = $this->determineCodes($exception); - $this->request = Services::request(); + $this->request = service('request'); if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes, true)) { $uri = $this->request->getPath() === '' ? '/' : $this->request->getPath(); @@ -155,7 +154,7 @@ public function exceptionHandler(Throwable $exception) } } - $this->response = Services::response(); + $this->response = service('response'); if (method_exists($this->config, 'handler')) { // Use new ExceptionHandler diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 9f20f987..b939e208 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -25,7 +25,6 @@ use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\I18n\Time; -use Config\Services; use Config\Toolbar as ToolbarConfig; use Kint\Kint; @@ -386,7 +385,7 @@ public function prepare(?RequestInterface $request = null, ?ResponseInterface $r return; } - $toolbar = Services::toolbar(config(ToolbarConfig::class)); + $toolbar = service('toolbar', config(ToolbarConfig::class)); $stats = $app->getPerformanceStats(); $data = $toolbar->run( $stats['startTime'], @@ -529,7 +528,7 @@ protected function format(string $data, string $format = 'html'): string case 'html': $data['styles'] = []; extract($data); - $parser = Services::parser($this->config->viewsPath, null, false); + $parser = service('parser', $this->config->viewsPath, null, false); ob_start(); include $this->config->viewsPath . 'toolbar.tpl.php'; $output = ob_get_clean(); diff --git a/system/Debug/Toolbar/Collectors/Database.php b/system/Debug/Toolbar/Collectors/Database.php index ee9f829b..1a923a35 100644 --- a/system/Debug/Toolbar/Collectors/Database.php +++ b/system/Debug/Toolbar/Collectors/Database.php @@ -148,7 +148,7 @@ protected function formatTimelineData(): array public function display(): array { $data = []; - $data['queries'] = array_map(static function (array $query) { + $data['queries'] = array_map(static function (array $query): array { $isDuplicate = $query['duplicate'] === true; $firstNonSystemLine = ''; diff --git a/system/Debug/Toolbar/Collectors/Logs.php b/system/Debug/Toolbar/Collectors/Logs.php index 2194526c..f36dfb1d 100644 --- a/system/Debug/Toolbar/Collectors/Logs.php +++ b/system/Debug/Toolbar/Collectors/Logs.php @@ -13,8 +13,6 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; -use Config\Services; - /** * Loags collector */ @@ -92,6 +90,6 @@ protected function collectLogs() return $this->data; } - return $this->data = Services::logger(true)->logCache ?? []; + return $this->data = service('logger', true)->logCache ?? []; } } diff --git a/system/Debug/Toolbar/Collectors/Routes.php b/system/Debug/Toolbar/Collectors/Routes.php index b6862dce..561399fb 100644 --- a/system/Debug/Toolbar/Collectors/Routes.php +++ b/system/Debug/Toolbar/Collectors/Routes.php @@ -14,7 +14,6 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; use CodeIgniter\Router\DefinedRouteCollector; -use Config\Services; use ReflectionException; use ReflectionFunction; use ReflectionMethod; @@ -74,8 +73,8 @@ class Routes extends BaseCollector */ public function display(): array { - $rawRoutes = Services::routes(true); - $router = Services::router(null, null, true); + $rawRoutes = service('routes', true); + $router = service('router', null, null, true); // Get our parameters // Closure routes @@ -153,7 +152,7 @@ public function display(): array */ public function getBadgeValue(): int { - $rawRoutes = Services::routes(true); + $rawRoutes = service('routes', true); return count($rawRoutes->getRoutes()); } diff --git a/system/Debug/Toolbar/Collectors/Timers.php b/system/Debug/Toolbar/Collectors/Timers.php index 163c9f57..d450cc5c 100644 --- a/system/Debug/Toolbar/Collectors/Timers.php +++ b/system/Debug/Toolbar/Collectors/Timers.php @@ -13,8 +13,6 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; -use Config\Services; - /** * Timers collector */ @@ -52,7 +50,7 @@ protected function formatTimelineData(): array { $data = []; - $benchmark = Services::timer(true); + $benchmark = service('timer', true); $rows = $benchmark->getTimers(6); foreach ($rows as $name => $info) { diff --git a/system/Email/Email.php b/system/Email/Email.php index b82779eb..5d420cc2 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -485,7 +485,7 @@ public function setFrom($from, $name = '', $returnPath = null) if ($this->validate) { $this->validateEmail($this->stringToArray($from)); - if ($returnPath) { + if ($returnPath !== null) { $this->validateEmail($this->stringToArray($returnPath)); } } @@ -495,7 +495,7 @@ public function setFrom($from, $name = '', $returnPath = null) if ($name !== '') { // only use Q encoding if there are characters that would require it - if (! preg_match('/[\200-\377]/', $name)) { + if (preg_match('/[\200-\377]/', $name) !== 1) { $name = '"' . addcslashes($name, "\0..\37\177'\"\\") . '"'; } else { $name = $this->prepQEncoding($name); @@ -532,7 +532,7 @@ public function setReplyTo($replyto, $name = '') $this->tmpArchive['replyName'] = $name; // only use Q encoding if there are characters that would require it - if (! preg_match('/[\200-\377]/', $name)) { + if (preg_match('/[\200-\377]/', $name) !== 1) { $name = '"' . addcslashes($name, "\0..\37\177'\"\\") . '"'; } else { $name = $this->prepQEncoding($name); @@ -1233,7 +1233,9 @@ protected function buildMessage() $this->headerStr .= $hdr; } - static::strlen($body) && $body .= $this->newline . $this->newline; + if (static::strlen($body) > 0) { + $body .= $this->newline . $this->newline; + } $body .= $this->getMimeMessage() . $this->newline . $this->newline . '--' . $lastBoundary . $this->newline diff --git a/system/Encryption/Handlers/OpenSSLHandler.php b/system/Encryption/Handlers/OpenSSLHandler.php index f3b62c64..9745160b 100644 --- a/system/Encryption/Handlers/OpenSSLHandler.php +++ b/system/Encryption/Handlers/OpenSSLHandler.php @@ -82,7 +82,7 @@ class OpenSSLHandler extends BaseHandler public function encrypt($data, $params = null) { // Allow key override - if ($params) { + if ($params !== null) { $this->key = is_array($params) && isset($params['key']) ? $params['key'] : $params; } @@ -118,7 +118,7 @@ public function encrypt($data, $params = null) public function decrypt($data, $params = null) { // Allow key override - if ($params) { + if ($params !== null) { $this->key = is_array($params) && isset($params['key']) ? $params['key'] : $params; } diff --git a/system/Encryption/Handlers/SodiumHandler.php b/system/Encryption/Handlers/SodiumHandler.php index b6c74da7..c74f2a5f 100644 --- a/system/Encryption/Handlers/SodiumHandler.php +++ b/system/Encryption/Handlers/SodiumHandler.php @@ -26,7 +26,7 @@ class SodiumHandler extends BaseHandler /** * Starter key * - * @var string + * @var string|null Null is used for buffer cleanup. */ protected $key = ''; diff --git a/system/Events/Events.php b/system/Events/Events.php index a06bd790..ad8efed6 100644 --- a/system/Events/Events.php +++ b/system/Events/Events.php @@ -84,7 +84,7 @@ public static function initialize() $files = service('locator')->search('Config/Events.php'); } - $files = array_filter(array_map(static function (string $file) { + $files = array_filter(array_map(static function (string $file): false|string { if (is_file($file)) { return realpath($file) ?: $file; } diff --git a/system/Exceptions/DownloadException.php b/system/Exceptions/DownloadException.php index df78127d..8ed2ba48 100644 --- a/system/Exceptions/DownloadException.php +++ b/system/Exceptions/DownloadException.php @@ -47,6 +47,8 @@ public static function forNotFoundDownloadSource() } /** + * @deprecated Since v4.5.6 + * * @return static */ public static function forCannotSetCache() diff --git a/system/Exceptions/PageNotFoundException.php b/system/Exceptions/PageNotFoundException.php index b1af079e..1a3b27a7 100644 --- a/system/Exceptions/PageNotFoundException.php +++ b/system/Exceptions/PageNotFoundException.php @@ -13,7 +13,6 @@ namespace CodeIgniter\Exceptions; -use Config\Services; use OutOfBoundsException; class PageNotFoundException extends OutOfBoundsException implements ExceptionInterface, HTTPExceptionInterface @@ -78,7 +77,7 @@ public static function forLocaleNotSupported(string $locale) */ private static function lang(string $line, array $args = []): string { - $lang = Services::language(null, false); + $lang = service('language', null, false); return $lang->getLine($line, $args); } diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 1255e0c4..fd252fe1 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -557,7 +557,7 @@ public function enableFilters(array $names, string $when = 'before') */ public function getArguments(?string $key = null) { - return $key === null ? $this->arguments : $this->arguments[$key]; + return ((string) $key === '') ? $this->arguments : $this->arguments[$key]; } // -------------------------------------------------------------------- @@ -674,7 +674,7 @@ protected function processMethods() */ protected function processFilters(?string $uri = null) { - if (! isset($this->config->filters) || ! $this->config->filters) { + if (! isset($this->config->filters) || $this->config->filters === []) { return; } diff --git a/system/Filters/Honeypot.php b/system/Filters/Honeypot.php index c2fb98c6..cdff293a 100644 --- a/system/Filters/Honeypot.php +++ b/system/Filters/Honeypot.php @@ -17,7 +17,6 @@ use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; -use Config\Services; /** * Honeypot filter @@ -40,7 +39,7 @@ public function before(RequestInterface $request, $arguments = null) return; } - if (Services::honeypot()->hasContent($request)) { + if (service('honeypot')->hasContent($request)) { throw HoneypotException::isBot(); } } diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 0b2c5737..fbc5165e 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -95,9 +95,7 @@ public function __construct(App $config) */ public function getPath(): string { - $path = implode('/', $this->segments); - - return ($path === '') ? '' : $path; + return implode('/', $this->segments); } /** diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index 9da231c1..90157e4e 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -389,7 +389,7 @@ public function send(string $method, string $url) $output = substr($output, strpos($output, $breakString) + 4); } - if (str_starts_with($output, 'HTTP/1.1 200 Connection established')) { + if (preg_match('/HTTP\/\d\.\d 200 Connection established/i', $output)) { $output = substr($output, strpos($output, $breakString) + 4); } diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 7582bc46..a6a2b26a 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -356,9 +356,9 @@ public function reportOnly(bool $value = true) } /** - * Adds a new base_uri value. Can be either a URI class or a simple string. + * Adds a new baseURI value. Can be either a URI class or a simple string. * - * base_uri restricts the URLs that can appear in a page's element. + * baseURI restricts the URLs that can appear in a page's element. * * @see http://www.w3.org/TR/CSP/#directive-base-uri * @@ -713,7 +713,7 @@ protected function generateNonces(ResponseInterface $response) $pattern = '/(' . preg_quote($this->styleNonceTag, '/') . '|' . preg_quote($this->scriptNonceTag, '/') . ')/'; - $body = preg_replace_callback($pattern, function ($match) { + $body = preg_replace_callback($pattern, function ($match): string { $nonce = $match[0] === $this->styleNonceTag ? $this->getStyleNonce() : $this->getScriptNonce(); return "nonce=\"{$nonce}\""; diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index 6b293fbc..a0da2e8c 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -85,7 +85,7 @@ public function __construct(string $filename, bool $setMime) */ public function setBinary(string $binary) { - if ($this->file !== null) { + if ($this->file instanceof File) { throw DownloadException::forCannotSetBinary(); } @@ -142,7 +142,7 @@ private function setContentTypeByMimeType(): void $mime = null; $charset = ''; - if ($this->setMime === true && ($lastDotPosition = strrpos($this->filename, '.')) !== false) { + if ($this->setMime && ($lastDotPosition = strrpos($this->filename, '.')) !== false) { $mime = Mimes::guessTypeFromExtension(substr($this->filename, $lastDotPosition + 1)); $charset = $this->charset; } @@ -242,16 +242,6 @@ public function noCache(): self return $this; } - /** - * Disables cache configuration. - * - * @throws DownloadException - */ - public function setCache(array $options = []) - { - throw DownloadException::forCannotSetCache(); - } - /** * {@inheritDoc} * @@ -290,10 +280,8 @@ public function buildHeaders() $this->setHeader('Content-Disposition', $this->getContentDisposition()); } - $this->setHeader('Expires-Disposition', '0'); $this->setHeader('Content-Transfer-Encoding', 'binary'); $this->setHeader('Content-Length', (string) $this->getContentLength()); - $this->noCache(); } /** @@ -309,7 +297,7 @@ public function sendBody() return $this->sendBodyByBinary(); } - if ($this->file !== null) { + if ($this->file instanceof File) { return $this->sendBodyByFilePath(); } diff --git a/system/HTTP/Exceptions/RedirectException.php b/system/HTTP/Exceptions/RedirectException.php index 5f2dcd05..3047bf96 100644 --- a/system/HTTP/Exceptions/RedirectException.php +++ b/system/HTTP/Exceptions/RedirectException.php @@ -69,7 +69,7 @@ public function __construct($message = '', int $code = 0, ?Throwable $previous = public function getResponse(): ResponseInterface { - if (null === $this->response) { + if (! $this->response instanceof ResponseInterface) { $this->response = service('response') ->redirect(base_url($this->getMessage()), 'auto', $this->getCode()); } diff --git a/system/HTTP/Files/FileCollection.php b/system/HTTP/Files/FileCollection.php index 079d5e73..add2c3c0 100644 --- a/system/HTTP/Files/FileCollection.php +++ b/system/HTTP/Files/FileCollection.php @@ -253,7 +253,7 @@ protected function getValueDotNotationSyntax(array $index, array $value) { $currentIndex = array_shift($index); - if (isset($currentIndex) && is_array($index) && $index && is_array($value[$currentIndex]) && $value[$currentIndex]) { + if (isset($currentIndex) && is_array($index) && $index !== [] && array_key_exists($currentIndex, $value) && is_array($value[$currentIndex])) { return $this->getValueDotNotationSyntax($index, $value[$currentIndex]); } diff --git a/system/HTTP/Files/UploadedFile.php b/system/HTTP/Files/UploadedFile.php index 78643aa7..c17a9e5a 100644 --- a/system/HTTP/Files/UploadedFile.php +++ b/system/HTTP/Files/UploadedFile.php @@ -302,7 +302,9 @@ public function getTempName(): string */ public function getExtension(): string { - return $this->guessExtension() ?: $this->getClientExtension(); + $guessExtension = $this->guessExtension(); + + return $guessExtension !== '' ? $guessExtension : $this->getClientExtension(); } /** diff --git a/system/HTTP/Negotiate.php b/system/HTTP/Negotiate.php index 67a03a38..33938c8f 100644 --- a/system/HTTP/Negotiate.php +++ b/system/HTTP/Negotiate.php @@ -39,7 +39,7 @@ class Negotiate */ public function __construct(?RequestInterface $request = null) { - if ($request !== null) { + if ($request instanceof RequestInterface) { assert($request instanceof IncomingRequest); $this->request = $request; @@ -233,7 +233,7 @@ public function parseHeader(string $header): array } // Sort to get the highest results first - usort($results, static function ($a, $b) { + usort($results, static function ($a, $b): int { if ($a['q'] === $b['q']) { $aAst = substr_count($a['value'], '*'); $bAst = substr_count($b['value'], '*'); diff --git a/system/HTTP/OutgoingRequest.php b/system/HTTP/OutgoingRequest.php index 27e473a5..e62f0724 100644 --- a/system/HTTP/OutgoingRequest.php +++ b/system/HTTP/OutgoingRequest.php @@ -64,7 +64,7 @@ private function getHostFromUri(URI $uri): string { $host = $uri->getHost(); - return $host . ($uri->getPort() ? ':' . $uri->getPort() : ''); + return $host . ($uri->getPort() > 0 ? ':' . $uri->getPort() : ''); } /** diff --git a/system/HTTP/RedirectResponse.php b/system/HTTP/RedirectResponse.php index 95bfadcd..206b1de0 100644 --- a/system/HTTP/RedirectResponse.php +++ b/system/HTTP/RedirectResponse.php @@ -15,7 +15,6 @@ use CodeIgniter\Cookie\CookieStore; use CodeIgniter\HTTP\Exceptions\HTTPException; -use Config\Services; /** * Handle a redirect response @@ -116,9 +115,8 @@ private function withErrors(): self { $validation = service('validation'); - if ($validation->getErrors()) { - $session = service('session'); - $session->setFlashdata('_ci_validation_errors', $validation->getErrors()); + if ($validation->getErrors() !== []) { + service('session')->setFlashdata('_ci_validation_errors', $validation->getErrors()); } return $this; @@ -148,7 +146,7 @@ public function with(string $key, $message) */ public function withCookies() { - $this->cookieStore = new CookieStore(Services::response()->getCookies()); + $this->cookieStore = new CookieStore(service('response')->getCookies()); return $this; } @@ -163,7 +161,7 @@ public function withCookies() */ public function withHeaders() { - foreach (Services::response()->headers() as $name => $value) { + foreach (service('response')->headers() as $name => $value) { if ($value instanceof Header) { $this->setHeader($name, $value->getValue()); } else { diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index 43a4f23a..dfa4b663 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -59,7 +59,7 @@ trait RequestTrait */ public function getIPAddress(): string { - if ($this->ipAddress) { + if ($this->ipAddress !== '') { return $this->ipAddress; } @@ -249,7 +249,7 @@ public function setGlobal(string $name, $value) * * @param string $name Supergrlobal name (lowercase) * @phpstan-param 'get'|'post'|'request'|'cookie'|'server' $name - * @param array|string|null $index + * @param array|int|string|null $index * @param int|null $filter Filter constant * @param array|int|null $flags Options * @@ -290,7 +290,7 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f } // Does the index contain array notation? - if (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) { + if (is_string($index) && ($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) { $value = $this->globals[$name]; for ($i = 0; $i < $count; $i++) { diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index a19fbb5f..ac853c3d 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -18,7 +18,6 @@ use CodeIgniter\HTTP\Exceptions\HTTPException; use Config\App; use Config\Cookie as CookieConfig; -use Config\Services; /** * Representation of an outgoing, server-side response. @@ -158,7 +157,7 @@ public function __construct($config) // @phpstan-ignore-line $this->noCache(); // We need CSP object even if not enabled to avoid calls to non existing methods - $this->CSP = Services::csp(); + $this->CSP = service('csp'); $this->cookieStore = new CookieStore([]); diff --git a/system/HTTP/ResponseInterface.php b/system/HTTP/ResponseInterface.php index 827eea44..382a2847 100644 --- a/system/HTTP/ResponseInterface.php +++ b/system/HTTP/ResponseInterface.php @@ -210,7 +210,7 @@ public function setContentType(string $mime, string $charset = 'UTF-8'); /** * Converts the $body into JSON and sets the Content Type header. * - * @param array|string $body + * @param array|object|string $body * * @return $this */ diff --git a/system/HTTP/ResponseTrait.php b/system/HTTP/ResponseTrait.php index 45f07d18..eaf60f2b 100644 --- a/system/HTTP/ResponseTrait.php +++ b/system/HTTP/ResponseTrait.php @@ -123,18 +123,21 @@ public function setDate(DateTime $date) */ public function setLink(PagerInterface $pager) { - $links = ''; + $links = ''; + $previous = $pager->getPreviousPageURI(); - if ($previous = $pager->getPreviousPageURI()) { + if (is_string($previous) && $previous !== '') { $links .= '<' . $pager->getPageURI($pager->getFirstPage()) . '>; rel="first",'; $links .= '<' . $previous . '>; rel="prev"'; } - if (($next = $pager->getNextPageURI()) && $previous) { + $next = $pager->getNextPageURI(); + + if (is_string($next) && $next !== '' && is_string($previous) && $previous !== '') { $links .= ','; } - if ($next) { + if (is_string($next) && $next !== '') { $links .= '<' . $next . '>; rel="next",'; $links .= '<' . $pager->getPageURI($pager->getLastPage()) . '>; rel="last"'; } @@ -566,7 +569,7 @@ public function getCookieStore() */ public function hasCookie(string $name, ?string $value = null, string $prefix = ''): bool { - $prefix = $prefix ?: Cookie::setDefaults()['prefix']; // to retain BC + $prefix = $prefix !== '' ? $prefix : Cookie::setDefaults()['prefix']; // to retain BC return $this->cookieStore->has($name, $prefix, $value); } @@ -586,7 +589,7 @@ public function getCookie(?string $name = null, string $prefix = '') } try { - $prefix = $prefix ?: Cookie::setDefaults()['prefix']; // to retain BC + $prefix = $prefix !== '' ? $prefix : Cookie::setDefaults()['prefix']; // to retain BC return $this->cookieStore->get($name, $prefix); } catch (CookieException $e) { @@ -607,7 +610,7 @@ public function deleteCookie(string $name = '', string $domain = '', string $pat return $this; } - $prefix = $prefix ?: Cookie::setDefaults()['prefix']; // to retain BC + $prefix = $prefix !== '' ? $prefix : Cookie::setDefaults()['prefix']; // to retain BC $prefixed = $prefix . $name; $store = $this->cookieStore; diff --git a/system/HTTP/SiteURI.php b/system/HTTP/SiteURI.php index def61b65..2be70a77 100644 --- a/system/HTTP/SiteURI.php +++ b/system/HTTP/SiteURI.php @@ -41,7 +41,7 @@ class SiteURI extends URI /** * The Index File. */ - private string $indexPage; + private readonly string $indexPage; /** * List of URI segments in baseURL and indexPage. @@ -421,7 +421,7 @@ public function siteUrl($relativePath = '', ?string $scheme = null, ?App $config $relativePath = $this->stringifyRelativePath($relativePath); // Check current host. - $host = $config === null ? $this->getHost() : null; + $host = $config instanceof App ? null : $this->getHost(); $config ??= config(App::class); diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index efa99cf7..d0d23a3d 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -1177,7 +1177,8 @@ protected function parseStr(string $query): array parse_str($params, $result); foreach ($result as $key => $value) { - $return[hex2bin($key)] = $value; + // Array key might be int + $return[hex2bin((string) $key)] = $value; } return $return; diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 6a510477..ec4d1743 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -125,7 +125,7 @@ public function isBrowser(?string $key = null): bool } // No need to be specific, it's a browser - if ($key === null) { + if ((string) $key === '') { return true; } @@ -143,7 +143,7 @@ public function isRobot(?string $key = null): bool } // No need to be specific, it's a robot - if ($key === null) { + if ((string) $key === '') { return true; } @@ -161,7 +161,7 @@ public function isMobile(?string $key = null): bool } // No need to be specific, it's a mobile - if ($key === null) { + if ((string) $key === '') { return true; } @@ -289,7 +289,7 @@ protected function compileData() */ protected function setPlatform(): bool { - if (is_array($this->config->platforms) && $this->config->platforms) { + if (is_array($this->config->platforms) && $this->config->platforms !== []) { foreach ($this->config->platforms as $key => $val) { if (preg_match('|' . preg_quote($key, '|') . '|i', $this->agent)) { $this->platform = $val; @@ -309,7 +309,7 @@ protected function setPlatform(): bool */ protected function setBrowser(): bool { - if (is_array($this->config->browsers) && $this->config->browsers) { + if (is_array($this->config->browsers) && $this->config->browsers !== []) { foreach ($this->config->browsers as $key => $val) { if (preg_match('|' . $key . '.*?([0-9\.]+)|i', $this->agent, $match)) { $this->isBrowser = true; @@ -330,7 +330,7 @@ protected function setBrowser(): bool */ protected function setRobot(): bool { - if (is_array($this->config->robots) && $this->config->robots) { + if (is_array($this->config->robots) && $this->config->robots !== []) { foreach ($this->config->robots as $key => $val) { if (preg_match('|' . preg_quote($key, '|') . '|i', $this->agent)) { $this->isRobot = true; @@ -350,7 +350,7 @@ protected function setRobot(): bool */ protected function setMobile(): bool { - if (is_array($this->config->mobiles) && $this->config->mobiles) { + if (is_array($this->config->mobiles) && $this->config->mobiles !== []) { foreach ($this->config->mobiles as $key => $val) { if (false !== (stripos($this->agent, $key))) { $this->isMobile = true; diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index 76b8e8ae..4bbd3442 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -307,7 +307,7 @@ public static function recursiveCount(array $array, int $counter = 0): int */ public static function sortValuesByNatural(array &$array, $sortByIndex = null): bool { - return usort($array, static function ($currentValue, $nextValue) use ($sortByIndex) { + return usort($array, static function ($currentValue, $nextValue) use ($sortByIndex): int { if ($sortByIndex !== null) { return strnatcmp((string) $currentValue[$sortByIndex], (string) $nextValue[$sortByIndex]); } diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 2b86654c..d5f2d974 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -166,7 +166,7 @@ function delete_files(string $path, bool $delDir = false, bool $htdocs = false, continue; } - if (! $htdocs || ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) { + if (! $htdocs || preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename) !== 1) { $isDir = $object->isDir(); if ($isDir && $delDir) { rmdir($object->getPathname()); @@ -260,13 +260,13 @@ function get_dir_file_info(string $sourceDir, bool $topLevelOnly = true, bool $r try { $fp = opendir($sourceDir); - // reset the array and make sure $source_dir has a trailing slash on the initial call + // reset the array and make sure $sourceDir has a trailing slash on the initial call if ($recursion === false) { $fileData = []; $sourceDir = rtrim(realpath($sourceDir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; } - // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast + // Used to be foreach (scandir($sourceDir, 1) as $file), but scandir() is simply not as fast while (false !== ($file = readdir($fp))) { if (is_dir($sourceDir . $file) && $file[0] !== '.' && $topLevelOnly === false) { get_dir_file_info($sourceDir . $file . DIRECTORY_SEPARATOR, $topLevelOnly, true); diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 2e5b9f7c..7e113888 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -62,7 +62,7 @@ function form_open(string $action = '', $attributes = [], array $hidden = []): s // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites $before = service('filters')->getFilters()['before']; - if ((in_array('csrf', $before, true) || array_key_exists('csrf', $before)) && str_contains($action, base_url()) && ! stripos($form, 'method="get"')) { + if ((in_array('csrf', $before, true) || array_key_exists('csrf', $before)) && str_contains($action, base_url()) && stripos($form, 'method="get"') === false) { $form .= csrf_field($csrfId ?? null); } @@ -582,7 +582,7 @@ function set_select(string $field, string $value = '', bool $default = false): s } if ($input === null) { - return ($default === true) ? ' selected="selected"' : ''; + return $default ? ' selected="selected"' : ''; } if (is_array($input)) { @@ -636,7 +636,7 @@ function set_checkbox(string $field, string $value = '', bool $default = false): return ($input === $value) ? ' checked="checked"' : ''; } - return ($default === true) ? ' checked="checked"' : ''; + return $default ? ' checked="checked"' : ''; } } @@ -673,7 +673,7 @@ function set_radio(string $field, string $value = '', bool $default = false): st return ((string) $input === $value) ? ' checked="checked"' : ''; } - return ($default === true) ? ' checked="checked"' : ''; + return $default ? ' checked="checked"' : ''; } } @@ -788,7 +788,7 @@ function parse_form_attributes($attributes, array $default): string if (! is_bool($val)) { if ($key === 'value') { $val = esc($val); - } elseif ($key === 'name' && ! strlen($default['name'])) { + } elseif ($key === 'name' && $default['name'] === '') { continue; } $att .= $key . '="' . $val . '"' . ($key === array_key_last($default) ? '' : ' '); diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index b9e5e5c8..3eb55a6d 100644 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -112,8 +112,8 @@ function img($src = '', bool $indexPage = false, $attributes = ''): string $img = ' $v) { - if ($k === 'src' && ! preg_match('#^([a-z]+:)?//#i', $v)) { - if ($indexPage === true) { + if ($k === 'src' && preg_match('#^([a-z]+:)?//#i', $v) !== 1) { + if ($indexPage) { $script .= 'src="' . site_url($v) . '" '; } else { $script .= 'src="' . slash_item('baseURL') . $v . '" '; @@ -252,7 +252,7 @@ function link_tag( $href = $href['href'] ?? ''; } - if (! preg_match('#^([a-z]+:)?//#i', $href)) { + if (preg_match('#^([a-z]+:)?//#i', $href) !== 1) { $attributes['href'] = $indexPage ? site_url($href) : slash_item('baseURL') . $href; } else { $attributes['href'] = $href; @@ -302,7 +302,7 @@ function video($src, string $unsupportedMessage = '', string $attributes = '', a if (_has_protocol($src)) { $video .= ' src="' . $src . '"'; - } elseif ($indexPage === true) { + } elseif ($indexPage) { $video .= ' src="' . site_url($src) . '"'; } else { $video .= ' src="' . slash_item('baseURL') . $src . '"'; @@ -349,7 +349,7 @@ function audio($src, string $unsupportedMessage = '', string $attributes = '', a if (_has_protocol($src)) { $audio .= ' src="' . $src . '"'; - } elseif ($indexPage === true) { + } elseif ($indexPage) { $audio .= ' src="' . site_url($src) . '"'; } else { $audio .= ' src="' . slash_item('baseURL') . $src . '"'; @@ -422,7 +422,7 @@ function _media(string $name, array $types = [], string $unsupportedMessage = '' function source(string $src, string $type = 'unknown', string $attributes = '', bool $indexPage = false): string { if (! _has_protocol($src)) { - $src = $indexPage === true ? site_url($src) : slash_item('baseURL') . $src; + $src = $indexPage ? site_url($src) : slash_item('baseURL') . $src; } $source = 'hash(); while (true) { - if (connection_status() !== CONNECTION_NORMAL || connection_aborted()) { + if (connection_status() !== CONNECTION_NORMAL || connection_aborted() === 1) { break; } @@ -50,6 +50,7 @@ public function run(): void $this->sendEvent('reload', ['time' => date('Y-m-d H:i:s')]); break; } + if (mt_rand(1, 10) > 8) { $this->sendEvent('ping', ['time' => date('Y-m-d H:i:s')]); } diff --git a/system/I18n/TimeTrait.php b/system/I18n/TimeTrait.php index fefb2fc5..354b7542 100644 --- a/system/I18n/TimeTrait.php +++ b/system/I18n/TimeTrait.php @@ -73,7 +73,7 @@ trait TimeTrait */ public function __construct(?string $time = null, $timezone = null, ?string $locale = null) { - $this->locale = $locale ?: Locale::getDefault(); + $this->locale = $locale !== null && $locale !== '' && $locale !== '0' ? $locale : Locale::getDefault(); $time ??= ''; @@ -958,7 +958,7 @@ public function sameAs($testTime, ?string $timezone = null): bool if ($testTime instanceof DateTimeInterface) { $testTime = $testTime->format('Y-m-d H:i:s'); } elseif (is_string($testTime)) { - $timezone = $timezone ?: $this->timezone; + $timezone = $timezone !== null && $timezone !== '' && $timezone !== '0' ? $timezone : $this->timezone; $timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); $testTime = new DateTime($testTime, $timezone); $testTime = $testTime->format('Y-m-d H:i:s'); @@ -1108,7 +1108,7 @@ public function getUTCObject($time, ?string $timezone = null) if ($time instanceof self) { $time = $time->toDateTime(); } elseif (is_string($time)) { - $timezone = $timezone ?: $this->timezone; + $timezone = $timezone !== null && $timezone !== '' && $timezone !== '0' ? $timezone : $this->timezone; $timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); $time = new DateTime($time, $timezone); } diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index fa226ccf..583a2759 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -42,7 +42,7 @@ public function __construct($config = null) { parent::__construct($config); - if (! (extension_loaded('imagick') || class_exists(Imagick::class))) { + if (! extension_loaded('imagick') && ! class_exists(Imagick::class)) { throw ImageException::forMissingExtension('IMAGICK'); // @codeCoverageIgnore } @@ -81,7 +81,7 @@ public function _resize(bool $maintainRatio = false) $escape = ''; } - $action = $maintainRatio === true + $action = $maintainRatio ? ' -resize ' . ($this->width ?? 0) . 'x' . ($this->height ?? 0) . ' "' . $source . '" "' . $destination . '"' : ' -resize ' . ($this->width ?? 0) . 'x' . ($this->height ?? 0) . "{$escape}! \"" . $source . '" "' . $destination . '"'; diff --git a/system/Images/Image.php b/system/Images/Image.php index a2cf5143..55754e33 100644 --- a/system/Images/Image.php +++ b/system/Images/Image.php @@ -102,8 +102,9 @@ public function copy(string $targetPath, ?string $targetName = null, int $perms public function getProperties(bool $return = false) { $path = $this->getPathname(); + $vals = getimagesize($path); - if (! $vals = getimagesize($path)) { + if ($vals === false) { throw ImageException::forFileNotSupported(); } @@ -116,7 +117,7 @@ public function getProperties(bool $return = false) $mime = 'image/' . ($types[$vals[2]] ?? 'jpg'); - if ($return === true) { + if ($return) { return [ 'width' => $vals[0], 'height' => $vals[1], diff --git a/system/Log/Handlers/ChromeLoggerHandler.php b/system/Log/Handlers/ChromeLoggerHandler.php index 8d39d4c0..37c7c0ff 100644 --- a/system/Log/Handlers/ChromeLoggerHandler.php +++ b/system/Log/Handlers/ChromeLoggerHandler.php @@ -14,7 +14,6 @@ namespace CodeIgniter\Log\Handlers; use CodeIgniter\HTTP\ResponseInterface; -use Config\Services; /** * Class ChromeLoggerHandler @@ -156,8 +155,8 @@ protected function format($object) */ public function sendLogs(?ResponseInterface &$response = null) { - if ($response === null) { - $response = Services::response(null, true); + if (! $response instanceof ResponseInterface) { + $response = service('response', null, true); } $data = base64_encode( diff --git a/system/Log/Handlers/FileHandler.php b/system/Log/Handlers/FileHandler.php index 42dd9cee..64d1485f 100644 --- a/system/Log/Handlers/FileHandler.php +++ b/system/Log/Handlers/FileHandler.php @@ -76,6 +76,7 @@ public function handle($level, $message): bool $msg = ''; + $newfile = false; if (! is_file($filepath)) { $newfile = true; @@ -117,7 +118,7 @@ public function handle($level, $message): bool flock($fp, LOCK_UN); fclose($fp); - if (isset($newfile) && $newfile === true) { + if ($newfile) { chmod($filepath, $this->filePermissions); } diff --git a/system/Model.php b/system/Model.php index d5df3f86..98e3be0b 100644 --- a/system/Model.php +++ b/system/Model.php @@ -317,13 +317,13 @@ protected function doFirst() if ($this->tempUseSoftDeletes) { $builder->where($this->table . '.' . $this->deletedField, null); - } elseif ($this->useSoftDeletes && ($builder->QBGroupBy === []) && $this->primaryKey) { + } elseif ($this->useSoftDeletes && ($builder->QBGroupBy === []) && $this->primaryKey !== '') { $builder->groupBy($this->table . '.' . $this->primaryKey); } // Some databases, like PostgreSQL, need order // information to consistently return correct results. - if ($builder->QBGroupBy && ($builder->QBOrderBy === []) && $this->primaryKey) { + if ($builder->QBGroupBy !== [] && ($builder->QBOrderBy === []) && $this->primaryKey !== '') { $builder->orderBy($this->table . '.' . $this->primaryKey, 'asc'); } @@ -443,7 +443,7 @@ protected function doUpdate($id = null, $row = null): bool $builder = $this->builder(); - if ($id) { + if (! in_array($id, [null, '', 0, '0', []], true)) { $builder = $builder->whereIn($this->table . '.' . $this->primaryKey, $id); } @@ -496,7 +496,7 @@ protected function doDelete($id = null, bool $purge = false) $set = []; $builder = $this->builder(); - if ($id) { + if (! in_array($id, [null, '', 0, '0', []], true)) { $builder = $builder->whereIn($this->primaryKey, $id); } @@ -592,9 +592,9 @@ protected function doErrors() */ public function getIdValue($row) { - if (is_object($row) && isset($row->{$this->primaryKey})) { - // Get the raw primary key value of the Entity. - if ($row instanceof Entity) { + if (is_object($row)) { + // Get the raw or mapped primary key value of the Entity. + if ($row instanceof Entity && $row->{$this->primaryKey} !== null) { $cast = $row->cast(); // Disable Entity casting, because raw primary key value is needed for database. @@ -608,7 +608,9 @@ public function getIdValue($row) return $primaryKey; } - return $row->{$this->primaryKey}; + if (! $row instanceof Entity && isset($row->{$this->primaryKey})) { + return $row->{$this->primaryKey}; + } } if (is_array($row) && isset($row[$this->primaryKey])) { @@ -623,10 +625,6 @@ public function getIdValue($row) * Works with $this->builder to get the Compiled select to * determine the rows to operate on. * This method works only with dbCalls. - * - * @return void - * - * @throws DataException */ public function chunk(int $size, Closure $userFunc) { @@ -692,7 +690,7 @@ public function builder(?string $table = null) // Check for an existing Builder if ($this->builder instanceof BaseBuilder) { // Make sure the requested table matches the builder - if ($table && $this->builder->getTable() !== $table) { + if ((string) $table !== '' && $this->builder->getTable() !== $table) { return $this->db->table($table); } @@ -706,7 +704,7 @@ public function builder(?string $table = null) throw ModelException::forNoPrimaryKey(static::class); } - $table = ($table === null || $table === '') ? $this->table : $table; + $table = ((string) $table === '') ? $this->table : $table; // Ensure we have a good db connection if (! $this->db instanceof BaseConnection) { diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index af0e9acc..3bdc8538 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -288,7 +288,7 @@ public function getPageURI(?int $page = null, string $group = 'default', bool $r $uri->setQueryArray($query); } - return ($returnObject === true) + return $returnObject ? $uri : URI::createURIString( $uri->getScheme(), diff --git a/system/RESTful/BaseResource.php b/system/RESTful/BaseResource.php index 0360bdb1..f2e171c8 100644 --- a/system/RESTful/BaseResource.php +++ b/system/RESTful/BaseResource.php @@ -61,7 +61,7 @@ public function initController(RequestInterface $request, ResponseInterface $res */ public function setModel($which = null) { - if ($which) { + if ($which !== null) { $this->model = is_object($which) ? $which : null; $this->modelName = is_object($which) ? null : $which; } diff --git a/system/Router/AutoRouter.php b/system/Router/AutoRouter.php index 6e9f3248..ce40f5cd 100644 --- a/system/Router/AutoRouter.php +++ b/system/Router/AutoRouter.php @@ -105,7 +105,7 @@ public function getRoute(string $uri, string $httpVerb): array if ($httpVerb !== 'CLI') { $controller = '\\' . $this->defaultNamespace; - $controller .= $this->directory ? str_replace('/', '\\', $this->directory) : ''; + $controller .= $this->directory !== null ? str_replace('/', '\\', $this->directory) : ''; $controller .= $controllerName; $controller = strtolower($controller); @@ -244,7 +244,7 @@ private function isValidSegment(string $segment): bool */ public function setDirectory(?string $dir = null, bool $append = false, bool $validate = true) { - if ($dir === null || $dir === '') { + if ((string) $dir === '') { $this->directory = null; return; @@ -260,7 +260,7 @@ public function setDirectory(?string $dir = null, bool $append = false, bool $va } } - if ($append !== true || ($this->directory === null || $this->directory === '')) { + if (! $append || ((string) $this->directory === '')) { $this->directory = trim($dir, '/') . '/'; } else { $this->directory .= trim($dir, '/') . '/'; @@ -275,7 +275,7 @@ public function setDirectory(?string $dir = null, bool $append = false, bool $va */ public function directory(): string { - return ($this->directory !== null && $this->directory !== '') ? $this->directory : ''; + return ((string) $this->directory !== '') ? $this->directory : ''; } private function controllerName(): string diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 8fba39dc..13cbba44 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -21,7 +21,6 @@ use Config\App; use Config\Modules; use Config\Routing; -use Config\Services; use InvalidArgumentException; /** @@ -283,7 +282,7 @@ public function __construct(FileLocatorInterface $locator, Modules $moduleConfig $this->fileLocator = $locator; $this->moduleConfig = $moduleConfig; - $this->httpHost = Services::request()->getServer('HTTP_HOST'); + $this->httpHost = service('request')->getServer('HTTP_HOST'); // Setup based on config file. Let routes file override. $this->defaultNamespace = rtrim($routing->defaultNamespace, '\\') . '\\'; @@ -561,7 +560,7 @@ public function shouldAutoRoute(): bool */ public function getRoutes(?string $verb = null, bool $includeWildcard = true): array { - if ($verb === null || $verb === '') { + if ((string) $verb === '') { $verb = $this->getHTTPVerb(); } @@ -610,7 +609,7 @@ public function getRoutesOptions(?string $from = null, ?string $verb = null): ar { $options = $this->loadRoutesOptions($verb); - return $from ? $options[$from] ?? [] : $options; + return ((string) $from !== '') ? $options[$from] ?? [] : $options; } /** @@ -780,7 +779,7 @@ public function group(string $name, ...$params) $callback = array_pop($params); - if ($params && is_array($params[0])) { + if ($params !== [] && is_array($params[0])) { $options = array_shift($params); if (isset($options['filter'])) { @@ -1159,6 +1158,8 @@ public function view(string $from, string $view, ?array $options = null): RouteC /** * Limits the routes to a specified ENVIRONMENT or they won't run. + * + * @param Closure(RouteCollection): void $callback */ public function environment(string $env, Closure $callback): RouteCollectionInterface { @@ -1328,7 +1329,7 @@ protected function fillRouteParams(string $from, ?array $params = null): string $patterns = $matches[0]; foreach ($patterns as $index => $pattern) { - if (! preg_match('#^' . $pattern . '$#u', $params[$index])) { + if (preg_match('#^' . $pattern . '$#u', $params[$index]) !== 1) { throw RouterException::forInvalidParameterType(); } @@ -1390,7 +1391,7 @@ protected function buildReverseRoute(string $from, array $params): string // or maybe $placeholder is not a placeholder, but a regex. $pattern = $this->placeholders[$placeholderName] ?? $placeholder; - if (! preg_match('#^' . $pattern . '$#u', (string) $params[$index])) { + if (preg_match('#^' . $pattern . '$#u', (string) $params[$index]) !== 1) { throw RouterException::forInvalidParameterType(); } @@ -1415,14 +1416,14 @@ private function replaceLocale(string $route, ?string $locale = null): string } // Check invalid locale - if ($locale !== null) { + if ((string) $locale !== '') { $config = config(App::class); if (! in_array($locale, $config->supportedLocales, true)) { $locale = null; } } - if ($locale === null) { + if ((string) $locale === '') { $locale = service('request')->getLocale(); } @@ -1604,7 +1605,7 @@ private function processArrayCallableSyntax(string $from, array $to): string private function getMethodParams(string $from): string { preg_match_all('/\(.+?\)/', $from, $matches); - $count = is_countable($matches[0]) ? count($matches[0]) : 0; + $count = count($matches[0]); $params = ''; diff --git a/system/Router/Router.php b/system/Router/Router.php index d60313f6..0cd16149 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -247,7 +247,7 @@ public function getFilters(): array */ public function controllerName() { - return $this->translateURIDashes + return $this->translateURIDashes && ! $this->controller instanceof Closure ? str_replace('-', '_', $this->controller) : $this->controller; } @@ -433,7 +433,7 @@ protected function checkRoutes(string $uri): bool // Is this route supposed to redirect to another? if ($this->collection->isRedirect($routeKey)) { // replacing matched route groups with references: post/([0-9]+) -> post/$1 - $redirectTo = preg_replace_callback('/(\([^\(]+\))/', static function () { + $redirectTo = preg_replace_callback('/(\([^\(]+\))/', static function (): string { static $i = 1; return '$' . $i++; diff --git a/system/Security/Security.php b/system/Security/Security.php index 0b51062e..b6d26602 100644 --- a/system/Security/Security.php +++ b/system/Security/Security.php @@ -377,7 +377,7 @@ protected function derandomize(string $token): string return bin2hex(hex2bin($value) ^ hex2bin($key)); } catch (ErrorException $e) { // "hex2bin(): Hexadecimal input string must have an even length" - throw new InvalidArgumentException($e->getMessage()); + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } } @@ -422,7 +422,7 @@ public function shouldRedirect(): bool * * If it is acceptable for the user input to include relative paths, * e.g. file/in/some/approved/folder.txt, you can set the second optional - * parameter, $relative_path to TRUE. + * parameter, $relativePath to TRUE. * * @param string $str Input file name * @param bool $relativePath Whether to preserve paths diff --git a/system/Security/SecurityInterface.php b/system/Security/SecurityInterface.php index 1460cf0e..03a5ba23 100644 --- a/system/Security/SecurityInterface.php +++ b/system/Security/SecurityInterface.php @@ -64,7 +64,7 @@ public function shouldRedirect(): bool; * * If it is acceptable for the user input to include relative paths, * e.g. file/in/some/approved/folder.txt, you can set the second optional - * parameter, $relative_path to TRUE. + * parameter, $relativePath to TRUE. * * @param string $str Input file name * @param bool $relativePath Whether to preserve paths diff --git a/system/Session/Handlers/Database/PostgreHandler.php b/system/Session/Handlers/Database/PostgreHandler.php index b92ab1e5..e999f356 100644 --- a/system/Session/Handlers/Database/PostgreHandler.php +++ b/system/Session/Handlers/Database/PostgreHandler.php @@ -75,7 +75,7 @@ public function gc($max_lifetime) protected function lockSession(string $sessionID): bool { $arg = "hashtext('{$sessionID}')" . ($this->matchIP ? ", hashtext('{$this->ipAddress}')" : ''); - if ($this->db->simpleQuery("SELECT pg_advisory_lock({$arg})")) { + if ($this->db->simpleQuery("SELECT pg_advisory_lock({$arg})") !== false) { $this->lock = $arg; return true; @@ -93,7 +93,7 @@ protected function releaseLock(): bool return true; } - if ($this->db->simpleQuery("SELECT pg_advisory_unlock({$this->lock})")) { + if ($this->db->simpleQuery("SELECT pg_advisory_unlock({$this->lock})") !== false) { $this->lock = false; return true; diff --git a/system/Session/Handlers/DatabaseHandler.php b/system/Session/Handlers/DatabaseHandler.php index 2d1d2fbb..d36e9592 100644 --- a/system/Session/Handlers/DatabaseHandler.php +++ b/system/Session/Handlers/DatabaseHandler.php @@ -282,12 +282,9 @@ public function destroy($id): bool #[ReturnTypeWillChange] public function gc($max_lifetime) { - $separator = ' '; - $interval = implode($separator, ['', "{$max_lifetime} second", '']); - return $this->db->table($this->table)->where( 'timestamp <', - "now() - INTERVAL {$interval}", + "now() - INTERVAL {$max_lifetime} second", false )->delete() ? 1 : $this->fail(); } diff --git a/system/Session/Handlers/FileHandler.php b/system/Session/Handlers/FileHandler.php index 4dbec779..6931ab1c 100644 --- a/system/Session/Handlers/FileHandler.php +++ b/system/Session/Handlers/FileHandler.php @@ -290,7 +290,7 @@ public function gc($max_lifetime) while (($file = readdir($directory)) !== false) { // If the filename doesn't match this pattern, it's either not a session file or is not ours - if (! preg_match($pattern, $file) + if (preg_match($pattern, $file) !== 1 || ! is_file($this->savePath . DIRECTORY_SEPARATOR . $file) || ($mtime = filemtime($this->savePath . DIRECTORY_SEPARATOR . $file)) === false || $mtime > $ts diff --git a/system/Session/Handlers/MemcachedHandler.php b/system/Session/Handlers/MemcachedHandler.php index 79bb4ca4..e651808a 100644 --- a/system/Session/Handlers/MemcachedHandler.php +++ b/system/Session/Handlers/MemcachedHandler.php @@ -93,12 +93,12 @@ public function open($path, $name): bool } if ( - ! preg_match_all( + preg_match_all( '#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->savePath, $matches, PREG_SET_ORDER - ) + ) === 0 ) { $this->memcached = null; $this->logger->error('Session: Invalid Memcached save path format: ' . $this->savePath); @@ -267,7 +267,7 @@ protected function lockSession(string $sessionID): bool $attempt = 0; do { - if ($this->memcached->get($lockKey)) { + if ($this->memcached->get($lockKey) !== false) { sleep(1); continue; diff --git a/system/Session/Session.php b/system/Session/Session.php index 0aabcbe3..a9b7bd4f 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -234,7 +234,7 @@ public function start() // Sanitize the cookie, because apparently PHP doesn't do that for userspace handlers if (isset($_COOKIE[$this->config->cookieName]) - && (! is_string($_COOKIE[$this->config->cookieName]) || ! preg_match('#\A' . $this->sidRegexp . '\z#', $_COOKIE[$this->config->cookieName])) + && (! is_string($_COOKIE[$this->config->cookieName]) || preg_match('#\A' . $this->sidRegexp . '\z#', $_COOKIE[$this->config->cookieName]) !== 1) ) { unset($_COOKIE[$this->config->cookieName]); } @@ -248,7 +248,7 @@ public function start() if (! isset($_SESSION['__ci_last_regenerate'])) { $_SESSION['__ci_last_regenerate'] = Time::now()->getTimestamp(); } elseif ($_SESSION['__ci_last_regenerate'] < (Time::now()->getTimestamp() - $regenerateTime)) { - $this->regenerate((bool) $this->config->regenerateDestroy); + $this->regenerate($this->config->regenerateDestroy); } } // Another work-around ... PHP doesn't seem to send the session cookie @@ -258,7 +258,7 @@ public function start() } $this->initVars(); - $this->logger->info("Session: Class initialized using '" . $this->config->driver . "' driver."); + $this->logger->debug("Session: Class initialized using '" . $this->config->driver . "' driver."); return $this; } diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index 62c0fff8..467e3a61 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -238,7 +238,7 @@ protected function setUp(): void { parent::setUp(); - if (! $this->app) { + if (! $this->app instanceof CodeIgniter) { $this->app = $this->createApplication(); } @@ -374,7 +374,7 @@ public function assertLogContains(string $level, string $logMessage, string $mes { $this->assertTrue( TestLogger::didLog($level, $logMessage, false), - $message ?: sprintf( + $message !== '' ? $message : sprintf( 'Failed asserting that logs have a record of message containing "%s" with level "%s".', $logMessage, $level diff --git a/system/Test/ControllerTestTrait.php b/system/Test/ControllerTestTrait.php index ab1a1aef..5a5bc708 100644 --- a/system/Test/ControllerTestTrait.php +++ b/system/Test/ControllerTestTrait.php @@ -31,7 +31,7 @@ * * $this->withRequest($request) * ->withResponse($response) - * ->withURI($uri) + * ->withUri($uri) * ->withBody($body) * ->controller('App\Controllers\Home') * ->execute('methodName'); @@ -109,14 +109,14 @@ protected function setUpControllerTestTrait(): void $tempUri = service('uri'); Services::injectMock('uri', $this->uri); - $this->withRequest(Services::incomingrequest($this->appConfig, false)); + $this->withRequest(service('incomingrequest', $this->appConfig, false)); // Restore the URI service Services::injectMock('uri', $tempUri); } if (empty($this->response)) { - $this->response = Services::response($this->appConfig, false); + $this->response = service('response', $this->appConfig, false); } if (empty($this->logger)) { @@ -285,7 +285,7 @@ public function withUri(string $uri) Services::injectMock('uri', $this->uri); // Update the Request instance, because Request has the SiteURI instance. - $this->request = Services::incomingrequest(null, false); + $this->request = service('incomingrequest', null, false); Services::injectMock('request', $this->request); return $this; diff --git a/system/Test/FeatureTestTrait.php b/system/Test/FeatureTestTrait.php index 0f2467ff..13522d87 100644 --- a/system/Test/FeatureTestTrait.php +++ b/system/Test/FeatureTestTrait.php @@ -179,8 +179,8 @@ public function call(string $method, string $path, ?array $params = null) $method = strtoupper($method); // Simulate having a blank session - $_SESSION = []; - $_SERVER['REQUEST_METHOD'] = $method; + $_SESSION = []; + service('superglobals')->setServer('REQUEST_METHOD', $method); $request = $this->setupRequest($method, $path); $request = $this->setupHeaders($request); @@ -189,7 +189,9 @@ public function call(string $method, string $path, ?array $params = null) $request = $this->setRequestBody($request, $params); // Initialize the RouteCollection - if (! $routes = $this->routes) { + $routes = $this->routes; + + if ($routes !== []) { $routes = service('routes')->loadRoutes(); } @@ -200,10 +202,10 @@ public function call(string $method, string $path, ?array $params = null) Services::injectMock('request', $request); // Make sure filters are reset between tests - Services::injectMock('filters', Services::filters(null, false)); + Services::injectMock('filters', service('filters', null, false)); // Make sure validation is reset between tests - Services::injectMock('validation', Services::validation(null, false)); + Services::injectMock('validation', service('validation', null, false)); $response = $this->app ->setContext('web') @@ -321,7 +323,7 @@ protected function setupRequest(string $method, ?string $path = null): IncomingR Services::injectMock('uri', $uri); - $request = Services::incomingrequest($config, false); + $request = service('incomingrequest', $config, false); $request->setMethod($method); $request->setProtocolVersion('1.1'); diff --git a/system/Test/FilterTestTrait.php b/system/Test/FilterTestTrait.php index 93f273a3..d004ec22 100644 --- a/system/Test/FilterTestTrait.php +++ b/system/Test/FilterTestTrait.php @@ -119,6 +119,8 @@ protected function setUpFilterTestTrait(): void * * @param FilterInterface|string $filter The filter instance, class, or alias * @param string $position "before" or "after" + * + * @phpstan-return Closure(list|null=): mixed */ protected function getFilterCaller($filter, string $position): Closure { @@ -219,7 +221,9 @@ protected function getFiltersForRoute(string $route, string $position): array $this->filters->reset(); - if ($routeFilters = $this->collection->getFiltersForRoute($route)) { + $routeFilters = $this->collection->getFiltersForRoute($route); + + if ($routeFilters !== []) { $this->filters->enableFilters($routeFilters, $position); } diff --git a/system/Test/ReflectionHelper.php b/system/Test/ReflectionHelper.php index c72276d1..a1b7ee99 100644 --- a/system/Test/ReflectionHelper.php +++ b/system/Test/ReflectionHelper.php @@ -31,15 +31,15 @@ trait ReflectionHelper * @param object|string $obj object or class name * @param string $method method name * - * @return Closure + * @return Closure + * @phpstan-return Closure(mixed ...$args): mixed * * @throws ReflectionException */ public static function getPrivateMethodInvoker($obj, $method) { $refMethod = new ReflectionMethod($obj, $method); - $refMethod->setAccessible(true); - $obj = (gettype($obj) === 'object') ? $obj : null; + $obj = (gettype($obj) === 'object') ? $obj : null; return static fn (...$args) => $refMethod->invokeArgs($obj, $args); } @@ -58,10 +58,7 @@ private static function getAccessibleRefProperty($obj, $property) { $refClass = is_object($obj) ? new ReflectionObject($obj) : new ReflectionClass($obj); - $refProperty = $refClass->getProperty($property); - $refProperty->setAccessible(true); - - return $refProperty; + return $refClass->getProperty($property); } /** @@ -73,7 +70,7 @@ private static function getAccessibleRefProperty($obj, $property) * * @throws ReflectionException */ - public static function setPrivateProperty($obj, $property, $value) + public static function setPrivateProperty($obj, $property, $value): void { $refProperty = self::getAccessibleRefProperty($obj, $property); @@ -90,7 +87,7 @@ public static function setPrivateProperty($obj, $property, $value) * @param object|string $obj object or class name * @param string $property property name * - * @return mixed value + * @return mixed * * @throws ReflectionException */ diff --git a/system/Test/bootstrap.php b/system/Test/bootstrap.php index 4a068a4c..d2b47aaf 100644 --- a/system/Test/bootstrap.php +++ b/system/Test/bootstrap.php @@ -13,7 +13,6 @@ use CodeIgniter\Boot; use Config\Paths; -use Config\Services; error_reporting(E_ALL); ini_set('display_errors', '1'); @@ -88,4 +87,4 @@ * --------------------------------------------------------------- */ -Services::routes()->loadRoutes(); +service('routes')->loadRoutes(); diff --git a/system/ThirdParty/PSR/Log/LoggerInterface.php b/system/ThirdParty/PSR/Log/LoggerInterface.php index 8afabc90..cb4cf648 100644 --- a/system/ThirdParty/PSR/Log/LoggerInterface.php +++ b/system/ThirdParty/PSR/Log/LoggerInterface.php @@ -89,6 +89,7 @@ public function debug(string|\Stringable $message, array $context = []): void; /** * Logs with an arbitrary level. * + * @param mixed $level * @param mixed[] $context * * @throws \Psr\Log\InvalidArgumentException diff --git a/system/Traits/ConditionalTrait.php b/system/Traits/ConditionalTrait.php index c927a681..a778e937 100644 --- a/system/Traits/ConditionalTrait.php +++ b/system/Traits/ConditionalTrait.php @@ -29,9 +29,9 @@ trait ConditionalTrait */ public function when($condition, callable $callback, ?callable $defaultCallback = null): self { - if ($condition) { + if ($condition !== '' && $condition !== false && $condition !== null) { $callback($this, $condition); - } elseif ($defaultCallback) { + } elseif ($defaultCallback !== null) { $defaultCallback($this); } @@ -52,9 +52,9 @@ public function when($condition, callable $callback, ?callable $defaultCallback */ public function whenNot($condition, callable $callback, ?callable $defaultCallback = null): self { - if (! $condition) { + if ($condition === '' || $condition === null || $condition === false || $condition === '0') { $callback($this, $condition); - } elseif ($defaultCallback) { + } elseif ($defaultCallback !== null) { $defaultCallback($this); } diff --git a/system/Traits/PropertiesTrait.php b/system/Traits/PropertiesTrait.php index ed2da42e..30b3f866 100644 --- a/system/Traits/PropertiesTrait.php +++ b/system/Traits/PropertiesTrait.php @@ -71,7 +71,6 @@ final public function getNonPublicProperties(): array continue; } - $property->setAccessible(true); $properties[] = $property; } diff --git a/system/Typography/Typography.php b/system/Typography/Typography.php index 89687d72..145ff68f 100644 --- a/system/Typography/Typography.php +++ b/system/Typography/Typography.php @@ -171,7 +171,7 @@ public function autoTypography(string $str, bool $reduceLinebreaks = false): str } // No opening block level tag? Add it if needed. - if (! preg_match('/^\s*<(?:' . $this->blockElements . ')/i', $str)) { + if (preg_match('/^\s*<(?:' . $this->blockElements . ')/i', $str) !== 1) { $str = preg_replace('/^(.*?)<(' . $this->blockElements . ')/i', '

$1

<$2', $str); } @@ -214,7 +214,7 @@ public function autoTypography(string $str, bool $reduceLinebreaks = false): str ]; // Do we need to reduce empty lines? - if ($reduceLinebreaks === true) { + if ($reduceLinebreaks) { $table['#

\n*

#'] = ''; } else { // If we have empty paragraph tags we add a non-breaking space diff --git a/system/Validation/DotArrayFilter.php b/system/Validation/DotArrayFilter.php index 4d2e1cb1..c7a401e1 100644 --- a/system/Validation/DotArrayFilter.php +++ b/system/Validation/DotArrayFilter.php @@ -21,8 +21,6 @@ final class DotArrayFilter /** * Creates a new array with only the elements specified in dot array syntax. * - * This code comes from the dot_array_search() function. - * * @param array $indexes The dot array syntax pattern to use for filtering. * @param array $array The array to filter. * @@ -33,20 +31,14 @@ public static function run(array $indexes, array $array): array $result = []; foreach ($indexes as $index) { - // See https://regex101.com/r/44Ipql/1 - $segments = preg_split( - '/(? str_replace('\.', '.', $key), - $segments - ); - - $result = array_replace_recursive($result, self::filter($segments, $array)); + $segments = preg_split('/(? str_replace('\.', '.', $key), $segments); + + $filteredArray = self::filter($segments, $array); + + if ($filteredArray !== []) { + $result = array_replace_recursive($result, $filteredArray); + } } return $result; @@ -62,53 +54,54 @@ public static function run(array $indexes, array $array): array */ private static function filter(array $indexes, array $array): array { - // If index is empty, returns empty array. + // If there are no indexes left, return an empty array if ($indexes === []) { return []; } - // Grab the current index. + // Get the current index $currentIndex = array_shift($indexes); + // If the current index doesn't exist and is not a wildcard, return an empty array if (! isset($array[$currentIndex]) && $currentIndex !== '*') { return []; } - // Handle Wildcard (*) + // Handle the wildcard '*' at the current level if ($currentIndex === '*') { - $answer = []; + $result = []; + // Iterate over all keys at this level foreach ($array as $key => $value) { - if (! is_array($value)) { - continue; - } - - $result = self::filter($indexes, $value); - - if ($result !== []) { - $answer[$key] = $result; + if ($indexes === []) { + // If no indexes are left, capture the entire value + $result[$key] = $value; + } elseif (is_array($value)) { + // If there are still indexes left, continue filtering recursively + $filtered = self::filter($indexes, $value); + if ($filtered !== []) { + $result[$key] = $filtered; + } } } - return $answer; + return $result; } - // If this is the last index, make sure to return it now, - // and not try to recurse through things. + // If this is the last index, return the value if ($indexes === []) { - return [$currentIndex => $array[$currentIndex]]; + return [$currentIndex => $array[$currentIndex] ?? []]; } - // Do we need to recursively filter this value? - if (is_array($array[$currentIndex]) && $array[$currentIndex] !== []) { - $result = self::filter($indexes, $array[$currentIndex]); + // If the current value is an array, recursively filter it + if (is_array($array[$currentIndex])) { + $filtered = self::filter($indexes, $array[$currentIndex]); - if ($result !== []) { - return [$currentIndex => $result]; + if ($filtered !== []) { + return [$currentIndex => $filtered]; } } - // Otherwise, not found. return []; } } diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index dd8c4290..058b1573 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -38,7 +38,7 @@ class FileRules */ public function __construct(?RequestInterface $request = null) { - if ($request === null) { + if (! $request instanceof RequestInterface) { $request = service('request'); } @@ -52,7 +52,9 @@ public function __construct(?RequestInterface $request = null) */ public function uploaded(?string $blank, string $name): bool { - if (! ($files = $this->request->getFileMultiple($name))) { + $files = $this->request->getFileMultiple($name); + + if ($files === [] || $files === null) { $files = [$this->request->getFile($name)]; } @@ -86,12 +88,15 @@ public function max_size(?string $blank, string $params): bool // Grab the file name off the top of the $params // after we split it. $paramArray = explode(',', $params); + if (count($paramArray) !== 2) { throw new InvalidArgumentException('Invalid max_size parameter: "' . $params . '"'); } - $name = array_shift($paramArray); - if (! ($files = $this->request->getFileMultiple($name))) { + $name = array_shift($paramArray); + $files = $this->request->getFileMultiple($name); + + if ($files === [] || $files === null) { $files = [$this->request->getFile($name)]; } @@ -126,8 +131,9 @@ public function is_image(?string $blank, string $params): bool // after we split it. $params = explode(',', $params); $name = array_shift($params); + $files = $this->request->getFileMultiple($name); - if (! ($files = $this->request->getFileMultiple($name))) { + if ($files === [] || $files === null) { $files = [$this->request->getFile($name)]; } @@ -161,8 +167,9 @@ public function mime_in(?string $blank, string $params): bool // after we split it. $params = explode(',', $params); $name = array_shift($params); + $files = $this->request->getFileMultiple($name); - if (! ($files = $this->request->getFileMultiple($name))) { + if ($files === [] || $files === null) { $files = [$this->request->getFile($name)]; } @@ -192,8 +199,9 @@ public function ext_in(?string $blank, string $params): bool // after we split it. $params = explode(',', $params); $name = array_shift($params); + $files = $this->request->getFileMultiple($name); - if (! ($files = $this->request->getFileMultiple($name))) { + if ($files === [] || $files === null) { $files = [$this->request->getFile($name)]; } @@ -224,8 +232,9 @@ public function max_dims(?string $blank, string $params): bool // after we split it. $params = explode(',', $params); $name = array_shift($params); + $files = $this->request->getFileMultiple($name); - if (! ($files = $this->request->getFileMultiple($name))) { + if ($files === [] || $files === null) { $files = [$this->request->getFile($name)]; } diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 04e8a054..7f2f0bd9 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -140,7 +140,7 @@ public function is_not_unique($str, string $field, array $data): bool if ( $whereField !== null && $whereField !== '' && $whereValue !== null && $whereValue !== '' - && ! preg_match('/^\{(\w+)\}$/', $whereValue) + && preg_match('/^\{(\w+)\}$/', $whereValue) !== 1 ) { $row = $row->where($whereField, $whereValue); } @@ -198,7 +198,7 @@ public function is_unique($str, string $field, array $data): bool if ( $ignoreField !== null && $ignoreField !== '' && $ignoreValue !== null && $ignoreValue !== '' - && ! preg_match('/^\{(\w+)\}$/', $ignoreValue) + && preg_match('/^\{(\w+)\}$/', $ignoreValue) !== 1 ) { $row = $row->where("{$ignoreField} !=", $ignoreValue); } @@ -359,10 +359,8 @@ public function required_with($str = null, ?string $fields = null, array $data = foreach (explode(',', $fields) as $field) { if ( - (array_key_exists($field, $data) - && ! empty($data[$field])) // @phpstan-ignore-line Use empty() - || (str_contains($field, '.') - && ! empty(dot_array_search($field, $data))) // @phpstan-ignore-line Use empty() + (array_key_exists($field, $data) && ! empty($data[$field])) + || (str_contains($field, '.') && ! empty(dot_array_search($field, $data))) ) { $requiredFields[] = $field; } @@ -409,7 +407,7 @@ public function required_without( if ( (! str_contains($otherField, '.')) && (! array_key_exists($otherField, $data) - || empty($data[$otherField])) // @phpstan-ignore-line Use empty() + || empty($data[$otherField])) ) { return false; } @@ -424,7 +422,7 @@ public function required_without( $fieldKey = $fieldSplitArray[1]; if (is_array($fieldData)) { - return ! empty(dot_array_search($otherField, $data)[$fieldKey]); // @phpstan-ignore-line Use empty() + return ! empty(dot_array_search($otherField, $data)[$fieldKey]); } $nowField = str_replace('*', $fieldKey, $otherField); $nowFieldVaule = dot_array_search($nowField, $data); diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index ec02a4e0..c9b98c47 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -164,7 +164,7 @@ public function is_not_unique($str, string $field, array $data): bool if ( $whereField !== null && $whereField !== '' && $whereValue !== null && $whereValue !== '' - && ! preg_match('/^\{(\w+)\}$/', $whereValue) + && preg_match('/^\{(\w+)\}$/', $whereValue) !== 1 ) { $row = $row->where($whereField, $whereValue); } @@ -224,7 +224,7 @@ public function is_unique($str, string $field, array $data): bool if ( $ignoreField !== null && $ignoreField !== '' && $ignoreValue !== null && $ignoreValue !== '' - && ! preg_match('/^\{(\w+)\}$/', $ignoreValue) + && preg_match('/^\{(\w+)\}$/', $ignoreValue) !== 1 ) { $row = $row->where("{$ignoreField} !=", $ignoreValue); } diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index a4d817d2..c00329d6 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -21,7 +21,6 @@ use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\Validation\Exceptions\ValidationException; use CodeIgniter\View\RendererInterface; -use Config\Services; use Config\Validation as ValidationConfig; use InvalidArgumentException; use LogicException; @@ -182,7 +181,7 @@ public function run(?array $data = null, ?string $group = null, $dbGroup = null) ); // if keys not found - $values = $values ?: [$field => null]; + $values = $values !== [] ? $values : [$field => null]; } else { $values = dot_array_search($field, $data); } @@ -456,7 +455,7 @@ private function processPermitEmpty($value, array $rules, array $data) } } - if ($passed === true) { + if ($passed) { return true; } } @@ -468,7 +467,7 @@ private function processPermitEmpty($value, array $rules, array $data) } /** - * @param Closure|string $rule + * @param Closure(bool|float|int|list|object|string|null, bool|float|int|list|object|string|null, string|null, string|null): (bool|string) $rule */ private function isClosure($rule): bool { @@ -796,7 +795,7 @@ protected function fillPlaceholders(array $rules, array $data): array $placeholderFields = $this->retrievePlaceholders($row, $data); foreach ($placeholderFields as $field) { - $validator ??= Services::validation(null, false); + $validator ??= service('validation', null, false); assert($validator instanceof Validation); $placeholderRules = $rules[$field]['rules'] ?? null; @@ -919,26 +918,24 @@ protected function getErrorMessage( ): string { $param ??= ''; + $args = [ + 'field' => ($label === null || $label === '') ? $field : lang($label), + 'param' => (! isset($this->rules[$param]['label'])) ? $param : lang($this->rules[$param]['label']), + 'value' => $value ?? '', + ]; + // Check if custom message has been defined by user if (isset($this->customErrors[$field][$rule])) { - $message = lang($this->customErrors[$field][$rule]); - } elseif (null !== $originalField && isset($this->customErrors[$originalField][$rule])) { - $message = lang($this->customErrors[$originalField][$rule]); - } else { - // Try to grab a localized version of the message... - // lang() will return the rule name back if not found, - // so there will always be a string being returned. - $message = lang('Validation.' . $rule); + return lang($this->customErrors[$field][$rule], $args); + } + if (null !== $originalField && isset($this->customErrors[$originalField][$rule])) { + return lang($this->customErrors[$originalField][$rule], $args); } - $message = str_replace('{field}', ($label === null || $label === '') ? $field : lang($label), $message); - $message = str_replace( - '{param}', - (! isset($this->rules[$param]['label'])) ? $param : lang($this->rules[$param]['label']), - $message - ); - - return str_replace('{value}', $value ?? '', $message); + // Try to grab a localized version of the message... + // lang() will return the rule name back if not found, + // so there will always be a string being returned. + return lang('Validation.' . $rule, $args); } /** diff --git a/system/View/Cell.php b/system/View/Cell.php index 28b249a6..f2801e41 100644 --- a/system/View/Cell.php +++ b/system/View/Cell.php @@ -17,7 +17,6 @@ use CodeIgniter\Config\Factories; use CodeIgniter\View\Cells\Cell as BaseCell; use CodeIgniter\View\Exceptions\ViewException; -use Config\Services; use ReflectionException; use ReflectionMethod; @@ -93,7 +92,7 @@ public function render(string $library, $params = null, int $ttl = 0, ?string $c } if (method_exists($instance, 'initController')) { - $instance->initController(Services::request(), service('response'), service('logger')); + $instance->initController(service('request'), service('response'), service('logger')); } if (! method_exists($instance, $method)) { diff --git a/system/View/Filters.php b/system/View/Filters.php index dde9ec6d..0be48b6d 100644 --- a/system/View/Filters.php +++ b/system/View/Filters.php @@ -71,9 +71,7 @@ public static function date_modify($value, string $adjustment) */ public static function default($value, string $default): string { - return empty($value) // @phpstan-ignore-line - ? $default - : $value; + return empty($value) ? $default : $value; } /** diff --git a/system/View/Parser.php b/system/View/Parser.php index bbeb00f7..08fec93e 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -254,20 +254,39 @@ protected function parse(string $template, array $data = [], ?array $options = n // it can potentially modify any template between its tags. $template = $this->parsePlugins($template); - // loop over the data variables, replacing - // the content as we go. + // Parse stack for each parse type (Single and Pairs) + $replaceSingleStack = []; + $replacePairsStack = []; + + // loop over the data variables, saving regex and data + // for later replacement. foreach ($data as $key => $val) { $escape = true; if (is_array($val)) { - $escape = false; - $replace = $this->parsePair($key, $val, $template); + $escape = false; + $replacePairsStack[] = [ + 'replace' => $this->parsePair($key, $val, $template), + 'escape' => $escape, + ]; } else { - $replace = $this->parseSingle($key, (string) $val); + $replaceSingleStack[] = [ + 'replace' => $this->parseSingle($key, (string) $val), + 'escape' => $escape, + ]; } + } + + // Merge both stacks, pairs first + single stacks + // This allows for nested data with the same key to be replaced properly + $replace = array_merge($replacePairsStack, $replaceSingleStack); - foreach ($replace as $pattern => $content) { - $template = $this->replaceSingle($pattern, $content, $template, $escape); + // Loop over each replace array item which + // holds all the data to be replaced + foreach ($replace as $replaceItem) { + // Loop over the actual data to be replaced + foreach ($replaceItem['replace'] as $pattern => $content) { + $template = $this->replaceSingle($pattern, $content, $template, $replaceItem['escape']); } } @@ -534,7 +553,7 @@ protected function replaceSingle($pattern, $content, $template, bool $escape = f $content = (string) $content; // Replace the content in the template - return preg_replace_callback($pattern, function ($matches) use ($content, $escape) { + return preg_replace_callback($pattern, function ($matches) use ($content, $escape): string { // Check for {! !} syntax to not escape this one. if ( str_starts_with($matches[0], $this->leftDelimiter . '!') @@ -594,7 +613,7 @@ public function shouldAddEscaping(string $key) $escape = false; } // If no `esc` filter is found, then we'll need to add one. - elseif (! preg_match('/\s+esc/u', $key)) { + elseif (preg_match('/\s+esc/u', $key) !== 1) { $escape = 'html'; } @@ -672,7 +691,7 @@ protected function parsePlugins(string $template) * $matches[1] = all parameters string in opening tag * $matches[2] = content between the tags to send to the plugin. */ - if (! preg_match_all($pattern, $template, $matches, PREG_SET_ORDER)) { + if (preg_match_all($pattern, $template, $matches, PREG_SET_ORDER) === 0) { continue; } diff --git a/system/View/Table.php b/system/View/Table.php index 60ccc509..9eb29578 100644 --- a/system/View/Table.php +++ b/system/View/Table.php @@ -333,7 +333,7 @@ public function generate($tableData = null) $out = $this->template['table_open'] . $this->newline; // Add any caption here - if ($this->caption) { + if (isset($this->caption) && $this->caption !== '') { $out .= '
' . $this->newline; } diff --git a/tests/session/ExampleSessionTest.php b/tests/session/ExampleSessionTest.php index 6ada0c56..33242a47 100644 --- a/tests/session/ExampleSessionTest.php +++ b/tests/session/ExampleSessionTest.php @@ -1,7 +1,6 @@ set('logged_in', 123); $this->assertSame(123, $session->get('logged_in')); diff --git a/tests/unit/HealthTest.php b/tests/unit/HealthTest.php index 25f229b0..f6a5a804 100644 --- a/tests/unit/HealthTest.php +++ b/tests/unit/HealthTest.php @@ -2,7 +2,6 @@ use CodeIgniter\Test\CIUnitTestCase; use Config\App; -use Config\Services; use Tests\Support\Libraries\ConfigReader; /** @@ -17,7 +16,7 @@ public function testIsDefinedAppPath(): void public function testBaseUrlHasBeenSet(): void { - $validation = Services::validation(); + $validation = service('validation'); $env = false;
' . $this->caption . '