From dbc07dc8cb260b5abe5e15d3ed316941183e7385 Mon Sep 17 00:00:00 2001 From: paulbalandan Date: Sat, 26 Jul 2025 15:39:12 +0000 Subject: [PATCH 1/3] Release v4.6.2 --- .gitignore | 2 +- app/Config/Cache.php | 6 +- app/Config/Cookie.php | 2 +- app/Config/Logger.php | 3 +- app/Views/errors/html/error_exception.php | 2 +- composer.json | 4 +- system/API/ResponseTrait.php | 43 +++--- system/Autoloader/Autoloader.php | 49 ++++--- system/Autoloader/FileLocator.php | 25 ++-- system/Autoloader/FileLocatorCached.php | 2 +- system/Autoloader/FileLocatorInterface.php | 8 +- system/BaseModel.php | 63 ++++----- system/Boot.php | 32 ++++- system/CLI/Console.php | 5 +- system/CLI/GeneratorTrait.php | 13 +- system/Cache/CacheFactory.php | 2 +- system/Cache/CacheInterface.php | 20 ++- system/Cache/Exceptions/CacheException.php | 11 +- system/Cache/FactoriesCache.php | 16 +-- .../FactoriesCache/FileVarExportHandler.php | 10 +- system/Cache/Handlers/BaseHandler.php | 9 +- system/Cache/Handlers/FileHandler.php | 75 ++++++++--- system/Cache/Handlers/MemcachedHandler.php | 19 +-- system/Cache/Handlers/PredisHandler.php | 8 +- system/Cache/Handlers/RedisHandler.php | 8 +- system/Cache/ResponseCache.php | 29 ++-- system/CodeIgniter.php | 6 +- system/Commands/Generators/TestGenerator.php | 20 +-- .../Translation/LocalizationFinder.php | 3 +- system/Common.php | 30 ++--- system/Config/AutoloadConfig.php | 2 + system/Config/Services.php | 5 + system/Cookie/Cookie.php | 39 +++++- system/Cookie/CookieInterface.php | 22 ++- system/DataCaster/DataCaster.php | 7 +- system/DataConverter/DataConverter.php | 15 +-- system/Database/BaseBuilder.php | 2 +- system/Database/BaseConnection.php | 49 ++++--- system/Database/BasePreparedQuery.php | 13 +- system/Database/BaseResult.php | 38 +++--- system/Database/ConnectionInterface.php | 18 +-- system/Database/Forge.php | 3 +- system/Database/MySQLi/Builder.php | 2 +- system/Database/MySQLi/Forge.php | 3 +- system/Database/OCI8/Forge.php | 3 +- system/Database/Postgre/Builder.php | 2 +- system/Database/Postgre/Connection.php | 3 +- system/Database/Postgre/Forge.php | 3 +- system/Database/PreparedQueryInterface.php | 3 +- system/Database/ResultInterface.php | 15 +-- system/Database/SQLSRV/Forge.php | 3 +- system/Database/SQLite3/Builder.php | 2 +- system/Database/SQLite3/Forge.php | 4 +- system/Database/SQLite3/Table.php | 3 +- system/Debug/Toolbar/Collectors/Logs.php | 18 ++- system/Email/Email.php | 123 +++++++++-------- system/Events/Events.php | 34 ++--- system/Files/File.php | 4 +- system/Filters/Filters.php | 20 +-- system/Format/Exceptions/FormatException.php | 2 +- system/Format/Format.php | 13 +- system/Format/FormatterInterface.php | 4 +- system/Format/JSONFormatter.php | 8 +- system/Format/XMLFormatter.php | 7 +- system/HTTP/CLIRequest.php | 3 - system/HTTP/CURLRequest.php | 96 ++++++++----- system/HTTP/DownloadResponse.php | 15 +-- system/HTTP/IncomingRequest.php | 3 - system/HTTP/RequestTrait.php | 17 +-- system/HTTP/SiteURI.php | 9 +- system/HTTP/SiteURIFactory.php | 12 +- system/HTTP/URI.php | 2 +- system/Helpers/filesystem_helper.php | 23 +++- system/Helpers/security_helper.php | 64 ++++++++- system/Images/Handlers/ImageMagickHandler.php | 16 +-- system/Log/Handlers/BaseHandler.php | 4 +- system/Log/Handlers/ChromeLoggerHandler.php | 29 ++-- system/Log/Handlers/ErrorlogHandler.php | 6 +- system/Log/Handlers/FileHandler.php | 20 +-- system/Log/Logger.php | 96 +++++-------- system/Model.php | 13 +- system/RESTful/ResourceController.php | 3 +- system/Security/Security.php | 51 +------ system/Security/SecurityInterface.php | 2 + system/Test/Interfaces/FabricatorModel.php | 7 +- system/Test/Mock/MockBuilder.php | 3 + system/Test/Mock/MockCLIConfig.php | 21 +-- system/Test/Mock/MockCURLRequest.php | 11 +- system/Test/Mock/MockCache.php | 5 +- system/Test/Mock/MockConnection.php | 49 ++++--- system/Test/Mock/MockEvents.php | 4 +- system/Test/Mock/MockFileLogger.php | 9 +- system/Test/Mock/MockInputOutput.php | 8 +- system/Test/Mock/MockLogger.php | 126 +++++++++--------- system/Test/Mock/MockResult.php | 14 +- system/Test/Mock/MockServices.php | 7 + system/Test/Mock/MockSession.php | 4 +- system/ThirdParty/Escaper/Escaper.php | 44 +----- .../ThirdParty/Escaper/EscaperInterface.php | 58 ++++++++ system/Traits/ConditionalTrait.php | 20 +-- system/Validation/Validation.php | 2 +- system/View/Cell.php | 7 +- system/View/Filters.php | 4 +- system/View/Parser.php | 17 ++- system/View/RendererInterface.php | 14 +- system/View/Table.php | 3 +- system/View/View.php | 16 +-- system/util_bootstrap.php | 84 ++++++++++++ writable/debugbar/index.html | 11 ++ 109 files changed, 1149 insertions(+), 900 deletions(-) create mode 100644 system/ThirdParty/Escaper/EscaperInterface.php create mode 100644 system/util_bootstrap.php create mode 100644 writable/debugbar/index.html diff --git a/.gitignore b/.gitignore index e24e7ce4..87e86b93 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,7 @@ writable/uploads/* !writable/uploads/index.html writable/debugbar/* -!writable/debugbar/.gitkeep +!writable/debugbar/index.html php_errors.log diff --git a/app/Config/Cache.php b/app/Config/Cache.php index e6efa3ac..1169c95f 100644 --- a/app/Config/Cache.php +++ b/app/Config/Cache.php @@ -78,7 +78,7 @@ class Cache extends BaseConfig * Your file storage preferences can be specified below, if you are using * the File driver. * - * @var array + * @var array{storePath?: string, mode?: int} */ public array $file = [ 'storePath' => WRITEPATH . 'cache/', @@ -95,7 +95,7 @@ class Cache extends BaseConfig * * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached * - * @var array + * @var array{host?: string, port?: int, weight?: int, raw?: bool} */ public array $memcached = [ 'host' => '127.0.0.1', @@ -112,7 +112,7 @@ class Cache extends BaseConfig * Your Redis server can be specified below, if you are using * the Redis or Predis drivers. * - * @var array + * @var array{host?: string, password?: string|null, port?: int, timeout?: int, database?: int} */ public array $redis = [ 'host' => '127.0.0.1', diff --git a/app/Config/Cookie.php b/app/Config/Cookie.php index 84ccc0e9..3bad1847 100644 --- a/app/Config/Cookie.php +++ b/app/Config/Cookie.php @@ -85,7 +85,7 @@ class Cookie extends BaseConfig * (empty string) means default SameSite attribute set by browsers (`Lax`) * will be set on cookies. If set to `None`, `$secure` must also be set. * - * @phpstan-var 'None'|'Lax'|'Strict'|'' + * @var ''|'Lax'|'None'|'Strict' */ public string $samesite = 'Lax'; diff --git a/app/Config/Logger.php b/app/Config/Logger.php index ab6997e5..799dc2c3 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -4,6 +4,7 @@ use CodeIgniter\Config\BaseConfig; use CodeIgniter\Log\Handlers\FileHandler; +use CodeIgniter\Log\Handlers\HandlerInterface; class Logger extends BaseConfig { @@ -73,7 +74,7 @@ class Logger extends BaseConfig * Handlers are executed in the order defined in this array, starting with * the handler on top and continuing down. * - * @var array|string>> + * @var array, array|string>> */ public array $handlers = [ /* diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php index d5e0c2ec..2c4e0091 100644 --- a/app/Views/errors/html/error_exception.php +++ b/app/Views/errors/html/error_exception.php @@ -24,7 +24,7 @@
- Displayed at — + Displayed at — PHP: — CodeIgniter: -- Environment: diff --git a/composer.json b/composer.json index 82d06cd9..499a97ea 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "php": "^8.1", "ext-intl": "*", "ext-mbstring": "*", - "laminas/laminas-escaper": "^2.14", + "laminas/laminas-escaper": "^2.17", "psr/log": "^3.0" }, "require-dev": { @@ -24,7 +24,7 @@ "mikey179/vfsstream": "^1.6.12", "nexusphp/cs-config": "^3.6", "phpunit/phpunit": "^10.5.16 || ^11.2", - "predis/predis": "^1.1 || ^2.3" + "predis/predis": "^3.0" }, "suggest": { "ext-curl": "If you use CURLRequest class", diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 5f2e068b..318af4b2 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -13,8 +13,10 @@ namespace CodeIgniter\API; +use CodeIgniter\Format\Format; use CodeIgniter\Format\FormatterInterface; use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; /** @@ -22,8 +24,10 @@ * consistent HTTP responses under a variety of common * situations when working as an API. * - * @property bool $stringAsHtml Whether to treat string data as HTML in JSON response. - * Setting `true` is only for backward compatibility. + * @property RequestInterface $request + * @property ResponseInterface $response + * @property bool $stringAsHtml Whether to treat string data as HTML in JSON response. + * Setting `true` is only for backward compatibility. */ trait ResponseTrait { @@ -69,8 +73,7 @@ trait ResponseTrait * Either 'json' or 'xml'. If null is set, it will be determined through * content negotiation. * - * @var string|null - * @phpstan-var 'html'|'json'|'xml'|null + * @var 'html'|'json'|'xml'|null */ protected $format = 'json'; @@ -85,7 +88,7 @@ trait ResponseTrait * Provides a single, simple method to return an API response, formatted * to match the requested format, with proper content-type and status code. * - * @param array|string|null $data + * @param array|string|null $data * * @return ResponseInterface */ @@ -119,9 +122,9 @@ protected function respond($data = null, ?int $status = null, string $message = /** * Used for generic failures that no custom methods exist for. * - * @param array|string $messages - * @param int $status HTTP status code - * @param string|null $code Custom, API-specific, error code + * @param list|string $messages + * @param int $status HTTP status code + * @param string|null $code Custom, API-specific, error code * * @return ResponseInterface */ @@ -147,7 +150,7 @@ protected function fail($messages, int $status = 400, ?string $code = null, stri /** * Used after successfully creating a new resource. * - * @param array|string|null $data + * @param array|string|null $data * * @return ResponseInterface */ @@ -159,7 +162,7 @@ protected function respondCreated($data = null, string $message = '') /** * Used after a resource has been successfully deleted. * - * @param array|string|null $data + * @param array|string|null $data * * @return ResponseInterface */ @@ -171,7 +174,7 @@ protected function respondDeleted($data = null, string $message = '') /** * Used after a resource has been successfully updated. * - * @param array|string|null $data + * @param array|string|null $data * * @return ResponseInterface */ @@ -288,15 +291,17 @@ protected function failServerError(string $description = 'Internal Server Error' * Handles formatting a response. Currently, makes some heavy assumptions * and needs updating! :) * - * @param array|string|null $data + * @param array|string|null $data * * @return string|null */ protected function format($data = null) { + /** @var Format $format */ $format = service('format'); - $mime = ($this->format === null) ? $format->getConfig()->supportedResponseFormats[0] + $mime = $this->format === null + ? $format->getConfig()->supportedResponseFormats[0] : "application/{$this->format}"; // Determine correct response type through content negotiation if not explicitly declared @@ -314,14 +319,10 @@ protected function format($data = null) $this->response->setContentType($mime); // if we don't have a formatter, make one - if (! isset($this->formatter)) { - // if no formatter, use the default - $this->formatter = $format->getFormatter($mime); - } + $this->formatter ??= $format->getFormatter($mime); $asHtml = $this->stringAsHtml ?? false; - // Returns as HTML. if ( ($mime === 'application/json' && $asHtml && is_string($data)) || ($mime !== 'application/json' && is_string($data)) @@ -339,6 +340,7 @@ protected function format($data = null) if ($mime !== 'application/json') { // Recursively convert objects into associative arrays // Conversion not required for JSONFormatter + /** @var array|string|null $data */ $data = json_decode(json_encode($data), true); } @@ -348,14 +350,13 @@ protected function format($data = null) /** * Sets the format the response should be in. * - * @param string|null $format Response format - * @phpstan-param 'json'|'xml' $format + * @param 'json'|'xml' $format Response format * * @return $this */ protected function setResponseFormat(?string $format = null) { - $this->format = ($format === null) ? null : strtolower($format); + $this->format = $format === null ? null : strtolower($format); return $this; } diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 2e628f15..65c535e8 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -21,7 +21,7 @@ use Config\Autoload; use Config\Kint as KintConfig; use Config\Modules; -use Kint; +use Kint\Kint; use Kint\Renderer\CliRenderer; use Kint\Renderer\RichRenderer; @@ -67,21 +67,21 @@ class Autoloader /** * Stores namespaces as key, and path as values. * - * @var array> + * @var array> */ protected $prefixes = []; /** * Stores class name as key, and path as values. * - * @var array + * @var array */ protected $classmap = []; /** * Stores files as a list. * - * @var list + * @var list */ protected $files = []; @@ -89,7 +89,7 @@ class Autoloader * Stores helper list. * Always load the URL helper, it should be used in most apps. * - * @var list + * @var list */ protected $helpers = ['url']; @@ -147,36 +147,35 @@ private function loadComposerAutoloader(Modules $modules): void // Should we load through Composer's namespaces, also? if ($modules->discoverInComposer) { - // @phpstan-ignore-next-line - $this->loadComposerNamespaces($composer, $modules->composerPackages ?? []); + $composerPackages = $modules->composerPackages; + $this->loadComposerNamespaces($composer, $composerPackages ?? []); } unset($composer); } /** - * Register the loader with the SPL autoloader stack. + * Register the loader with the SPL autoloader stack + * in the following order: + * + * 1. Classmap loader + * 2. PSR-4 autoloader + * 3. Non-class files * * @return void */ public function register() { - // Register classmap loader for the files in our class map. spl_autoload_register($this->loadClassmap(...), true); - - // Register the PSR-4 autoloader. spl_autoload_register($this->loadClass(...), true); - // Load our non-class files foreach ($this->files as $file) { $this->includeFile($file); } } /** - * Unregister autoloader. - * - * This method is for testing. + * Unregisters the autoloader from the SPL autoload stack. */ public function unregister(): void { @@ -187,7 +186,7 @@ public function unregister(): void /** * Registers namespaces with the autoloader. * - * @param array|string>|string $namespace + * @param array|non-empty-string>|non-empty-string $namespace * * @return $this */ @@ -219,8 +218,7 @@ public function addNamespace($namespace, ?string $path = null) * * If a prefix param is set, returns only paths to the given prefix. * - * @return array>|list - * @phpstan-return ($prefix is null ? array> : list) + * @return ($prefix is null ? array> : list) */ public function getNamespace(?string $prefix = null) { @@ -248,6 +246,8 @@ public function removeNamespace(string $namespace) /** * Load a class using available class mapping. * + * @param class-string $class The fully qualified class name. + * * @internal For `spl_autoload_register` use. */ public function loadClassmap(string $class): void @@ -262,9 +262,9 @@ public function loadClassmap(string $class): void /** * Loads the class file for a given class name. * - * @internal For `spl_autoload_register` use. + * @param class-string $class The fully qualified class name. * - * @param string $class The fully qualified class name. + * @internal For `spl_autoload_register` use. */ public function loadClass(string $class): void { @@ -274,9 +274,9 @@ public function loadClass(string $class): void /** * Loads the class file for a given class name. * - * @param string $class The fully-qualified class name + * @param class-string $class The fully qualified class name. * - * @return false|string The mapped file name on success, or boolean false on fail + * @return false|non-empty-string The mapped file name on success, or boolean false on fail */ protected function loadInNamespace(string $class) { @@ -294,21 +294,20 @@ protected function loadInNamespace(string $class) $filePath = $directory . $relativeClassPath . '.php'; $filename = $this->includeFile($filePath); - if ($filename) { + if ($filename !== false) { return $filename; } } } } - // never found a mapped file return false; } /** * A central way to include a file. Split out primarily for testing purposes. * - * @return false|string The filename on success, false if the file is not loaded + * @return false|non-empty-string The filename on success, false if the file is not loaded */ protected function includeFile(string $file) { diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 13ecb817..f67708a4 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -44,7 +44,7 @@ public function __construct(Autoloader $autoloader) * Attempts to locate a file by examining the name for a namespace * and looking through the PSR-4 namespaced files that we know about. * - * @param string $file The relative file path or namespaced file to + * @param non-empty-string $file The relative file path or namespaced file to * locate. If not namespaced, search in the app * folder. * @param non-empty-string|null $folder The folder within the namespace that we should @@ -53,7 +53,7 @@ public function __construct(Autoloader $autoloader) * folder. * @param string $ext The file extension the file should have. * - * @return false|string The path to the file, or false if not found. + * @return false|non-empty-string The path to the file, or false if not found. */ public function locateFile(string $file, ?string $folder = null, string $ext = 'php') { @@ -156,9 +156,11 @@ public function getClassname(string $file): string $dlm = false; } - if (($tokens[$i - 2][0] === T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] === 'phpclass')) + if ( + ($tokens[$i - 2][0] === T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] === 'phpclass')) && $tokens[$i - 1][0] === T_WHITESPACE - && $token[0] === T_STRING) { + && $token[0] === T_STRING + ) { $className = $token[1]; break; } @@ -184,7 +186,7 @@ public function getClassname(string $file): string * 'app/Modules/bar/Config/Routes.php', * ] * - * @return list + * @return list */ public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array { @@ -213,7 +215,6 @@ public function search(string $path, string $ext = 'php', bool $prioritizeApp = $foundPaths = [...$foundPaths, ...$appPaths]; } - // Remove any duplicates return array_values(array_unique($foundPaths)); } @@ -236,13 +237,12 @@ protected function ensureExt(string $path, string $ext): string /** * Return the namespace mappings we know about. * - * @return array> + * @return list */ protected function getNamespaces() { $namespaces = []; - // Save system for last $system = []; foreach ($this->autoloader->getNamespace() as $prefix => $paths) { @@ -263,6 +263,7 @@ protected function getNamespaces() } } + // Save system for last return array_merge($namespaces, $system); } @@ -295,19 +296,17 @@ public function findQualifiedNameFromPath(string $path) ); // Remove the file extension (.php) - /** @var class-string */ + /** @var class-string $className */ $className = mb_substr($className, 0, -4); if (in_array($className, $this->invalidClassnames, true)) { continue; } - // Check if this exists if (class_exists($className)) { return $className; } - // If the class does not exist, it is an invalid classname. $this->invalidClassnames[] = $className; } } @@ -353,11 +352,11 @@ public function listFiles(string $path): array * Scans the provided namespace, returning a list of all files * that are contained within the sub path specified by $path. * - * @return list List of file paths + * @return list List of file paths */ public function listNamespaceFiles(string $prefix, string $path): array { - if ($path === '' || ($prefix === '')) { + if ($path === '' || $prefix === '') { return []; } diff --git a/system/Autoloader/FileLocatorCached.php b/system/Autoloader/FileLocatorCached.php index 8b173994..0aa267b2 100644 --- a/system/Autoloader/FileLocatorCached.php +++ b/system/Autoloader/FileLocatorCached.php @@ -117,7 +117,7 @@ public function getClassname(string $file): string } /** - * @return list + * @return list */ public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array { diff --git a/system/Autoloader/FileLocatorInterface.php b/system/Autoloader/FileLocatorInterface.php index 8f6129ab..0422ec25 100644 --- a/system/Autoloader/FileLocatorInterface.php +++ b/system/Autoloader/FileLocatorInterface.php @@ -23,7 +23,7 @@ interface FileLocatorInterface * Attempts to locate a file by examining the name for a namespace * and looking through the PSR-4 namespaced files that we know about. * - * @param string $file The relative file path or namespaced file to + * @param non-empty-string $file The relative file path or namespaced file to * locate. If not namespaced, search in the app * folder. * @param non-empty-string|null $folder The folder within the namespace that we should @@ -32,12 +32,14 @@ interface FileLocatorInterface * folder. * @param string $ext The file extension the file should have. * - * @return false|string The path to the file, or false if not found. + * @return false|non-empty-string The path to the file, or false if not found. */ public function locateFile(string $file, ?string $folder = null, string $ext = 'php'); /** * Examines a file and returns the fully qualified class name. + * + * @param non-empty-string $file */ public function getClassname(string $file): string; @@ -54,7 +56,7 @@ public function getClassname(string $file): string; * 'app/Modules/bar/Config/Routes.php', * ] * - * @return list + * @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 cf8df099..4f1cf3dc 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -532,9 +532,8 @@ abstract protected function doOnlyDeleted(); * Compiles a replace and runs the query. * This method works only with dbCalls. * - * @param array|null $row Row data - * @phpstan-param row_array|null $row - * @param bool $returnSQL Set to true to return Query String + * @param row_array|null $row Row data + * @param bool $returnSQL Set to true to return Query String * * @return BaseResult|false|Query|string */ @@ -552,8 +551,7 @@ abstract protected function doErrors(); * Public getter to return the id value using the idValue() method. * For example with SQL this will return $data->$this->primaryKey. * - * @param array|object $row Row data - * @phpstan-param row_array|object $row + * @param object|row_array $row Row data * * @return array|int|string|null */ @@ -737,8 +735,7 @@ public function first() * you must ensure that the class will provide access to the class * variables, even if through a magic method. * - * @param array|object $row Row data - * @phpstan-param row_array|object $row + * @param object|row_array $row Row data * * @throws ReflectionException */ @@ -789,12 +786,10 @@ public function getInsertID() * Inserts data into the database. If an object is provided, * it will attempt to convert it to an array. * - * @param array|object|null $row Row data - * @phpstan-param row_array|object|null $row - * @param bool $returnID Whether insert ID should be returned or not. + * @param object|row_array|null $row Row data + * @param bool $returnID Whether insert ID should be returned or not. * - * @return bool|int|string insert ID or true on success. false on failure. - * @phpstan-return ($returnID is true ? int|string|false : bool) + * @return ($returnID is true ? false|int|string : bool) * * @throws ReflectionException */ @@ -867,8 +862,8 @@ public function insert($row = null, bool $returnID = true) /** * Set datetime to created field. * - * @phpstan-param row_array $row - * @param int|string $date timestamp or datetime string + * @param row_array $row + * @param int|string $date timestamp or datetime string */ protected function setCreatedField(array $row, $date): array { @@ -882,8 +877,8 @@ protected function setCreatedField(array $row, $date): array /** * Set datetime to updated field. * - * @phpstan-param row_array $row - * @param int|string $date timestamp or datetime string + * @param row_array $row + * @param int|string $date timestamp or datetime string */ protected function setUpdatedField(array $row, $date): array { @@ -897,11 +892,10 @@ protected function setUpdatedField(array $row, $date): array /** * Compiles batch insert runs the queries, validating each row prior. * - * @param list|null $set an associative array of insert values - * @phpstan-param list|null $set - * @param bool|null $escape Whether to escape values - * @param int $batchSize The size of the batch to run - * @param bool $testing True means only number of records is returned, false will execute the query + * @param list|null $set an associative array of insert values + * @param bool|null $escape Whether to escape values + * @param int $batchSize The size of the batch to run + * @param bool $testing True means only number of records is returned, false will execute the query * * @return bool|int Number of rows inserted or FALSE on failure * @@ -1043,11 +1037,10 @@ public function update($id = null, $row = null): bool /** * Compiles an update and runs the query. * - * @param list|null $set an associative array of insert values - * @phpstan-param list|null $set - * @param string|null $index The where key - * @param int $batchSize The size of the batch to run - * @param bool $returnSQL True means SQL is returned, false will execute the query + * @param list|null $set an associative array of insert values + * @param string|null $index The where key + * @param int $batchSize The size of the batch to run + * @param bool $returnSQL True means SQL is returned, false will execute the query * * @return false|int|list Number of rows affected or FALSE on failure, SQL array when testMode * @@ -1094,9 +1087,7 @@ public function updateBatch(?array $set = null, ?string $index = null, int $batc $row = $this->doProtectFields($row); // Restore updateIndex value in case it was wiped out - if ($updateIndex !== null) { - $row[$index] = $updateIndex; - } + $row[$index] = $updateIndex; $row = $this->setUpdatedField($row, $this->setDate()); } @@ -1128,8 +1119,8 @@ public function updateBatch(?array $set = null, ?string $index = null, int $batc /** * Deletes a single record from the database where $id matches. * - * @param array|int|string|null $id The rows primary key(s) - * @param bool $purge Allows overriding the soft deletes setting. + * @param int|list|string|null $id The rows primary key(s) + * @param bool $purge Allows overriding the soft deletes setting. * * @return BaseResult|bool * @@ -1217,9 +1208,8 @@ public function onlyDeleted() /** * Compiles a replace and runs the query. * - * @param array|null $row Row data - * @phpstan-param row_array|null $row - * @param bool $returnSQL Set to true to return Query String + * @param row_array|null $row Row data + * @param bool $returnSQL Set to true to return Query String * * @return BaseResult|false|Query|string */ @@ -1813,9 +1803,8 @@ protected function objectToRawArray($object, bool $onlyChanged = true, bool $rec /** * Transform data to array. * - * @param array|object|null $row Row data - * @phpstan-param row_array|object|null $row - * @param string $type Type of data (insert|update) + * @param object|row_array|null $row Row data + * @param string $type Type of data (insert|update) * * @throws DataException * @throws InvalidArgumentException diff --git a/system/Boot.php b/system/Boot.php index 50269224..ba367551 100644 --- a/system/Boot.php +++ b/system/Boot.php @@ -16,6 +16,7 @@ use CodeIgniter\Cache\FactoriesCache; use CodeIgniter\CLI\Console; use CodeIgniter\Config\DotEnv; +use Config\App; use Config\Autoload; use Config\Modules; use Config\Optimize; @@ -75,6 +76,34 @@ public static function bootWeb(Paths $paths): int return EXIT_SUCCESS; } + /** + * Used by command line scripts other than + * * `spark` + * * `php-cli` + * * `phpunit` + * + * @used-by `system/util_bootstrap.php` + */ + public static function bootConsole(Paths $paths): void + { + static::definePathConstants($paths); + static::loadConstants(); + static::checkMissingExtensions(); + + static::loadDotEnv($paths); + static::loadEnvironmentBootstrap($paths); + + static::loadCommonFunctions(); + static::loadAutoloader(); + static::setExceptionHandler(); + static::initializeKint(); + static::autoloadHelpers(); + + // We need to force the request to be a CLIRequest since we're in console + Services::createRequest(new App(), true); + service('routes')->loadRoutes(); + } + /** * Used by `spark` * @@ -344,9 +373,8 @@ protected static function initializeConsole(): Console $console = new Console(); // Show basic information before we do anything else. - // @phpstan-ignore-next-line if (is_int($suppress = array_search('--no-header', $_SERVER['argv'], true))) { - unset($_SERVER['argv'][$suppress]); // @phpstan-ignore-line + unset($_SERVER['argv'][$suppress]); $suppress = true; } diff --git a/system/CLI/Console.php b/system/CLI/Console.php index 325eaa7b..89415e26 100644 --- a/system/CLI/Console.php +++ b/system/CLI/Console.php @@ -59,10 +59,9 @@ public function showHeader(bool $suppress = false) } CLI::write(sprintf( - 'CodeIgniter v%s Command Line Tool - Server Time: %s UTC%s', + 'CodeIgniter v%s Command Line Tool - Server Time: %s', CodeIgniter::CI_VERSION, - date('Y-m-d H:i:s'), - date('P'), + date('Y-m-d H:i:s \\U\\T\\CP'), ), 'green'); CLI::newLine(); } diff --git a/system/CLI/GeneratorTrait.php b/system/CLI/GeneratorTrait.php index 1024fa69..62f5ceec 100644 --- a/system/CLI/GeneratorTrait.php +++ b/system/CLI/GeneratorTrait.php @@ -282,22 +282,21 @@ protected function qualifyClassName(): string return $namespace . $directoryString . str_replace('/', '\\', $class); } - /** - * Normalize input classname. - */ private function normalizeInputClassName(): string { // Gets the class name from input. $class = $this->params[0] ?? CLI::getSegment(2); if ($class === null && $this->hasClassName) { - // @codeCoverageIgnoreStart - $nameLang = $this->classNameLang !== '' + $nameField = $this->classNameLang !== '' ? $this->classNameLang : 'CLI.generator.className.default'; - $class = CLI::prompt(lang($nameLang), null, 'required'); + $class = CLI::prompt(lang($nameField), null, 'required'); + + // Reassign the class name to the params array in case + // the class name is requested again + $this->params[0] = $class; CLI::newLine(); - // @codeCoverageIgnoreEnd } helper('inflector'); diff --git a/system/Cache/CacheFactory.php b/system/Cache/CacheFactory.php index fb60d73c..e8425414 100644 --- a/system/Cache/CacheFactory.php +++ b/system/Cache/CacheFactory.php @@ -75,7 +75,7 @@ public static function getHandler(Cache $config, ?string $handler = null, ?strin } } - // If $adapter->initialization throws a CriticalError exception, we will attempt to + // If $adapter->initialize() throws a CriticalError exception, we will attempt to // use the $backup handler, if that also fails, we resort to the dummy handler. try { $adapter->initialize(); diff --git a/system/Cache/CacheInterface.php b/system/Cache/CacheInterface.php index d76d5aa1..0f94cd56 100644 --- a/system/Cache/CacheInterface.php +++ b/system/Cache/CacheInterface.php @@ -13,9 +13,6 @@ namespace CodeIgniter\Cache; -/** - * Cache interface - */ interface CacheInterface { /** @@ -30,16 +27,16 @@ public function initialize(); * * @param string $key Cache item name * - * @return array|bool|float|int|object|string|null + * @return mixed */ public function get(string $key); /** * Saves an item to the cache store. * - * @param string $key Cache item name - * @param array|bool|float|int|object|string|null $value The data to save - * @param int $ttl Time To Live, in seconds (default 60) + * @param string $key Cache item name + * @param mixed $value The data to save + * @param int $ttl Time To Live, in seconds (default 60) * * @return bool Success or failure */ @@ -87,7 +84,7 @@ public function clean(); * The information returned and the structure of the data * varies depending on the handler. * - * @return array|false|object|null + * @return array|false|object|null */ public function getCacheInfo(); @@ -96,10 +93,9 @@ public function getCacheInfo(); * * @param string $key Cache item name. * - * @return array|false|null - * Returns null if the item does not exist, otherwise array - * with at least the 'expire' key for absolute epoch expiry (or null). - * Some handlers may return false when an item does not exist, which is deprecated. + * @return array|false|null Returns null if the item does not exist, otherwise array + * with at least the 'expire' key for absolute epoch expiry (or null). + * Some handlers may return false when an item does not exist, which is deprecated. */ public function getMetaData(string $key); diff --git a/system/Cache/Exceptions/CacheException.php b/system/Cache/Exceptions/CacheException.php index 650dad64..c4871a00 100644 --- a/system/Cache/Exceptions/CacheException.php +++ b/system/Cache/Exceptions/CacheException.php @@ -16,9 +16,6 @@ use CodeIgniter\Exceptions\DebugTraceableTrait; use CodeIgniter\Exceptions\RuntimeException; -/** - * CacheException - */ class CacheException extends RuntimeException { use DebugTraceableTrait; @@ -26,7 +23,7 @@ class CacheException extends RuntimeException /** * Thrown when handler has no permission to write cache. * - * @return CacheException + * @return static */ public static function forUnableToWrite(string $path) { @@ -36,7 +33,7 @@ public static function forUnableToWrite(string $path) /** * Thrown when an unrecognized handler is used. * - * @return CacheException + * @return static */ public static function forInvalidHandlers() { @@ -46,7 +43,7 @@ public static function forInvalidHandlers() /** * Thrown when no backup handler is setup in config. * - * @return CacheException + * @return static */ public static function forNoBackup() { @@ -56,7 +53,7 @@ public static function forNoBackup() /** * Thrown when specified handler was not found. * - * @return CacheException + * @return static */ public static function forHandlerNotFound() { diff --git a/system/Cache/FactoriesCache.php b/system/Cache/FactoriesCache.php index e4b7488f..6e49996c 100644 --- a/system/Cache/FactoriesCache.php +++ b/system/Cache/FactoriesCache.php @@ -18,15 +18,9 @@ final class FactoriesCache { - /** - * @var CacheInterface|FileVarExportHandler - */ - private $cache; - - /** - * @param CacheInterface|FileVarExportHandler|null $cache - */ - public function __construct($cache = null) + private readonly CacheInterface|FileVarExportHandler $cache; + + public function __construct(CacheInterface|FileVarExportHandler|null $cache = null) { $this->cache = $cache ?? new FileVarExportHandler(); } @@ -51,7 +45,9 @@ public function load(string $component): bool { $key = $this->getCacheKey($component); - if (! $data = $this->cache->get($key)) { + $data = $this->cache->get($key); + + if (! is_array($data) || $data === []) { return false; } diff --git a/system/Cache/FactoriesCache/FileVarExportHandler.php b/system/Cache/FactoriesCache/FileVarExportHandler.php index b7c1a043..092cd67e 100644 --- a/system/Cache/FactoriesCache/FileVarExportHandler.php +++ b/system/Cache/FactoriesCache/FileVarExportHandler.php @@ -17,10 +17,7 @@ final class FileVarExportHandler { private string $path = WRITEPATH . 'cache'; - /** - * @param array|bool|float|int|object|string|null $val - */ - public function save(string $key, $val): void + public function save(string $key, mixed $val): void { $val = var_export($val, true); @@ -36,10 +33,7 @@ public function delete(string $key): void @unlink($this->path . "/{$key}"); } - /** - * @return array|bool|float|int|object|string|null - */ - public function get(string $key) + public function get(string $key): mixed { return @include $this->path . "/{$key}"; } diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index ebe0ca2f..db482952 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -53,7 +53,7 @@ abstract class BaseHandler implements CacheInterface * Keys that exceed MAX_KEY_LENGTH are hashed. * From https://github.com/symfony/cache/blob/7b024c6726af21fd4984ac8d1eae2b9f3d90de88/CacheItem.php#L158 * - * @param string $key The key to validate + * @param mixed $key The key to validate * @param string $prefix Optional prefix to include in length calculations * * @throws InvalidArgumentException When $key is not valid @@ -67,7 +67,8 @@ public static function validateKey($key, $prefix = ''): string throw new InvalidArgumentException('Cache key cannot be empty.'); } - $reserved = config(Cache::class)->reservedCharacters ?? self::RESERVED_CHARACTERS; + $reserved = config(Cache::class)->reservedCharacters; + if ($reserved !== '' && strpbrk($key, $reserved) !== false) { throw new InvalidArgumentException('Cache key contains reserved characters ' . $reserved); } @@ -83,7 +84,7 @@ public static function validateKey($key, $prefix = ''): string * @param int $ttl Time to live * @param Closure(): mixed $callback Callback return value * - * @return array|bool|float|int|object|string|null + * @return mixed */ public function remember(string $key, int $ttl, Closure $callback) { @@ -103,7 +104,7 @@ public function remember(string $key, int $ttl, Closure $callback) * * @param string $pattern Cache items glob-style pattern * - * @return int|never + * @return int * * @throws Exception */ diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index 4fd03e79..010fc86d 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -54,14 +54,19 @@ class FileHandler extends BaseHandler */ public function __construct(Cache $config) { - $this->path = ! empty($config->file['storePath']) ? $config->file['storePath'] : WRITEPATH . 'cache'; - $this->path = rtrim($this->path, '/') . '/'; + $options = [ + ...['storePath' => WRITEPATH . 'cache', 'mode' => 0640], + ...$config->file, + ]; + + $this->path = $options['storePath'] !== '' ? $options['storePath'] : WRITEPATH . 'cache'; + $this->path = rtrim($this->path, '\\/') . '/'; if (! is_really_writable($this->path)) { throw CacheException::forUnableToWrite($this->path); } - $this->mode = $config->file['mode'] ?? 0640; + $this->mode = $options['mode']; $this->prefix = $config->prefix; helper('filesystem'); @@ -217,7 +222,7 @@ public function isSupported(): bool /** * Does the heavy lifting of actually retrieving the file and - * verifying it's age. + * verifying its age. * * @return array{data: mixed, ttl: int, time: int}|false */ @@ -227,7 +232,17 @@ protected function getItem(string $filename) return false; } - $data = @unserialize(file_get_contents($this->path . $filename)); + $content = @file_get_contents($this->path . $filename); + + if ($content === false) { + return false; + } + + try { + $data = unserialize($content); + } catch (Throwable) { + return false; + } if (! is_array($data)) { return false; @@ -332,33 +347,46 @@ protected function deleteFiles(string $path, bool $delDir = false, bool $htdocs * @param bool $topLevelOnly Look only at the top level directory specified? * @param bool $_recursion Internal variable to determine recursion status - do not use in calls * - * @return array|false + * @return array|false */ protected function getDirFileInfo(string $sourceDir, bool $topLevelOnly = true, bool $_recursion = false) { - static $_filedata = []; - $relativePath = $sourceDir; + static $filedata = []; + + $relativePath = $sourceDir; + $filePointer = @opendir($sourceDir); - if ($fp = @opendir($sourceDir)) { + if (! is_bool($filePointer)) { // 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; + $filedata = []; + + $resolvedSrc = realpath($sourceDir); + $resolvedSrc = $resolvedSrc === false ? $sourceDir : $resolvedSrc; + + $sourceDir = rtrim($resolvedSrc, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; } // Used to be foreach (scandir($sourceDir, 1) as $file), but scandir() is simply not as fast - while (false !== ($file = readdir($fp))) { + while (false !== $file = readdir($filePointer)) { if (is_dir($sourceDir . $file) && $file[0] !== '.' && $topLevelOnly === false) { $this->getDirFileInfo($sourceDir . $file . DIRECTORY_SEPARATOR, $topLevelOnly, true); } elseif (! is_dir($sourceDir . $file) && $file[0] !== '.') { - $_filedata[$file] = $this->getFileInfo($sourceDir . $file); - $_filedata[$file]['relative_path'] = $relativePath; + $filedata[$file] = $this->getFileInfo($sourceDir . $file); + + $filedata[$file]['relative_path'] = $relativePath; } } - closedir($fp); + closedir($filePointer); - return $_filedata; + return $filedata; } return false; @@ -372,10 +400,19 @@ protected function getDirFileInfo(string $sourceDir, bool $topLevelOnly = true, * * @deprecated 4.6.1 Use `get_file_info()` instead. * - * @param string $file Path to file - * @param array|string $returnedValues Array or comma separated string of information returned + * @param string $file Path to file + * @param list|string $returnedValues Array or comma separated string of information returned * - * @return array|false + * @return array{ + * name?: string, + * server_path?: string, + * size?: int, + * date?: int, + * readable?: bool, + * writable?: bool, + * executable?: bool, + * fileperms?: int + * }|false */ protected function getFileInfo(string $file, $returnedValues = ['name', 'server_path', 'size', 'date']) { diff --git a/system/Cache/Handlers/MemcachedHandler.php b/system/Cache/Handlers/MemcachedHandler.php index c6bb6ddc..91ac38a9 100644 --- a/system/Cache/Handlers/MemcachedHandler.php +++ b/system/Cache/Handlers/MemcachedHandler.php @@ -38,7 +38,7 @@ class MemcachedHandler extends BaseHandler /** * Memcached Configuration * - * @var array + * @var array{host: string, port: int, weight: int, raw: bool} */ protected $config = [ 'host' => '127.0.0.1', @@ -76,43 +76,32 @@ public function initialize() { try { if (class_exists(Memcached::class)) { - // Create new instance of Memcached $this->memcached = new Memcached(); + if ($this->config['raw']) { $this->memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true); } - // Add server $this->memcached->addServer( $this->config['host'], $this->config['port'], $this->config['weight'], ); - // attempt to get status of servers $stats = $this->memcached->getStats(); // $stats should be an associate array with a key in the format of host:port. // If it doesn't have the key, we know the server is not working as expected. - if (! isset($stats[$this->config['host'] . ':' . $this->config['port']])) { + if (! is_array($stats) || ! isset($stats[$this->config['host'] . ':' . $this->config['port']])) { throw new CriticalError('Cache: Memcached connection failed.'); } } elseif (class_exists(Memcache::class)) { - // Create new instance of Memcache $this->memcached = new Memcache(); - // Check if we can connect to the server - $canConnect = $this->memcached->connect( - $this->config['host'], - $this->config['port'], - ); - - // If we can't connect, throw a CriticalError exception - if ($canConnect === false) { + if (! $this->memcached->connect($this->config['host'], $this->config['port'])) { throw new CriticalError('Cache: Memcache connection failed.'); } - // Add server, third parameter is persistence and defaults to TRUE. $this->memcached->addServer( $this->config['host'], $this->config['port'], diff --git a/system/Cache/Handlers/PredisHandler.php b/system/Cache/Handlers/PredisHandler.php index bdf5afe0..250ea74a 100644 --- a/system/Cache/Handlers/PredisHandler.php +++ b/system/Cache/Handlers/PredisHandler.php @@ -31,7 +31,13 @@ class PredisHandler extends BaseHandler /** * Default config * - * @var array + * @var array{ + * scheme: string, + * host: string, + * password: string|null, + * port: int, + * timeout: int + * } */ protected $config = [ 'scheme' => 'tcp', diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index 80c21e85..3a13ee07 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -29,7 +29,13 @@ class RedisHandler extends BaseHandler /** * Default config * - * @var array + * @var array{ + * host: string, + * password: string|null, + * port: int, + * timeout: int, + * database: int, + * } */ protected $config = [ 'host' => '127.0.0.1', diff --git a/system/Cache/ResponseCache.php b/system/Cache/ResponseCache.php index e984b8e3..2c92dc50 100644 --- a/system/Cache/ResponseCache.php +++ b/system/Cache/ResponseCache.php @@ -40,12 +40,10 @@ final class ResponseCache * * @var bool|list */ - private $cacheQueryString = false; + private array|bool $cacheQueryString = false; /** - * Cache time to live. - * - * @var int seconds + * Cache time to live (TTL) in seconds. */ private int $ttl = 0; @@ -54,10 +52,7 @@ public function __construct(CacheConfig $config, private readonly CacheInterface $this->cacheQueryString = $config->cacheQueryString; } - /** - * @return $this - */ - public function setTtl(int $ttl) + public function setTtl(int $ttl): self { $this->ttl = $ttl; @@ -67,11 +62,9 @@ public function setTtl(int $ttl) /** * Generates the cache key to use from the current request. * - * @param CLIRequest|IncomingRequest $request - * * @internal for testing purposes only */ - public function generateCacheKey($request): string + public function generateCacheKey(CLIRequest|IncomingRequest $request): string { if ($request instanceof CLIRequest) { return md5($request->getPath()); @@ -79,7 +72,7 @@ public function generateCacheKey($request): string $uri = clone $request->getUri(); - $query = $this->cacheQueryString + $query = (bool) $this->cacheQueryString ? $uri->getQuery(is_array($this->cacheQueryString) ? ['only' => $this->cacheQueryString] : []) : ''; @@ -88,10 +81,8 @@ public function generateCacheKey($request): string /** * Caches the response. - * - * @param CLIRequest|IncomingRequest $request */ - public function make($request, ResponseInterface $response): bool + public function make(CLIRequest|IncomingRequest $request, ResponseInterface $response): bool { if ($this->ttl === 0) { return true; @@ -118,12 +109,12 @@ public function make($request, ResponseInterface $response): bool /** * Gets the cached response for the request. - * - * @param CLIRequest|IncomingRequest $request */ - public function get($request, ResponseInterface $response): ?ResponseInterface + public function get(CLIRequest|IncomingRequest $request, ResponseInterface $response): ?ResponseInterface { - if ($cachedResponse = $this->cache->get($this->generateCacheKey($request))) { + $cachedResponse = $this->cache->get($this->generateCacheKey($request)); + + if (is_string($cachedResponse) && $cachedResponse !== '') { $cachedResponse = unserialize($cachedResponse); if ( diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 2c89a5e2..3c81f64c 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -55,7 +55,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.6.1'; + public const CI_VERSION = '4.6.2'; /** * App startup time. @@ -141,7 +141,7 @@ class CodeIgniter * web: Invoked by HTTP request * php-cli: Invoked by CLI via `php public/index.php` * - * @phpstan-var 'php-cli'|'web' + * @var 'php-cli'|'web'|null */ protected ?string $context = null; @@ -1128,7 +1128,7 @@ protected function callExit($code) /** * Sets the app context. * - * @phpstan-param 'php-cli'|'web' $context + * @param 'php-cli'|'web' $context * * @return $this */ diff --git a/system/Commands/Generators/TestGenerator.php b/system/Commands/Generators/TestGenerator.php index 7ab6859e..47f71294 100644 --- a/system/Commands/Generators/TestGenerator.php +++ b/system/Commands/Generators/TestGenerator.php @@ -76,6 +76,9 @@ class TestGenerator extends BaseCommand */ public function run(array $params) { + // Ensure tests are always suffixed with 'Test' + $params['suffix'] = null; + $this->component = 'Test'; $this->template = 'test.tpl.php'; @@ -173,20 +176,17 @@ protected function buildPath(string $class): string /** * Returns test file path for the namespace. */ - private function searchTestFilePath(string $namespace): ?string + private function searchTestFilePath(string $testNamespace): ?string { - $bases = service('autoloader')->getNamespace($namespace); - - $base = null; - - foreach ($bases as $candidate) { - if (str_contains($candidate, '/tests/')) { - $base = $candidate; + /** @var list $testPaths */ + $testPaths = service('autoloader')->getNamespace($testNamespace); - break; + foreach ($testPaths as $candidate) { + if (str_contains($candidate, DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR)) { + return $candidate; } } - return $base; + return null; } } diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 3307dab8..e6557b74 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -361,8 +361,7 @@ private function isSubDirectory(string $directory, string $rootDirectory): bool /** * @param list $files * - * @return array - * @phpstan-return array{'foundLanguageKeys': array>, 'badLanguageKeys': array>, 'countFiles': int} + * @return array{'foundLanguageKeys': array>, 'badLanguageKeys': array>, 'countFiles': int} */ private function findLanguageKeysInFiles(array $files): array { diff --git a/system/Common.php b/system/Common.php index 910ec9b1..3f69838e 100644 --- a/system/Common.php +++ b/system/Common.php @@ -69,8 +69,7 @@ function app_timezone(): string * cache()->save('foo', 'bar'); * $foo = cache('bar'); * - * @return array|bool|CacheInterface|float|int|object|string|null - * @phpstan-return ($key is null ? CacheInterface : array|bool|float|int|object|string|null) + * @return ($key is null ? CacheInterface : mixed) */ function cache(?string $key = null) { @@ -201,8 +200,7 @@ function command(string $command) * * @param class-string|string $name * - * @return ConfigTemplate|null - * @phpstan-return ($name is class-string ? ConfigTemplate : object|null) + * @return ($name is class-string ? ConfigTemplate : object|null) */ function config(string $name, bool $getShared = true) { @@ -404,11 +402,11 @@ function env(string $key, $default = null) * If $data is an array, then it loops over it, escaping each * 'value' of the key/value pairs. * - * @param array|string $data - * @phpstan-param 'html'|'js'|'css'|'url'|'attr'|'raw' $context - * @param string|null $encoding Current encoding for escaping. - * If not UTF-8, we convert strings from this encoding - * pre-escaping and back to this encoding post-escaping. + * @param array|string $data + * @param 'attr'|'css'|'html'|'js'|'raw'|'url' $context + * @param string|null $encoding Current encoding for escaping. + * If not UTF-8, we convert strings from this encoding + * pre-escaping and back to this encoding post-escaping. * * @return array|string * @@ -796,8 +794,7 @@ function log_message(string $level, string $message, array $context = []): void * * @param class-string|string $name * - * @return ModelTemplate|null - * @phpstan-return ($name is class-string ? ModelTemplate : object|null) + * @return ($name is class-string ? ModelTemplate : object|null) */ function model(string $name, bool $getShared = true, ?ConnectionInterface &$conn = null) { @@ -810,9 +807,8 @@ function model(string $name, bool $getShared = true, ?ConnectionInterface &$conn * Provides access to "old input" that was set in the session * during a redirect()->withInput(). * - * @param string|null $default - * @param false|string $escape - * @phpstan-param false|'attr'|'css'|'html'|'js'|'raw'|'url' $escape + * @param string|null $default + * @param 'attr'|'css'|'html'|'js'|'raw'|'url'|false $escape * * @return array|string|null */ @@ -965,8 +961,7 @@ function route_to(string $method, ...$params) * session()->set('foo', 'bar'); * $foo = session('bar'); * - * @return array|bool|float|int|object|Session|string|null - * @phpstan-return ($val is null ? Session : array|bool|float|int|object|string|null) + * @return ($val is null ? Session : mixed) */ function session(?string $val = null) { @@ -1123,8 +1118,7 @@ function stringify_attributes($attributes, bool $js = false): string * @param non-empty-string|null $name * @param (callable(): mixed)|null $callable * - * @return mixed|Timer - * @phpstan-return ($name is null ? Timer : ($callable is (callable(): mixed) ? mixed : Timer)) + * @return ($name is null ? Timer : ($callable is (callable(): mixed) ? mixed : Timer)) */ function timer(?string $name = null, ?callable $callable = null) { diff --git a/system/Config/AutoloadConfig.php b/system/Config/AutoloadConfig.php index 0a99cdb0..b6d7f72b 100644 --- a/system/Config/AutoloadConfig.php +++ b/system/Config/AutoloadConfig.php @@ -14,6 +14,7 @@ namespace CodeIgniter\Config; use Laminas\Escaper\Escaper; +use Laminas\Escaper\EscaperInterface; use Laminas\Escaper\Exception\ExceptionInterface; use Laminas\Escaper\Exception\InvalidArgumentException as EscaperInvalidArgumentException; use Laminas\Escaper\Exception\RuntimeException; @@ -119,6 +120,7 @@ class AutoloadConfig ExceptionInterface::class => SYSTEMPATH . 'ThirdParty/Escaper/Exception/ExceptionInterface.php', EscaperInvalidArgumentException::class => SYSTEMPATH . 'ThirdParty/Escaper/Exception/InvalidArgumentException.php', RuntimeException::class => SYSTEMPATH . 'ThirdParty/Escaper/Exception/RuntimeException.php', + EscaperInterface::class => SYSTEMPATH . 'ThirdParty/Escaper/EscaperInterface.php', Escaper::class => SYSTEMPATH . 'ThirdParty/Escaper/Escaper.php', ]; diff --git a/system/Config/Services.php b/system/Config/Services.php index f5c5a2db..c9266a89 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -683,6 +683,11 @@ public static function session(?SessionConfig $config = null, bool $getShared = $driverName = MySQLiHandler::class; } elseif ($driverPlatform === 'Postgre') { $driverName = PostgreHandler::class; + } else { + throw new InvalidArgumentException(sprintf( + 'Invalid session database handler "%s" provided. Only "MySQLi" and "Postgre" are supported.', + $driverPlatform, + )); } } diff --git a/system/Cookie/Cookie.php b/system/Cookie/Cookie.php index df75c03c..ad461c72 100644 --- a/system/Cookie/Cookie.php +++ b/system/Cookie/Cookie.php @@ -99,7 +99,16 @@ class Cookie implements ArrayAccess, CloneableCookieInterface * Default attributes for a Cookie object. The keys here are the * lowercase attribute names. Do not camelCase! * - * @var array + * @var array{ + * prefix: string, + * expires: int, + * path: string, + * domain: string, + * secure: bool, + * httponly: bool, + * samesite: string, + * raw: bool, + * } */ private static array $defaults = [ 'prefix' => '', @@ -127,9 +136,27 @@ class Cookie implements ArrayAccess, CloneableCookieInterface * * This method is called from Response::__construct(). * - * @param array|CookieConfig $config + * @param array{ + * prefix?: string, + * expires?: int, + * path?: string, + * domain?: string, + * secure?: bool, + * httponly?: bool, + * samesite?: string, + * raw?: bool, + * }|CookieConfig $config * - * @return array The old defaults array. Useful for resetting. + * @return array{ + * prefix: string, + * expires: int, + * path: string, + * domain: string, + * secure: bool, + * httponly: bool, + * samesite: string, + * raw: bool, + * } The old defaults array. Useful for resetting. */ public static function setDefaults($config = []) { @@ -198,9 +225,9 @@ public static function fromHeaderString(string $cookie, bool $raw = false) /** * Construct a new Cookie instance. * - * @param string $name The cookie's name - * @param string $value The cookie's value - * @param array $options The cookie's options + * @param string $name The cookie's name + * @param string $value The cookie's value + * @param array{prefix?: string, max-age?: int|numeric-string, expires?: DateTimeInterface|int|string, path?: string, domain?: string, secure?: bool, httponly?: bool, samesite?: string, raw?: bool} $options The cookie's options * * @throws CookieException */ diff --git a/system/Cookie/CookieInterface.php b/system/Cookie/CookieInterface.php index c848fa88..b4a7ea3d 100644 --- a/system/Cookie/CookieInterface.php +++ b/system/Cookie/CookieInterface.php @@ -145,7 +145,14 @@ public function isRaw(): bool; * Gets the options that are passable to the `setcookie` variant * available on PHP 7.3+ * - * @return array + * @return array{ + * expires: int, + * path: string, + * domain: string, + * secure: bool, + * httponly: bool, + * samesite: string, + * } */ public function getOptions(): array; @@ -164,7 +171,18 @@ public function __toString(); /** * Returns the array representation of the Cookie object. * - * @return array + * @return array{ + * name: string, + * value: string, + * prefix: string, + * raw: bool, + * expires: int, + * path: string, + * domain: string, + * secure: bool, + * httponly: bool, + * samesite: string, + * } */ public function toArray(): array; } diff --git a/system/DataCaster/DataCaster.php b/system/DataCaster/DataCaster.php index ae3f40f4..81ebe233 100644 --- a/system/DataCaster/DataCaster.php +++ b/system/DataCaster/DataCaster.php @@ -113,10 +113,9 @@ public function setTypes(array $types): static * Add ? at the beginning of the type (i.e. ?string) to get `null` * instead of casting $value when $value is null. * - * @param mixed $value The value to convert - * @param string $field The field name - * @param string $method Allowed to "get" and "set" - * @phpstan-param 'get'|'set' $method + * @param mixed $value The value to convert + * @param string $field The field name + * @param 'get'|'set' $method Allowed to "get" and "set" */ public function castAs(mixed $value, string $field, string $method = 'get'): mixed { diff --git a/system/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php index 43b42a08..fa8353e8 100644 --- a/system/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -20,9 +20,9 @@ /** * PHP data <==> DataSource data converter * - * @see \CodeIgniter\DataConverter\DataConverterTest - * * @template TEntity of object + * + * @see \CodeIgniter\DataConverter\DataConverterTest */ final class DataConverter { @@ -52,14 +52,14 @@ public function __construct( * Static reconstruct method name or closure to reconstruct an object. * Used by reconstruct(). * - * @phpstan-var (Closure(array): TEntity)|string|null + * @var (Closure(array): TEntity)|string|null */ private readonly Closure|string|null $reconstructor = 'reconstruct', /** * Extract method name or closure to extract data from an object. * Used by extract(). * - * @phpstan-var (Closure(TEntity, bool, bool): array)|string|null + * @var (Closure(TEntity, bool, bool): array)|string|null */ private readonly Closure|string|null $extractor = null, ) { @@ -105,11 +105,10 @@ public function toDataSource(array $phpData): array /** * Takes database data array and creates a specified type object. * - * @param class-string $classname - * @phpstan-param class-string $classname - * @param array $row Raw data from database + * @param class-string $classname + * @param array $row Raw data from database * - * @phpstan-return TEntity + * @return TEntity * * @internal */ diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index cba7f5cb..82442fbc 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -256,7 +256,7 @@ class BaseBuilder * Specifies which sql statements * support the ignore option. * - * @var array + * @var array */ protected $supportedIgnoreStatements = []; diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index b274b02a..f09175d5 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -206,16 +206,14 @@ abstract class BaseConnection implements ConnectionInterface /** * Connection ID * - * @var false|object|resource - * @phpstan-var false|TConnection + * @var false|TConnection */ public $connID = false; /** * Result ID * - * @var false|object|resource - * @phpstan-var false|TResult + * @var false|TResult */ public $resultID = false; @@ -498,8 +496,7 @@ abstract protected function _close(); /** * Create a persistent database connection. * - * @return false|object|resource - * @phpstan-return false|TConnection + * @return false|TConnection */ public function persistentConnect() { @@ -512,8 +509,7 @@ public function persistentConnect() * get that connection. If you pass either alias in and only a single * connection is present, it must return the sole connection. * - * @return false|object|resource - * @phpstan-return TConnection + * @return false|TConnection */ public function getConnection(?string $alias = null) { @@ -592,8 +588,7 @@ public function addTableAlias(string $alias) /** * Executes the query against the database. * - * @return false|object|resource - * @phpstan-return false|TResult + * @return false|TResult */ abstract protected function execute(string $sql); @@ -607,8 +602,7 @@ abstract protected function execute(string $sql); * * @param array|string|null $binds * - * @return BaseResult|bool|Query BaseResult when “read” type query, bool when “write” type query, Query when prepared query - * @phpstan-return BaseResult|bool|Query + * @return BaseResult|bool|Query * * @todo BC set $queryClass default as null in 4.1 */ @@ -658,9 +652,7 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s $query->setDuration($startTime, $startTime); // This will trigger a rollback if transactions are being used - if ($this->transDepth !== 0) { - $this->transStatus = false; - } + $this->handleTransStatus(); if ( $this->DBDebug @@ -726,8 +718,7 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s * is performed, nor are transactions handled. Simply takes a raw * query string and returns the database-specific result id. * - * @return false|object|resource - * @phpstan-return false|TResult + * @return false|TResult */ public function simpleQuery(string $sql) { @@ -911,6 +902,18 @@ public function resetTransStatus(): static return $this; } + /** + * Handle transaction status when a query fails + * + * @internal This method is for internal database component use only + */ + public function handleTransStatus(): void + { + if ($this->transDepth !== 0) { + $this->transStatus = false; + } + } + /** * Begin Transaction */ @@ -1065,8 +1068,7 @@ public function getConnectDuration(int $decimals = 6): string * @param bool $protectIdentifiers Protect table or column names? * @param bool $fieldExists Supplied $item contains a column name? * - * @return array|string - * @phpstan-return ($item is array ? array : string) + * @return ($item is array ? array : string) */ public function protectIdentifiers($item, bool $prefixSingle = false, ?bool $protectIdentifiers = null, bool $fieldExists = true) { @@ -1270,8 +1272,7 @@ private function escapeTableName(TableName $tableName): string * * @param array|string $item * - * @return array|string - * @phpstan-return ($item is array ? array : string) + * @return ($item is array ? array : string) */ public function escapeIdentifiers($item) { @@ -1355,8 +1356,7 @@ abstract public function affectedRows(): int; * * @param array|bool|float|int|object|string|null $str * - * @return array|float|int|string - * @phpstan-return ($str is array ? array : float|int|string) + * @return ($str is array ? array : float|int|string) */ public function escape($str) { @@ -1787,8 +1787,7 @@ public function isWriteType($sql): bool * * Must return an array with keys 'code' and 'message': * - * @return array - * @phpstan-return array{code: int|string|null, message: string|null} + * @return array{code: int|string|null, message: string|null} */ abstract public function error(): array; diff --git a/system/Database/BasePreparedQuery.php b/system/Database/BasePreparedQuery.php index e9017bcf..d5bb40be 100644 --- a/system/Database/BasePreparedQuery.php +++ b/system/Database/BasePreparedQuery.php @@ -31,8 +31,7 @@ abstract class BasePreparedQuery implements PreparedQueryInterface /** * The prepared statement itself. * - * @var object|resource|null - * @phpstan-var TStatement|null + * @var TStatement|null */ protected $statement; @@ -61,8 +60,7 @@ abstract class BasePreparedQuery implements PreparedQueryInterface /** * A reference to the db connection to use. * - * @var BaseConnection - * @phpstan-var BaseConnection + * @var BaseConnection */ protected $db; @@ -112,8 +110,7 @@ abstract public function _prepare(string $sql, array $options = []); * Takes a new set of data and runs it against the currently * prepared query. Upon success, will return a Results object. * - * @return bool|ResultInterface - * @phpstan-return bool|ResultInterface + * @return bool|ResultInterface * * @throws DatabaseException */ @@ -137,9 +134,7 @@ public function execute(...$data) $query->setDuration($startTime, $startTime); // This will trigger a rollback if transactions are being used - if ($this->db->transDepth !== 0) { - $this->db->transStatus = false; - } + $this->db->handleTransStatus(); if ($this->db->DBDebug) { // We call this function in order to roll-back queries diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index efbc1672..c0bdc2aa 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -27,16 +27,14 @@ abstract class BaseResult implements ResultInterface /** * Connection ID * - * @var object|resource - * @phpstan-var TConnection + * @var TConnection */ public $connID; /** * Result ID * - * @var false|object|resource - * @phpstan-var false|TResult + * @var false|TResult */ public $resultID; @@ -85,10 +83,8 @@ abstract class BaseResult implements ResultInterface /** * Constructor * - * @param object|resource $connID - * @param object|resource $resultID - * @phpstan-param TConnection $connID - * @phpstan-param TResult $resultID + * @param TConnection $connID + * @param TResult $resultID */ public function __construct(&$connID, &$resultID) { @@ -119,7 +115,7 @@ public function getResult(string $type = 'object'): array /** * Returns the results as an array of custom objects. * - * @phpstan-param class-string $className + * @param class-string $className * * @return array */ @@ -211,8 +207,7 @@ public function getResultArray(): array * * If no results, an empty array is returned. * - * @return array - * @phpstan-return list + * @return list */ public function getResultObject(): array { @@ -258,12 +253,10 @@ public function getResultObject(): array * * @template T of object * - * @param int|string $n The index of the results to return, or column name. - * @param string $type The type of result object. 'array', 'object' or class name. - * @phpstan-param class-string|'array'|'object' $type + * @param int|string $n The index of the results to return, or column name. + * @param 'array'|'object'|class-string $type The type of result object. 'array', 'object' or class name. * - * @return array|float|int|object|stdClass|string|null - * @phpstan-return ($n is string ? float|int|string|null : ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null))) + * @return ($n is string ? float|int|string|null : ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null))) */ public function getRow($n = 0, string $type = 'object') { @@ -300,11 +293,10 @@ public function getRow($n = 0, string $type = 'object') * * @template T of object * - * @param int $n The index of the results to return. - * @phpstan-param class-string $className + * @param int $n The index of the results to return. + * @param class-string $className * - * @return object|null - * @phpstan-return T|null + * @return T|null */ public function getCustomRowObject(int $n, string $className) { @@ -537,9 +529,9 @@ abstract protected function fetchAssoc(); /** * Returns the result set as an object. * - * Overridden by child classes. + * @param class-string $className * - * @return Entity|false|object|stdClass + * @return false|object */ - abstract protected function fetchObject(string $className = 'stdClass'); + abstract protected function fetchObject(string $className = stdClass::class); } diff --git a/system/Database/ConnectionInterface.php b/system/Database/ConnectionInterface.php index 2f1c932c..8e483448 100644 --- a/system/Database/ConnectionInterface.php +++ b/system/Database/ConnectionInterface.php @@ -32,16 +32,14 @@ public function initialize(); /** * Connect to the database. * - * @return false|object|resource - * @phpstan-return false|TConnection + * @return false|TConnection */ public function connect(bool $persistent = false); /** * Create a persistent database connection. * - * @return false|object|resource - * @phpstan-return false|TConnection + * @return false|TConnection */ public function persistentConnect(); @@ -59,8 +57,7 @@ public function reconnect(); * get that connection. If you pass either alias in and only a single * connection is present, it must return the sole connection. * - * @return false|object|resource - * @phpstan-return false|TConnection + * @return false|TConnection */ public function getConnection(?string $alias = null); @@ -105,8 +102,7 @@ public function getVersion(): string; * * @param array|string|null $binds * - * @return BaseResult|bool|Query - * @phpstan-return BaseResult|bool|Query + * @return BaseResult|bool|Query */ public function query(string $sql, $binds = null); @@ -115,8 +111,7 @@ public function query(string $sql, $binds = null); * is performed, nor are transactions handled. Simply takes a raw * query string and returns the database-specific result id. * - * @return false|object|resource - * @phpstan-return false|TResult + * @return false|TResult */ public function simpleQuery(string $sql); @@ -144,8 +139,7 @@ public function getLastQuery(); * * @param array|bool|float|int|object|string|null $str * - * @return array|float|int|string - * @phpstan-return ($str is array ? array : float|int|string) + * @return ($str is array ? array : float|int|string) */ public function escape($str); diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 8405d9f7..f6eb617f 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -846,8 +846,7 @@ public function modifyColumn(string $table, $fields): bool * @param array|string $processedFields Processed column definitions * or column names to DROP * - * @return false|list|string|null SQL string - * @phpstan-return ($alterType is 'DROP' ? string : list|false|null) + * @return ($alterType is 'DROP' ? string : false|list|null) */ protected function _alterTable(string $alterType, string $table, $processedFields) { diff --git a/system/Database/MySQLi/Builder.php b/system/Database/MySQLi/Builder.php index b8d0fa54..a9e248fc 100644 --- a/system/Database/MySQLi/Builder.php +++ b/system/Database/MySQLi/Builder.php @@ -33,7 +33,7 @@ class Builder extends BaseBuilder * Specifies which sql statements * support the ignore option. * - * @var array + * @var array */ protected $supportedIgnoreStatements = [ 'update' => 'IGNORE', diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index fe207f52..9482f7e2 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -135,8 +135,7 @@ protected function _createTableAttributes(array $attributes): string * @param array|string $processedFields Processed column definitions * or column names to DROP * - * @return list|string SQL string - * @phpstan-return ($alterType is 'DROP' ? string : list) + * @return ($alterType is 'DROP' ? string : list) */ protected function _alterTable(string $alterType, string $table, $processedFields) { diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index f761045d..8f711691 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -100,8 +100,7 @@ class Forge extends BaseForge * @param array|string $processedFields Processed column definitions * or column names to DROP * - * @return list|string SQL string - * @phpstan-return ($alterType is 'DROP' ? string : list) + * @return ($alterType is 'DROP' ? string : list) */ protected function _alterTable(string $alterType, string $table, $processedFields) { diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index 8d0d569d..126ab574 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -36,7 +36,7 @@ class Builder extends BaseBuilder * Specifies which sql statements * support the ignore option. * - * @var array + * @var array */ protected $supportedIgnoreStatements = [ 'insert' => 'ON CONFLICT DO NOTHING', diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 584c452b..9b480975 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -242,8 +242,7 @@ public function affectedRows(): int * * @param array|bool|float|int|object|string|null $str * - * @return array|float|int|string - * @phpstan-return ($str is array ? array : float|int|string) + * @return ($str is array ? array : float|int|string) */ public function escape($str) { diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index b0a38c90..8b18d7d4 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -86,8 +86,7 @@ protected function _createTableAttributes(array $attributes): string * @param array|string $processedFields Processed column definitions * or column names to DROP * - * @return false|list|string SQL string or false - * @phpstan-return ($alterType is 'DROP' ? string : list|false) + * @return ($alterType is 'DROP' ? string : false|list) */ protected function _alterTable(string $alterType, string $table, $processedFields) { diff --git a/system/Database/PreparedQueryInterface.php b/system/Database/PreparedQueryInterface.php index 6ac69904..c49c2e7b 100644 --- a/system/Database/PreparedQueryInterface.php +++ b/system/Database/PreparedQueryInterface.php @@ -26,8 +26,7 @@ interface PreparedQueryInterface * Takes a new set of data and runs it against the currently * prepared query. Upon success, will return a Results object. * - * @return bool|ResultInterface - * @phpstan-return bool|ResultInterface + * @return bool|ResultInterface */ public function execute(...$data); diff --git a/system/Database/ResultInterface.php b/system/Database/ResultInterface.php index e6383cab..89febfef 100644 --- a/system/Database/ResultInterface.php +++ b/system/Database/ResultInterface.php @@ -61,12 +61,10 @@ public function getResultObject(): array; * * @template T of object * - * @param int|string $n The index of the results to return, or column name. - * @param string $type The type of result object. 'array', 'object' or class name. - * @phpstan-param class-string|'array'|'object' $type + * @param int|string $n The index of the results to return, or column name. + * @param 'array'|'object'|class-string $type The type of result object. 'array', 'object' or class name. * - * @return array|float|int|object|stdClass|string|null - * @phpstan-return ($n is string ? float|int|string|null : ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null))) + * @return ($n is string ? float|int|string|null : ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null))) */ public function getRow($n = 0, string $type = 'object'); @@ -77,11 +75,10 @@ public function getRow($n = 0, string $type = 'object'); * * @template T of object * - * @param int $n The index of the results to return. - * @phpstan-param class-string $className + * @param int $n The index of the results to return. + * @param class-string $className * - * @return object|null - * @phpstan-return T|null + * @return T|null */ public function getCustomRowObject(int $n, string $className); diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 7b1da6e8..f35d8e7b 100644 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -180,8 +180,7 @@ protected function _createTableAttributes(array $attributes): string * @param array|string $processedFields Processed column definitions * or column names to DROP * - * @return false|list|string SQL string or false - * @phpstan-return ($alterType is 'DROP' ? string : list|false) + * @return ($alterType is 'DROP' ? string : false|list) */ protected function _alterTable(string $alterType, string $table, $processedFields) { diff --git a/system/Database/SQLite3/Builder.php b/system/Database/SQLite3/Builder.php index 3aa13ede..4f8dff97 100644 --- a/system/Database/SQLite3/Builder.php +++ b/system/Database/SQLite3/Builder.php @@ -49,7 +49,7 @@ class Builder extends BaseBuilder ]; /** - * @var array + * @var array */ protected $supportedIgnoreStatements = [ 'insert' => 'OR IGNORE', diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index 31c2a7ed..2bb9386f 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -139,9 +139,7 @@ public function dropColumn(string $table, $columnNames): bool * @param array|string $processedFields Processed column definitions * or column names to DROP * - * @return array|string|null - * @return list|string|null SQL string or null - * @phpstan-return ($alterType is 'DROP' ? string : list|null) + * @return ($alterType is 'DROP' ? string : list|null) */ protected function _alterTable(string $alterType, string $table, $processedFields) { diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 674d26b7..ea84e001 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -376,8 +376,7 @@ protected function copyData() * * @param array|bool $fields * - * @return mixed - * @phpstan-return ($fields is array ? array : mixed) + * @return ($fields is array ? array : mixed) */ protected function formatFields($fields) { diff --git a/system/Debug/Toolbar/Collectors/Logs.php b/system/Debug/Toolbar/Collectors/Logs.php index f36dfb1d..c6e22fc0 100644 --- a/system/Debug/Toolbar/Collectors/Logs.php +++ b/system/Debug/Toolbar/Collectors/Logs.php @@ -45,12 +45,14 @@ class Logs extends BaseCollector /** * Our collected data. * - * @var array + * @var list */ protected $data; /** - * Returns the data of this collector to be formatted in the toolbar + * Returns the data of this collector to be formatted in the toolbar. + * + * @return array{logs: list} */ public function display(): array { @@ -66,7 +68,7 @@ public function isEmpty(): bool { $this->collectLogs(); - return empty($this->data); + return $this->data !== []; } /** @@ -82,14 +84,18 @@ public function icon(): string /** * Ensures the data has been collected. * - * @return array + * @return list */ protected function collectLogs() { - if (! empty($this->data)) { + if ($this->data !== []) { return $this->data; } - return $this->data = service('logger', true)->logCache ?? []; + $cache = service('logger')->logCache; + + $this->data = $cache ?? []; + + return $this->data; } } diff --git a/system/Email/Email.php b/system/Email/Email.php index 2a0facc0..3e4cd567 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -68,7 +68,7 @@ class Email /** * Which method to use for sending e-mails. * - * @var string 'mail', 'sendmail' or 'smtp' + * @var 'mail'|'sendmail'|'smtp' */ public $protocol = 'mail'; @@ -117,9 +117,11 @@ class Email /** * SMTP Encryption * - * @var string '', 'tls' or 'ssl'. 'tls' will issue a STARTTLS command - * to the server. 'ssl' means implicit SSL. Connection on port - * 465 should set this to ''. + * * `tls` - will issue a STARTTLS command to the server + * * `ssl` - means implicit SSL + * * `''` - for connection on port 465 + * + * @var ''|'ssl'|'tls' */ public $SMTPCrypto = ''; @@ -142,7 +144,7 @@ class Email /** * Message format. * - * @var string 'text' or 'html' + * @var 'html'|'text' */ public $mailType = 'text'; @@ -170,7 +172,7 @@ class Email /** * X-Priority header value. * - * @var int 1-5 + * @var int<1, 5> */ public $priority = 3; @@ -180,7 +182,7 @@ class Email * * @see http://www.ietf.org/rfc/rfc822.txt * - * @var string "\r\n" or "\n" + * @var "\r\n"|"n" */ public $newline = "\r\n"; @@ -195,7 +197,7 @@ class Email * * @see http://www.ietf.org/rfc/rfc822.txt * - * @var string + * @var "\r\n"|"n" */ public $CRLF = "\r\n"; @@ -268,7 +270,7 @@ class Email /** * Mail encoding * - * @var string '8bit' or '7bit' + * @var '7bit'|'8bit' */ protected $encoding = '8bit'; @@ -342,7 +344,7 @@ class Email * * @see Email::$protocol * - * @var array + * @var list */ protected $protocols = [ 'mail', @@ -368,7 +370,7 @@ class Email * * @see Email::$encoding * - * @var array + * @var list */ protected $bitDepths = [ '7bit', @@ -380,7 +382,7 @@ class Email * * Actual values to send with the X-Priority header * - * @var array + * @var array */ protected $priorities = [ 1 => '1 (Highest)', @@ -414,7 +416,7 @@ public function __construct($config = null) * * @param array|\Config\Email|null $config * - * @return Email + * @return $this */ public function initialize($config) { @@ -445,7 +447,7 @@ public function initialize($config) /** * @param bool $clearAttachments * - * @return Email + * @return $this */ public function clear($clearAttachments = false) { @@ -463,7 +465,7 @@ public function clear($clearAttachments = false) $this->setHeader('Date', $this->setDate()); - if ($clearAttachments !== false) { + if ($clearAttachments) { $this->attachments = []; } @@ -473,13 +475,13 @@ public function clear($clearAttachments = false) /** * @param string $from * @param string $name - * @param string|null $returnPath Return-Path + * @param string|null $returnPath * - * @return Email + * @return $this */ public function setFrom($from, $name = '', $returnPath = null) { - if (preg_match('/\<(.*)\>/', $from, $match)) { + if (preg_match('/\<(.*)\>/', $from, $match) === 1) { $from = $match[1]; } @@ -504,9 +506,8 @@ public function setFrom($from, $name = '', $returnPath = null) } $this->setHeader('From', $name . ' <' . $from . '>'); - if (! isset($returnPath)) { - $returnPath = $from; - } + $returnPath ??= $from; + $this->setHeader('Return-Path', '<' . $returnPath . '>'); $this->tmpArchive['returnPath'] = $returnPath; @@ -517,11 +518,11 @@ public function setFrom($from, $name = '', $returnPath = null) * @param string $replyto * @param string $name * - * @return Email + * @return $this */ public function setReplyTo($replyto, $name = '') { - if (preg_match('/\<(.*)\>/', $replyto, $match)) { + if (preg_match('/\<(.*)\>/', $replyto, $match) === 1) { $replyto = $match[1]; } @@ -550,7 +551,7 @@ public function setReplyTo($replyto, $name = '') /** * @param array|string $to * - * @return Email + * @return $this */ public function setTo($to) { @@ -573,7 +574,7 @@ public function setTo($to) /** * @param string $cc * - * @return Email + * @return $this */ public function setCC($cc) { @@ -598,7 +599,7 @@ public function setCC($cc) * @param string $bcc * @param string $limit * - * @return Email + * @return $this */ public function setBCC($bcc, $limit = '') { @@ -626,7 +627,7 @@ public function setBCC($bcc, $limit = '') /** * @param string $subject * - * @return Email + * @return $this */ public function setSubject($subject) { @@ -641,7 +642,7 @@ public function setSubject($subject) /** * @param string $body * - * @return Email + * @return $this */ public function setMessage($body) { @@ -734,7 +735,7 @@ public function setAttachmentCID($filename) * @param string $header * @param string $value * - * @return Email + * @return $this */ public function setHeader($header, $value) { @@ -744,14 +745,16 @@ public function setHeader($header, $value) } /** - * @param array|string $email + * @param list|string $email * - * @return array + * @return list */ protected function stringToArray($email) { if (! is_array($email)) { - return (str_contains($email, ',')) ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY) : (array) trim($email); + return str_contains($email, ',') + ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY) + : (array) trim($email); } return $email; @@ -760,7 +763,7 @@ protected function stringToArray($email) /** * @param string $str * - * @return Email + * @return $this */ public function setAltMessage($str) { @@ -772,11 +775,11 @@ public function setAltMessage($str) /** * @param string $type * - * @return Email + * @return $this */ public function setMailType($type = 'text') { - $this->mailType = ($type === 'html') ? 'html' : 'text'; + $this->mailType = $type === 'html' ? 'html' : 'text'; return $this; } @@ -784,7 +787,7 @@ public function setMailType($type = 'text') /** * @param bool $wordWrap * - * @return Email + * @return $this */ public function setWordWrap($wordWrap = true) { @@ -796,7 +799,7 @@ public function setWordWrap($wordWrap = true) /** * @param string $protocol * - * @return Email + * @return $this */ public function setProtocol($protocol = 'mail') { @@ -808,7 +811,7 @@ public function setProtocol($protocol = 'mail') /** * @param int $n * - * @return Email + * @return $this */ public function setPriority($n = 3) { @@ -820,7 +823,7 @@ public function setPriority($n = 3) /** * @param string $newline * - * @return Email + * @return $this */ public function setNewline($newline = "\n") { @@ -832,11 +835,11 @@ public function setNewline($newline = "\n") /** * @param string $CRLF * - * @return Email + * @return $this */ public function setCRLF($CRLF = "\n") { - $this->CRLF = ($CRLF !== "\n" && $CRLF !== "\r\n" && $CRLF !== "\r") ? "\n" : $CRLF; + $this->CRLF = ! in_array($CRLF, ["\n", "\r\n", "\r"], true) ? "\n" : $CRLF; return $this; } @@ -891,10 +894,10 @@ protected function getEncoding() protected function getContentType() { if ($this->mailType === 'html') { - return empty($this->attachments) ? 'html' : 'html-attach'; + return $this->attachments === [] ? 'html' : 'html-attach'; } - if ($this->mailType === 'text' && ! empty($this->attachments)) { + if ($this->mailType === 'text' && $this->attachments !== []) { return 'plain-attach'; } @@ -996,8 +999,8 @@ public function cleanEmail($email) */ protected function getAltMessage() { - if (! empty($this->altMessage)) { - return ($this->wordWrap) ? $this->wordWrap($this->altMessage, 76) : $this->altMessage; + if ($this->altMessage !== '') { + return $this->wordWrap ? $this->wordWrap($this->altMessage, 76) : $this->altMessage; } $body = preg_match('/\(.*)\<\/body\>/si', $this->body, $match) ? $match[1] : $this->body; @@ -1009,7 +1012,7 @@ protected function getAltMessage() $body = preg_replace('| +|', ' ', $body); - return ($this->wordWrap) ? $this->wordWrap($body, 76) : $body; + return $this->wordWrap ? $this->wordWrap($body, 76) : $body; } /** @@ -1020,8 +1023,10 @@ protected function getAltMessage() */ public function wordWrap($str, $charlim = null) { - if (empty($charlim)) { - $charlim = empty($this->wrapChars) ? 76 : $this->wrapChars; + $charlim ??= 0; + + if ($charlim === 0) { + $charlim = $this->wrapChars === 0 ? 76 : $this->wrapChars; } if (str_contains($str, "\r")) { @@ -1269,13 +1274,13 @@ protected function buildMessage() } /** - * @param mixed $type + * @param string $type * * @return bool */ protected function attachmentsHaveMultipart($type) { - foreach ($this->attachments as &$attachment) { + foreach ($this->attachments as $attachment) { if ($attachment['multipart'] === $type) { return true; } @@ -1303,14 +1308,14 @@ protected function appendAttachments(&$body, $boundary, $multipart = null) . 'Content-Type: ' . $attachment['type'] . '; name="' . $name . '"' . $this->newline . 'Content-Disposition: ' . $attachment['disposition'] . ';' . $this->newline . 'Content-Transfer-Encoding: base64' . $this->newline - . (empty($attachment['cid']) ? '' : 'Content-ID: <' . $attachment['cid'] . '>' . $this->newline) + . ($attachment['cid'] === '' ? '' : 'Content-ID: <' . $attachment['cid'] . '>' . $this->newline) . $this->newline . $attachment['content'] . $this->newline; } // $name won't be set if no attachments were appended, // and therefore a boundary wouldn't be necessary - if (! empty($name)) { + if (isset($name)) { $body .= '--' . $boundary . '--'; } } @@ -2137,12 +2142,16 @@ protected function getSMTPData() */ protected function getHostname() { - if (isset($_SERVER['SERVER_NAME'])) { - return $_SERVER['SERVER_NAME']; + $superglobals = service('superglobals'); + + $serverName = $superglobals->server('SERVER_NAME'); + if (! in_array($serverName, [null, ''], true)) { + return $serverName; } - if (isset($_SERVER['SERVER_ADDR'])) { - return '[' . $_SERVER['SERVER_ADDR'] . ']'; + $serverAddr = $superglobals->server('SERVER_ADDR'); + if (! in_array($serverAddr, [null, ''], true)) { + return '[' . $serverAddr . ']'; } $hostname = gethostname(); @@ -2218,7 +2227,7 @@ protected function mimeTypes($ext = '') public function __destruct() { - if (is_resource($this->SMTPConnect)) { + if ($this->SMTPConnect !== null) { try { $this->sendCommand('quit'); } catch (ErrorException $e) { diff --git a/system/Events/Events.php b/system/Events/Events.php index ebe13d86..ae68e205 100644 --- a/system/Events/Events.php +++ b/system/Events/Events.php @@ -29,7 +29,7 @@ class Events /** * The list of listeners. * - * @var array + * @var array, 2: list}> */ protected static $listeners = []; @@ -53,7 +53,7 @@ class Events * Stores information about the events * for display in the debug toolbar. * - * @var list> + * @var list */ protected static $performanceLog = []; @@ -84,15 +84,12 @@ public static function initialize() $files = service('locator')->search('Config/Events.php'); } - $files = array_filter(array_map(static function (string $file): false|string { - if (is_file($file)) { - return realpath($file) ?: $file; - } - - return false; // @codeCoverageIgnore - }, $files)); + $files = array_filter(array_map( + static fn (string $file): false|string => realpath($file), + $files, + )); - static::$files = array_unique(array_merge($files, [$events])); + static::$files = array_values(array_unique(array_merge($files, [$events]))); foreach (static::$files as $file) { include $file; @@ -110,9 +107,9 @@ public static function initialize() * Events::on('create', [$myInstance, 'myMethod']); // Method on an existing instance * Events::on('create', function() {}); // Closure * - * @param string $eventName - * @param callable $callback - * @param int $priority + * @param string $eventName + * @param callable(mixed): mixed $callback + * @param int $priority * * @return void */ @@ -138,7 +135,7 @@ public static function on($eventName, $callback, $priority = self::PRIORITY_NORM * b) a method returns false, at which point execution of subscribers stops. * * @param string $eventName - * @param mixed $arguments + * @param mixed ...$arguments */ public static function trigger($eventName, ...$arguments): bool { @@ -175,6 +172,8 @@ public static function trigger($eventName, ...$arguments): bool * sorted by priority. * * @param string $eventName + * + * @return list */ public static function listeners($eventName): array { @@ -200,7 +199,8 @@ public static function listeners($eventName): array * If the listener couldn't be found, returns FALSE, else TRUE if * it was removed. * - * @param string $eventName + * @param string $eventName + * @param callable(mixed): mixed $listener */ public static function removeListener($eventName, callable $listener): bool { @@ -244,6 +244,8 @@ public static function removeAllListeners($eventName = null) /** * Sets the path to the file that routes are read from. * + * @param list $files + * * @return void */ public static function setFiles(array $files) @@ -276,7 +278,7 @@ public static function simulate(bool $choice = true) /** * Getter for the performance log records. * - * @return list> + * @return list */ public static function getPerformanceLogs() { diff --git a/system/Files/File.php b/system/Files/File.php index 23eb324d..be7f84f8 100644 --- a/system/Files/File.php +++ b/system/Files/File.php @@ -73,7 +73,7 @@ public function getSize() /** * Retrieve the file size by unit, calculated in IEC standards with 1024 as base value. * - * @phpstan-param positive-int $precision + * @param positive-int $precision */ public function getSizeByBinaryUnit(FileSizeUnit $unit = FileSizeUnit::B, int $precision = 3): int|string { @@ -83,7 +83,7 @@ public function getSizeByBinaryUnit(FileSizeUnit $unit = FileSizeUnit::B, int $p /** * Retrieve the file size by unit, calculated in metric standards with 1000 as base value. * - * @phpstan-param positive-int $precision + * @param positive-int $precision */ public function getSizeByMetricUnit(FileSizeUnit $unit = FileSizeUnit::B, int $precision = 3): int|string { diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 412a9f45..9a253a42 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -206,8 +206,8 @@ public function setResponse(ResponseInterface $response) * Runs through all the filters (except "Required Filters") for the specified * URI and position. * - * @param string $uri URI path relative to baseURL - * @phpstan-param 'before'|'after' $position + * @param string $uri URI path relative to baseURL + * @param 'after'|'before' $position * * @return RequestInterface|ResponseInterface|string|null * @@ -310,7 +310,7 @@ private function createFilter(string $className): FilterInterface /** * Returns the "Required Filters" class list. * - * @phpstan-param 'before'|'after' $position + * @param 'after'|'before' $position * * @return list}> [[classname, arguments], ...] */ @@ -340,7 +340,7 @@ public function getRequiredClasses(string $position): array /** * Runs "Required Filters" for the specified position. * - * @phpstan-param 'before'|'after' $position + * @param 'after'|'before' $position * * @return RequestInterface|ResponseInterface|string|null * @@ -367,7 +367,7 @@ public function runRequired(string $position = 'before') /** * Returns "Required Filters" for the specified position. * - * @phpstan-param 'before'|'after' $position + * @param 'after'|'before' $position * * @internal */ @@ -544,7 +544,7 @@ public function getFiltersClass(): array * MUST be called prior to initialize(); * Intended for use within routes files. * - * @phpstan-param 'before'|'after' $position + * @param 'after'|'before' $position * * @return $this */ @@ -574,9 +574,9 @@ public function addFilter(string $class, ?string $alias = null, string $position * after the filter name, followed by a comma-separated list of arguments that * are passed to the filter when executed. * - * @param string $filter filter_name or filter_name:arguments like 'role:admin,manager' - * or filter classname. - * @phpstan-param 'before'|'after' $position + * @param string $filter filter_name or filter_name:arguments like 'role:admin,manager' + * or filter classname. + * @param 'after'|'before' $position */ private function enableFilter(string $filter, string $position = 'before'): void { @@ -824,7 +824,7 @@ protected function processFilters(?string $uri = null) /** * Maps filter aliases to the equivalent filter classes * - * @phpstan-param 'before'|'after' $position + * @param 'after'|'before' $position * * @return void * diff --git a/system/Format/Exceptions/FormatException.php b/system/Format/Exceptions/FormatException.php index 46daad55..cde333b1 100644 --- a/system/Format/Exceptions/FormatException.php +++ b/system/Format/Exceptions/FormatException.php @@ -37,7 +37,7 @@ public static function forInvalidFormatter(string $class) * Thrown in JSONFormatter when the json_encode produces * an error code other than JSON_ERROR_NONE and JSON_ERROR_RECURSION. * - * @param string $error The error message + * @param string|null $error The error message * * @return static */ diff --git a/system/Format/Format.php b/system/Format/Format.php index 4a9cb609..6100b1a5 100644 --- a/system/Format/Format.php +++ b/system/Format/Format.php @@ -23,19 +23,8 @@ */ class Format { - /** - * Configuration instance - * - * @var FormatConfig - */ - protected $config; - - /** - * Constructor. - */ - public function __construct(FormatConfig $config) + public function __construct(protected FormatConfig $config) { - $this->config = $config; } /** diff --git a/system/Format/FormatterInterface.php b/system/Format/FormatterInterface.php index 0f005568..0c2492b9 100644 --- a/system/Format/FormatterInterface.php +++ b/system/Format/FormatterInterface.php @@ -21,9 +21,9 @@ interface FormatterInterface /** * Takes the given data and formats it. * - * @param array|object|string $data + * @param array|object|string $data * - * @return false|string + * @return false|non-empty-string */ public function format($data); } diff --git a/system/Format/JSONFormatter.php b/system/Format/JSONFormatter.php index 7d2ad525..a6ba87cd 100644 --- a/system/Format/JSONFormatter.php +++ b/system/Format/JSONFormatter.php @@ -26,9 +26,9 @@ class JSONFormatter implements FormatterInterface /** * Takes the given data and formats it. * - * @param array|bool|float|int|object|string|null $data + * @param array|object|string $data * - * @return false|string (JSON string | false) + * @return false|non-empty-string */ public function format($data) { @@ -37,7 +37,9 @@ public function format($data) $options = $config->formatterOptions['application/json'] ?? JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES; $options |= JSON_PARTIAL_OUTPUT_ON_ERROR; - $options = ENVIRONMENT === 'production' ? $options : $options | JSON_PRETTY_PRINT; + if (ENVIRONMENT !== 'production') { + $options |= JSON_PRETTY_PRINT; + } $result = json_encode($data, $options, 512); diff --git a/system/Format/XMLFormatter.php b/system/Format/XMLFormatter.php index c85eae5f..efef70ce 100644 --- a/system/Format/XMLFormatter.php +++ b/system/Format/XMLFormatter.php @@ -27,9 +27,9 @@ class XMLFormatter implements FormatterInterface /** * Takes the given data and formats it. * - * @param array|bool|float|int|object|string|null $data + * @param array|object|string $data * - * @return false|string (XML string | false) + * @return false|non-empty-string */ public function format($data) { @@ -56,7 +56,8 @@ public function format($data) * * @see http://www.codexworld.com/convert-array-to-xml-in-php/ * - * @param SimpleXMLElement $output + * @param array $data + * @param SimpleXMLElement $output * * @return void */ diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 6ef97170..bde4e1b8 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -314,9 +314,6 @@ public function getLocale(): string /** * Checks this request type. - * - * @param string $type HTTP verb or 'json' or 'ajax' - * @phpstan-param string|'get'|'post'|'put'|'delete'|'head'|'patch'|'options'|'json'|'ajax' $type */ public function is(string $type): bool { diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index 8a21d772..5e8c5847 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -383,22 +383,8 @@ public function send(string $method, string $url) // Set the string we want to break our response from $breakString = "\r\n\r\n"; - if (isset($this->config['allow_redirects']) && $this->config['allow_redirects'] !== false) { - $output = $this->handleRedirectHeaders($output, $breakString); - } - - while (str_starts_with($output, 'HTTP/1.1 100 Continue')) { - $output = substr($output, strpos($output, $breakString) + 4); - } - - if (preg_match('/HTTP\/\d\.\d 200 Connection established/i', $output)) { - $output = substr($output, strpos($output, $breakString) + 4); - } - - // If request and response have Digest - if (isset($this->config['auth'][2]) && $this->config['auth'][2] === 'digest' && str_contains($output, 'WWW-Authenticate: Digest')) { - $output = substr($output, strpos($output, $breakString) + 4); - } + // Remove all intermediate responses + $output = $this->removeIntermediateResponses($output, $breakString); // Split out our headers and body $break = strpos($output, $breakString); @@ -696,6 +682,8 @@ protected function setCURLOptions(array $curlOptions = [], array $config = []) * Does the actual work of initializing cURL, setting the options, * and grabbing the output. * + * @param array $curlOptions + * * @codeCoverageIgnore */ protected function sendRequest(array $curlOptions = []): string @@ -716,29 +704,71 @@ protected function sendRequest(array $curlOptions = []): string return $output; } - private function handleRedirectHeaders(string $output, string $breakString): string + private function removeIntermediateResponses(string $output, string $breakString): string { - // Strip out multiple redirect header sections - while (preg_match('/^HTTP\/\d(?:\.\d)? 3\d\d/', $output)) { - $breakStringPos = strpos($output, $breakString); - $redirectHeaderSection = substr($output, 0, $breakStringPos); - $redirectHeaders = explode("\n", $redirectHeaderSection); - $locationHeaderFound = false; - - foreach ($redirectHeaders as $header) { - if (str_starts_with(strtolower($header), 'location:')) { - $locationHeaderFound = true; - break; + while (true) { + // Check if we should remove the current response + if ($this->shouldRemoveCurrentResponse($output, $breakString)) { + $breakStringPos = strpos($output, $breakString); + if ($breakStringPos !== false) { + $output = substr($output, $breakStringPos + 4); + + continue; } } - if ($locationHeaderFound) { - $output = substr($output, $breakStringPos + 4); - } else { - break; - } + // No more intermediate responses to remove + break; } return $output; } + + /** + * Check if the current response (at the beginning of output) should be removed. + */ + private function shouldRemoveCurrentResponse(string $output, string $breakString): bool + { + // HTTP/x.x 1xx responses (Continue, Processing, etc.) + if (preg_match('/^HTTP\/\d+(?:\.\d+)?\s+1\d\d\s/', $output)) { + return true; + } + + // HTTP/x.x 200 Connection established (proxy responses) + if (preg_match('/^HTTP\/\d+(?:\.\d+)?\s+200\s+Connection\s+established/i', $output)) { + return true; + } + + // HTTP/x.x 3xx responses (redirects) - only if redirects are allowed + $allowRedirects = isset($this->config['allow_redirects']) && $this->config['allow_redirects'] !== false; + if ($allowRedirects && preg_match('/^HTTP\/\d+(?:\.\d+)?\s+3\d\d\s/', $output)) { + // Check if there's a Location header + $breakStringPos = strpos($output, $breakString); + if ($breakStringPos !== false) { + $headerSection = substr($output, 0, $breakStringPos); + $headers = explode("\n", $headerSection); + + foreach ($headers as $header) { + if (str_starts_with(strtolower($header), 'location:')) { + return true; // Found location header, this is a redirect to remove + } + } + } + } + + // Digest auth challenges - only remove if there's another response after + if (isset($this->config['auth'][2]) && $this->config['auth'][2] === 'digest') { + $breakStringPos = strpos($output, $breakString); + if ($breakStringPos !== false) { + $headerSection = substr($output, 0, $breakStringPos); + if (str_contains($headerSection, 'WWW-Authenticate: Digest')) { + $nextBreakPos = strpos($output, $breakString, $breakStringPos + 4); + + return $nextBreakPos !== false; // Only remove if there's another response + } + } + } + + return false; + } } diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index a0da2e8c..f6861d54 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -182,22 +182,21 @@ private function getDownloadFileName(): string } /** - * get Content-Disposition Header string. + * Get Content-Disposition Header string. */ - private function getContentDisposition(): string + private function getContentDisposition(bool $inline = false): string { - $downloadFilename = $this->getDownloadFileName(); - - $utf8Filename = $downloadFilename; + $downloadFilename = $utf8Filename = $this->getDownloadFileName(); + $disposition = $inline ? 'inline' : 'attachment'; if (strtoupper($this->charset) !== 'UTF-8') { $utf8Filename = mb_convert_encoding($downloadFilename, 'UTF-8', $this->charset); } - $result = sprintf('attachment; filename="%s"', $downloadFilename); + $result = sprintf('%s; filename="%s"', $disposition, addslashes($downloadFilename)); if ($utf8Filename !== '') { - $result .= '; filename*=UTF-8\'\'' . rawurlencode($utf8Filename); + $result .= sprintf('; filename*=UTF-8\'\'%s', rawurlencode($utf8Filename)); } return $result; @@ -341,7 +340,7 @@ private function sendBodyByBinary() */ public function inline() { - $this->setHeader('Content-Disposition', 'inline'); + $this->setHeader('Content-Disposition', $this->getContentDisposition(true)); return $this; } diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index ba6f61d7..72b1ce0c 100644 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -348,9 +348,6 @@ public function negotiate(string $type, array $supported, bool $strictMatch = fa /** * Checks this request type. - * - * @param string $type HTTP verb or 'json' or 'ajax' - * @phpstan-param string|'get'|'post'|'put'|'delete'|'head'|'patch'|'options'|'json'|'ajax' $type */ public function is(string $type): bool { diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index 39f4269e..3c3da161 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -224,9 +224,8 @@ public function getEnv($index = null, $filter = null, $flags = null) /** * Allows manually setting the value of PHP global, like $_GET, $_POST, etc. * - * @param string $name Supergrlobal name (lowercase) - * @phpstan-param 'get'|'post'|'request'|'cookie'|'server' $name - * @param mixed $value + * @param 'cookie'|'get'|'post'|'request'|'server' $name Superglobal name (lowercase) + * @param mixed $value * * @return $this */ @@ -247,11 +246,10 @@ public function setGlobal(string $name, $value) * * http://php.net/manual/en/filter.filters.sanitize.php * - * @param string $name Supergrlobal name (lowercase) - * @phpstan-param 'get'|'post'|'request'|'cookie'|'server' $name - * @param array|int|string|null $index - * @param int|null $filter Filter constant - * @param array|int|null $flags Options + * @param 'cookie'|'get'|'post'|'request'|'server' $name Superglobal name (lowercase) + * @param array|int|string|null $index + * @param int|null $filter Filter constant + * @param array|int|null $flags Options * * @return array|bool|float|int|object|string|null */ @@ -341,8 +339,7 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f * Saves a copy of the current state of one of several PHP globals, * so we can retrieve them later. * - * @param string $name Superglobal name (lowercase) - * @phpstan-param 'get'|'post'|'request'|'cookie'|'server' $name + * @param 'cookie'|'get'|'post'|'request'|'server' $name Superglobal name (lowercase) * * @return void */ diff --git a/system/HTTP/SiteURI.php b/system/HTTP/SiteURI.php index 6e5206a0..d6653b10 100644 --- a/system/HTTP/SiteURI.php +++ b/system/HTTP/SiteURI.php @@ -85,11 +85,10 @@ class SiteURI extends URI private string $routePath; /** - * @param string $relativePath URI path relative to baseURL. May include - * queries or fragments. - * @param string|null $host Optional current hostname. - * @param string|null $scheme Optional scheme. 'http' or 'https'. - * @phpstan-param 'http'|'https'|null $scheme + * @param string $relativePath URI path relative to baseURL. May include + * queries or fragments. + * @param string|null $host Optional current hostname. + * @param 'http'|'https'|null $scheme Optional scheme. 'http' or 'https'. */ public function __construct( App $configApp, diff --git a/system/HTTP/SiteURIFactory.php b/system/HTTP/SiteURIFactory.php index c87898c0..483c3ded 100644 --- a/system/HTTP/SiteURIFactory.php +++ b/system/HTTP/SiteURIFactory.php @@ -132,7 +132,8 @@ private function parseRequestURI(): string && pathinfo($this->superglobals->server('SCRIPT_NAME'), PATHINFO_EXTENSION) === 'php' ) { // Compare each segment, dropping them until there is no match - $segments = $keep = explode('/', $path); + $segments = explode('/', rawurldecode($path)); + $keep = explode('/', $path); foreach (explode('/', $this->superglobals->server('SCRIPT_NAME')) as $i => $segment) { // If these segments are not the same then we're done @@ -146,6 +147,15 @@ private function parseRequestURI(): string $path = implode('/', $keep); } + // Cleanup: if indexPage is still visible in the path, remove it + if ($this->appConfig->indexPage !== '' && str_starts_with($path, $this->appConfig->indexPage)) { + $remainingPath = substr($path, strlen($this->appConfig->indexPage)); + // Only remove if followed by '/' (route) or nothing (root) + if ($remainingPath === '' || str_starts_with($remainingPath, '/')) { + $path = ltrim($remainingPath, '/'); + } + } + // This section ensures that even on servers that require the URI to // contain the query string (Nginx) a correct URI is found, and also // fixes the QUERY_STRING Server var and $_GET array. diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index b714995c..5bcf11de 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -376,7 +376,7 @@ public function getAuthority(bool $ignorePort = false): string } // Don't add port if it's a standard port for this scheme - if ((int) $this->port !== 0 && ! $ignorePort && $this->port !== $this->defaultPorts[$this->scheme]) { + if ((int) $this->port !== 0 && ! $ignorePort && $this->port !== ($this->defaultPorts[$this->scheme] ?? null)) { $authority .= ':' . $this->port; } diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index aa7efbea..e7bd3544 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -255,6 +255,14 @@ function get_filenames( * @param string $sourceDir Path to source * @param bool $topLevelOnly Look only at the top level directory specified? * @param bool $recursion Internal variable to determine recursion status - do not use in calls + * + * @return array */ function get_dir_file_info(string $sourceDir, bool $topLevelOnly = true, bool $recursion = false): array { @@ -298,10 +306,19 @@ function get_dir_file_info(string $sourceDir, bool $topLevelOnly = true, bool $r * Options are: name, server_path, size, date, readable, writable, executable, fileperms * Returns false if the file cannot be found. * - * @param string $file Path to file - * @param array|string $returnedValues Array or comma separated string of information returned + * @param string $file Path to file + * @param list|string $returnedValues Array or comma separated string of information returned * - * @return array|null + * @return array{ + * name?: string, + * server_path?: string, + * size?: int, + * date?: int, + * readable?: bool, + * writable?: bool, + * executable?: bool, + * fileperms?: int + * }|null */ function get_file_info(string $file, $returnedValues = ['name', 'server_path', 'size', 'date']) { diff --git a/system/Helpers/security_helper.php b/system/Helpers/security_helper.php index d42ac0dd..d73fd621 100644 --- a/system/Helpers/security_helper.php +++ b/system/Helpers/security_helper.php @@ -15,11 +15,69 @@ if (! function_exists('sanitize_filename')) { /** - * Sanitize a filename to use in a URI. + * Sanitize Filename + * + * Tries to sanitize filenames in order to prevent directory traversal attempts + * and other security threats, which is particularly useful for files that + * were supplied via user input. + * + * 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, $relativePath to TRUE. + * + * @param string $filename Input file name + * @param bool $relativePath Whether to preserve paths */ - function sanitize_filename(string $filename): string + function sanitize_filename(string $filename, bool $relativePath = false): string { - return service('security')->sanitizeFilename($filename); + // List of sanitized filename strings + $bad = [ + '../', + '', + '<', + '>', + "'", + '"', + '&', + '$', + '#', + '{', + '}', + '[', + ']', + '=', + ';', + '?', + '%20', + '%22', + '%3c', + '%253c', + '%3e', + '%0e', + '%28', + '%29', + '%2528', + '%26', + '%24', + '%3f', + '%3b', + '%3d', + ]; + + if (! $relativePath) { + $bad[] = './'; + $bad[] = '/'; + } + + $filename = remove_invisible_characters($filename, false); + + do { + $old = $filename; + $filename = str_replace($bad, '', $filename); + } while ($old !== $filename); + + return stripslashes($filename); } } diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index f1448efa..233ea2d0 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -82,8 +82,8 @@ public function _resize(bool $maintainRatio = false) } $action = $maintainRatio - ? ' -resize ' . ($this->width ?? 0) . 'x' . ($this->height ?? 0) . ' "' . $source . '" "' . $destination . '"' - : ' -resize ' . ($this->width ?? 0) . 'x' . ($this->height ?? 0) . "{$escape}! \"" . $source . '" "' . $destination . '"'; + ? ' -resize ' . ($this->width ?? 0) . 'x' . ($this->height ?? 0) . ' ' . escapeshellarg($source) . ' ' . escapeshellarg($destination) + : ' -resize ' . ($this->width ?? 0) . 'x' . ($this->height ?? 0) . "{$escape}! " . escapeshellarg($source) . ' ' . escapeshellarg($destination); $this->process($action); @@ -354,7 +354,7 @@ protected function _text(string $text, array $options = []) // Font if (! empty($options['fontPath'])) { - $cmd .= " -font '{$options['fontPath']}'"; + $cmd .= ' -font ' . escapeshellarg($options['fontPath']); } if (isset($options['hAlign'], $options['vAlign'])) { @@ -393,28 +393,28 @@ protected function _text(string $text, array $options = []) $xAxis = $xAxis >= 0 ? '+' . $xAxis : $xAxis; $yAxis = $yAxis >= 0 ? '+' . $yAxis : $yAxis; - $cmd .= " -gravity {$gravity} -geometry {$xAxis}{$yAxis}"; + $cmd .= ' -gravity ' . escapeshellarg($gravity) . ' -geometry ' . escapeshellarg("{$xAxis}{$yAxis}"); } // Color if (isset($options['color'])) { [$r, $g, $b] = sscanf("#{$options['color']}", '#%02x%02x%02x'); - $cmd .= " -fill 'rgba({$r},{$g},{$b},{$options['opacity']})'"; + $cmd .= ' -fill ' . escapeshellarg("rgba({$r},{$g},{$b},{$options['opacity']})"); } // Font Size - use points.... if (isset($options['fontSize'])) { - $cmd .= " -pointsize {$options['fontSize']}"; + $cmd .= ' -pointsize ' . escapeshellarg((string) $options['fontSize']); } // Text - $cmd .= " -annotate 0 '{$text}'"; + $cmd .= ' -annotate 0 ' . escapeshellarg($text); $source = ! empty($this->resource) ? $this->resource : $this->image()->getPathname(); $destination = $this->getResourcePath(); - $cmd = " '{$source}' {$cmd} '{$destination}'"; + $cmd = ' ' . escapeshellarg($source) . ' ' . $cmd . ' ' . escapeshellarg($destination); $this->process($cmd); } diff --git a/system/Log/Handlers/BaseHandler.php b/system/Log/Handlers/BaseHandler.php index 34f9fe2e..2b82f0f4 100644 --- a/system/Log/Handlers/BaseHandler.php +++ b/system/Log/Handlers/BaseHandler.php @@ -21,7 +21,7 @@ abstract class BaseHandler implements HandlerInterface /** * Handles * - * @var array + * @var list */ protected $handles; @@ -33,7 +33,7 @@ abstract class BaseHandler implements HandlerInterface protected $dateFormat = 'Y-m-d H:i:s'; /** - * Constructor + * @param array{handles?: list} $config */ public function __construct(array $config) { diff --git a/system/Log/Handlers/ChromeLoggerHandler.php b/system/Log/Handlers/ChromeLoggerHandler.php index 5813f338..8d763399 100644 --- a/system/Log/Handlers/ChromeLoggerHandler.php +++ b/system/Log/Handlers/ChromeLoggerHandler.php @@ -16,8 +16,6 @@ use CodeIgniter\HTTP\ResponseInterface; /** - * Class ChromeLoggerHandler - * * Allows for logging items to the Chrome console for debugging. * Requires the ChromeLogger extension installed in your browser. * @@ -41,7 +39,16 @@ class ChromeLoggerHandler extends BaseHandler /** * The final data that is sent to the browser. * - * @var array + * @var array{ + * version: float, + * columns: list, + * rows: list, + * 1: string, + * 2: string, + * }>, + * request_uri?: string, + * } */ protected $json = [ 'version' => self::VERSION, @@ -63,7 +70,7 @@ class ChromeLoggerHandler extends BaseHandler /** * Maps the log levels to the ChromeLogger types. * - * @var array + * @var array */ protected $levels = [ 'emergency' => 'error', @@ -77,7 +84,7 @@ class ChromeLoggerHandler extends BaseHandler ]; /** - * Constructor + * @param array{handles?: list} $config */ public function __construct(array $config = []) { @@ -97,10 +104,8 @@ public function __construct(array $config = []) */ public function handle($level, $message): bool { - // Format our message $message = $this->format($message); - // Generate Backtrace info $backtrace = debug_backtrace(0, $this->backtraceLevel); $backtrace = end($backtrace); @@ -116,11 +121,7 @@ public function handle($level, $message): bool $type = $this->levels[$level]; } - $this->json['rows'][] = [ - [$message], - $backtraceMessage, - $type, - ]; + $this->json['rows'][] = [[$message], $backtraceMessage, $type]; $this->sendLogs(); @@ -130,9 +131,9 @@ public function handle($level, $message): bool /** * Converts the object to display nicely in the Chrome Logger UI. * - * @param array|int|object|string $object + * @param object|string $object * - * @return array + * @return array|string */ protected function format($object) { diff --git a/system/Log/Handlers/ErrorlogHandler.php b/system/Log/Handlers/ErrorlogHandler.php index 8eee392c..a7e82041 100644 --- a/system/Log/Handlers/ErrorlogHandler.php +++ b/system/Log/Handlers/ErrorlogHandler.php @@ -38,14 +38,14 @@ class ErrorlogHandler extends BaseHandler * Says where the error should go. Currently supported are * 0 (`TYPE_OS`) and 4 (`TYPE_SAPI`). * - * @var int + * @var 0|4 */ protected $messageType = 0; /** * Constructor. * - * @param list $config + * @param array{handles?: list, messageType?: int} $config */ public function __construct(array $config = []) { @@ -79,6 +79,8 @@ public function handle($level, $message): bool /** * Extracted call to `error_log()` in order to be tested. * + * @param 0|4 $messageType + * * @codeCoverageIgnore */ protected function errorLog(string $message, int $messageType): bool diff --git a/system/Log/Handlers/FileHandler.php b/system/Log/Handlers/FileHandler.php index 64d1485f..d3132bf8 100644 --- a/system/Log/Handlers/FileHandler.php +++ b/system/Log/Handlers/FileHandler.php @@ -45,18 +45,22 @@ class FileHandler extends BaseHandler protected $filePermissions; /** - * Constructor + * @param array{handles?: list, path?: string, fileExtension?: string, filePermissions?: int} $config */ public function __construct(array $config = []) { parent::__construct($config); - $this->path = empty($config['path']) ? WRITEPATH . 'logs/' : $config['path']; + $defaults = ['path' => WRITEPATH . 'logs/', 'fileExtension' => 'log', 'filePermissions' => 0644]; + $config = [...$defaults, ...$config]; - $this->fileExtension = empty($config['fileExtension']) ? 'log' : $config['fileExtension']; - $this->fileExtension = ltrim($this->fileExtension, '.'); + $this->path = $config['path'] === '' ? $defaults['path'] : $config['path']; - $this->filePermissions = $config['filePermissions'] ?? 0644; + $this->fileExtension = $config['fileExtension'] === '' + ? $defaults['fileExtension'] + : ltrim($config['fileExtension'], '.'); + + $this->filePermissions = $config['filePermissions']; } /** @@ -108,10 +112,8 @@ public function handle($level, $message): bool for ($written = 0, $length = strlen($msg); $written < $length; $written += $result) { if (($result = fwrite($fp, substr($msg, $written))) === false) { - // if we get this far, we'll never see this during travis-ci - // @codeCoverageIgnoreStart - break; - // @codeCoverageIgnoreEnd + // if we get this far, we'll never see this during unit testing + break; // @codeCoverageIgnore } } diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 7c14fcda..3770e3a6 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -54,11 +54,11 @@ class Logger implements LoggerInterface ]; /** - * Array of levels to be logged. - * The rest will be ignored. - * Set in Config/logger.php + * Array of levels to be logged. The rest will be ignored. * - * @var array + * Set in app/Config/Logger.php + * + * @var list */ protected $loggableLevels = []; @@ -86,7 +86,7 @@ class Logger implements LoggerInterface /** * Caches instances of the handlers. * - * @var array + * @var array, HandlerInterface> */ protected $handlers = []; @@ -96,14 +96,14 @@ class Logger implements LoggerInterface * value is an associative array of configuration * items. * - * @var array|string>> + * @var array, array|string>> */ protected $handlerConfig = []; /** * Caches logging calls for debugbar. * - * @var array + * @var list */ public $logCache; @@ -123,32 +123,34 @@ class Logger implements LoggerInterface */ public function __construct($config, bool $debug = CI_DEBUG) { - $this->loggableLevels = is_array($config->threshold) ? $config->threshold : range(1, (int) $config->threshold); + $loggableLevels = is_array($config->threshold) ? $config->threshold : range(1, (int) $config->threshold); // Now convert loggable levels to strings. // We only use numbers to make the threshold setting convenient for users. - if ($this->loggableLevels !== []) { - $temp = []; + foreach ($loggableLevels as $level) { + /** @var false|string $stringLevel */ + $stringLevel = array_search($level, $this->logLevels, true); - foreach ($this->loggableLevels as $level) { - $temp[] = array_search((int) $level, $this->logLevels, true); + if ($stringLevel === false) { + continue; } - $this->loggableLevels = $temp; - unset($temp); + $this->loggableLevels[] = $stringLevel; } - $this->dateFormat = $config->dateFormat ?? $this->dateFormat; + if (isset($config->dateFormat)) { + $this->dateFormat = $config->dateFormat; + } - if (! is_array($config->handlers) || $config->handlers === []) { + if ($config->handlers === []) { throw LogException::forNoHandlers('LoggerConfig'); } // Save the handler configuration for later. // Instances will be created on demand. $this->handlerConfig = $config->handlers; + $this->cacheLogs = $debug; - $this->cacheLogs = $debug; if ($this->cacheLogs) { $this->logCache = []; } @@ -156,8 +158,6 @@ public function __construct($config, bool $debug = CI_DEBUG) /** * System is unusable. - * - * @param string $message */ public function emergency(string|Stringable $message, array $context = []): void { @@ -169,8 +169,6 @@ public function emergency(string|Stringable $message, array $context = []): void * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. - * - * @param string $message */ public function alert(string|Stringable $message, array $context = []): void { @@ -181,8 +179,6 @@ public function alert(string|Stringable $message, array $context = []): void * Critical conditions. * * Example: Application component unavailable, unexpected exception. - * - * @param string $message */ public function critical(string|Stringable $message, array $context = []): void { @@ -192,8 +188,6 @@ public function critical(string|Stringable $message, array $context = []): void /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. - * - * @param string $message */ public function error(string|Stringable $message, array $context = []): void { @@ -205,8 +199,6 @@ public function error(string|Stringable $message, array $context = []): void * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. - * - * @param string $message */ public function warning(string|Stringable $message, array $context = []): void { @@ -215,8 +207,6 @@ public function warning(string|Stringable $message, array $context = []): void /** * Normal but significant events. - * - * @param string $message */ public function notice(string|Stringable $message, array $context = []): void { @@ -227,8 +217,6 @@ public function notice(string|Stringable $message, array $context = []): void * Interesting events. * * Example: User logs in, SQL logs. - * - * @param string $message */ public function info(string|Stringable $message, array $context = []): void { @@ -237,8 +225,6 @@ public function info(string|Stringable $message, array $context = []): void /** * Detailed debug information. - * - * @param string $message */ public function debug(string|Stringable $message, array $context = []): void { @@ -248,8 +234,7 @@ public function debug(string|Stringable $message, array $context = []): void /** * Logs with an arbitrary level. * - * @param string $level - * @param string $message + * @param mixed $level */ public function log($level, string|Stringable $message, array $context = []): void { @@ -257,24 +242,18 @@ public function log($level, string|Stringable $message, array $context = []): vo $level = array_search((int) $level, $this->logLevels, true); } - // Is the level a valid level? if (! array_key_exists($level, $this->logLevels)) { throw LogException::forInvalidLogLevel($level); } - // Does the app want to log this right now? if (! in_array($level, $this->loggableLevels, true)) { return; } - // Parse our placeholders $message = $this->interpolate($message, $context); if ($this->cacheLogs) { - $this->logCache[] = [ - 'level' => $level, - 'msg' => $message, - ]; + $this->logCache[] = ['level' => $level, 'msg' => $message]; } foreach ($this->handlerConfig as $className => $config) { @@ -282,17 +261,13 @@ public function log($level, string|Stringable $message, array $context = []): vo $this->handlers[$className] = new $className($config); } - /** - * @var HandlerInterface $handler - */ $handler = $this->handlers[$className]; if (! $handler->canHandle($level)) { continue; } - // If the handler returns false, then we - // don't execute any other handlers. + // If the handler returns false, then we don't execute any other handlers. if (! $handler->setDateFormat($this->dateFormat)->handle($level, $message)) { break; } @@ -311,7 +286,8 @@ public function log($level, string|Stringable $message, array $context = []): vo * {file} * {line} * - * @param string $message + * @param string|Stringable $message + * @param array $context * * @return string */ @@ -321,7 +297,6 @@ protected function interpolate($message, array $context = []) return print_r($message, true); } - // build a replacement array with braces around the context keys $replace = []; foreach ($context as $key => $val) { @@ -335,7 +310,6 @@ protected function interpolate($message, array $context = []) $replace['{' . $key . '}'] = $val; } - // Add special placeholders $replace['{post_vars}'] = '$_POST: ' . print_r($_POST, true); $replace['{get_vars}'] = '$_GET: ' . print_r($_GET, true); $replace['{env}'] = ENVIRONMENT; @@ -362,7 +336,6 @@ protected function interpolate($message, array $context = []) $replace['{session_vars}'] = '$_SESSION: ' . print_r($_SESSION, true); } - // interpolate replacement values into the message and return return strtr($message, $replace); } @@ -370,6 +343,8 @@ protected function interpolate($message, array $context = []) * Determines the file and line that the logging call * was made from by analyzing the backtrace. * Find the earliest stack frame that is part of our logging system. + * + * @return array{string, int|string} */ public function determineFile(): array { @@ -386,28 +361,19 @@ public function determineFile(): array 'notice', ]; - // Generate Backtrace info - $trace = \debug_backtrace(0); + $trace = debug_backtrace(0); - // So we search from the bottom (earliest) of the stack frames - $stackFrames = \array_reverse($trace); + $stackFrames = array_reverse($trace); - // Find the first reference to a Logger class method foreach ($stackFrames as $frame) { - if (\in_array($frame['function'], $logFunctions, true)) { + if (in_array($frame['function'], $logFunctions, true)) { $file = isset($frame['file']) ? clean_path($frame['file']) : 'unknown'; $line = $frame['line'] ?? 'unknown'; - return [ - $file, - $line, - ]; + return [$file, $line]; } } - return [ - 'unknown', - 'unknown', - ]; + return ['unknown', 'unknown']; } } diff --git a/system/Model.php b/system/Model.php index 473584cb..44ed52a0 100644 --- a/system/Model.php +++ b/system/Model.php @@ -551,9 +551,8 @@ protected function doOnlyDeleted() * Compiles a replace into string and runs the query * This method works only with dbCalls. * - * @param array|null $row Data - * @phpstan-param row_array|null $row - * @param bool $returnSQL Set to true to return Query String + * @param row_array|null $row Data + * @param bool $returnSQL Set to true to return Query String * * @return BaseResult|false|Query|string */ @@ -774,12 +773,10 @@ protected function shouldUpdate($row): bool * Inserts data into the database. If an object is provided, * it will attempt to convert it to an array. * - * @param array|object|null $row - * @phpstan-param row_array|object|null $row - * @param bool $returnID Whether insert ID should be returned or not. + * @param object|row_array|null $row + * @param bool $returnID Whether insert ID should be returned or not. * - * @return bool|int|string - * @phpstan-return ($returnID is true ? int|string|false : bool) + * @return ($returnID is true ? false|int|string : bool) * * @throws ReflectionException */ diff --git a/system/RESTful/ResourceController.php b/system/RESTful/ResourceController.php index 072b52ee..ed72df21 100644 --- a/system/RESTful/ResourceController.php +++ b/system/RESTful/ResourceController.php @@ -106,8 +106,7 @@ public function delete($id = null) /** * Set/change the expected response representation for returned objects * - * @param string $format Response format - * @phpstan-param 'json'|'xml' $format + * @param 'json'|'xml' $format Response format * * @return void */ diff --git a/system/Security/Security.php b/system/Security/Security.php index 9e87a217..aa744d9e 100644 --- a/system/Security/Security.php +++ b/system/Security/Security.php @@ -427,59 +427,16 @@ public function shouldRedirect(): bool * e.g. file/in/some/approved/folder.txt, you can set the second optional * parameter, $relativePath to TRUE. * + * @deprecated 4.6.2 Use `sanitize_filename()` instead + * * @param string $str Input file name * @param bool $relativePath Whether to preserve paths */ public function sanitizeFilename(string $str, bool $relativePath = false): string { - // List of sanitize filename strings - $bad = [ - '../', - '', - '<', - '>', - "'", - '"', - '&', - '$', - '#', - '{', - '}', - '[', - ']', - '=', - ';', - '?', - '%20', - '%22', - '%3c', - '%253c', - '%3e', - '%0e', - '%28', - '%29', - '%2528', - '%26', - '%24', - '%3f', - '%3b', - '%3d', - ]; - - if (! $relativePath) { - $bad[] = './'; - $bad[] = '/'; - } - - $str = remove_invisible_characters($str, false); - - do { - $old = $str; - $str = str_replace($bad, '', $str); - } while ($old !== $str); + helper('security'); - return stripslashes($str); + return sanitize_filename($str, $relativePath); } /** diff --git a/system/Security/SecurityInterface.php b/system/Security/SecurityInterface.php index 03a5ba23..ebd8919c 100644 --- a/system/Security/SecurityInterface.php +++ b/system/Security/SecurityInterface.php @@ -66,6 +66,8 @@ public function shouldRedirect(): bool; * e.g. file/in/some/approved/folder.txt, you can set the second optional * parameter, $relativePath to TRUE. * + * @deprecated 4.6.2 Use `sanitize_filename()` instead + * * @param string $str Input file name * @param bool $relativePath Whether to preserve paths */ diff --git a/system/Test/Interfaces/FabricatorModel.php b/system/Test/Interfaces/FabricatorModel.php index a5860e22..57c1b16c 100644 --- a/system/Test/Interfaces/FabricatorModel.php +++ b/system/Test/Interfaces/FabricatorModel.php @@ -39,7 +39,7 @@ interface FabricatorModel * * @param int|list|string|null $id One primary key or an array of primary keys * - * @phpstan-return ($id is int|string ? row_array|object|null : list) + * @return ($id is int|string ? object|row_array|null : list) */ public function find($id = null); @@ -47,9 +47,8 @@ public function find($id = null); * Inserts data into the current table. If an object is provided, * it will attempt to convert it to an array. * - * @param array|object|null $row - * @phpstan-param row_array|object|null $row - * @param bool $returnID Whether insert ID should be returned or not. + * @param object|row_array|null $row + * @param bool $returnID Whether insert ID should be returned or not. * * @return bool|int|string * diff --git a/system/Test/Mock/MockBuilder.php b/system/Test/Mock/MockBuilder.php index e947f0ef..f95f0949 100644 --- a/system/Test/Mock/MockBuilder.php +++ b/system/Test/Mock/MockBuilder.php @@ -17,6 +17,9 @@ class MockBuilder extends BaseBuilder { + /** + * @var array + */ protected $supportedIgnoreStatements = [ 'update' => 'IGNORE', 'insert' => 'IGNORE', diff --git a/system/Test/Mock/MockCLIConfig.php b/system/Test/Mock/MockCLIConfig.php index c62df3d9..99e3597d 100644 --- a/system/Test/Mock/MockCLIConfig.php +++ b/system/Test/Mock/MockCLIConfig.php @@ -17,14 +17,19 @@ class MockCLIConfig extends App { - public string $baseURL = 'http://example.com/'; - public string $uriProtocol = 'REQUEST_URI'; - public array $proxyIPs = []; - public string $CSRFTokenName = 'csrf_test_name'; - public string $CSRFCookieName = 'csrf_cookie_name'; - public int $CSRFExpire = 7200; - public bool $CSRFRegenerate = true; - public $CSRFExcludeURIs = ['http://example.com']; + public string $baseURL = 'http://example.com/'; + public string $uriProtocol = 'REQUEST_URI'; + public array $proxyIPs = []; + public string $CSRFTokenName = 'csrf_test_name'; + public string $CSRFCookieName = 'csrf_cookie_name'; + public int $CSRFExpire = 7200; + public bool $CSRFRegenerate = true; + + /** + * @var list + */ + public array $CSRFExcludeURIs = ['http://example.com']; + public string $CSRFSameSite = 'Lax'; public bool $CSPEnabled = false; public string $defaultLocale = 'en'; diff --git a/system/Test/Mock/MockCURLRequest.php b/system/Test/Mock/MockCURLRequest.php index 0f0b203a..059b8311 100644 --- a/system/Test/Mock/MockCURLRequest.php +++ b/system/Test/Mock/MockCURLRequest.php @@ -23,7 +23,14 @@ */ class MockCURLRequest extends CURLRequest { + /** + * @var array + */ public $curl_options; + + /** + * @var string + */ protected $output = ''; /** @@ -38,11 +45,13 @@ public function setOutput($output) return $this; } + /** + * @param array $curlOptions + */ protected function sendRequest(array $curlOptions = []): string { $this->response = clone $this->responseOrig; - // Save so we can access later. $this->curl_options = $curlOptions; return $this->output; diff --git a/system/Test/Mock/MockCache.php b/system/Test/Mock/MockCache.php index b410df87..67f8aee8 100644 --- a/system/Test/Mock/MockCache.php +++ b/system/Test/Mock/MockCache.php @@ -214,8 +214,9 @@ public function getCacheInfo() /** * Returns detailed information about the specific item in the cache. * - * @return array|null Returns null if the item does not exist, otherwise array - * with at least the 'expire' key for absolute epoch expiry (or null). + * @return array{expire: int|null}|null Returns null if the item does not exist, + * otherwise, array with the 'expire' key for + * absolute epoch expiry (or null). */ public function getMetaData(string $key) { diff --git a/system/Test/Mock/MockConnection.php b/system/Test/Mock/MockConnection.php index 7870d51b..2801a067 100644 --- a/system/Test/Mock/MockConnection.php +++ b/system/Test/Mock/MockConnection.php @@ -18,6 +18,7 @@ use CodeIgniter\Database\BaseResult; use CodeIgniter\Database\Query; use CodeIgniter\Database\TableName; +use stdClass; /** * @extends BaseConnection @@ -25,7 +26,10 @@ class MockConnection extends BaseConnection { /** - * @var array{connect?: mixed, execute?: bool|object} + * @var array{ + * connect?: object|resource|false|list, + * execute?: object|resource|false, + * } */ protected $returnValues = []; @@ -36,11 +40,18 @@ class MockConnection extends BaseConnection */ protected $schema; + /** + * @var string + */ public $database; + + /** + * @var Query + */ public $lastQuery; /** - * @param mixed $return + * @param false|list|object|resource $return * * @return $this */ @@ -59,14 +70,15 @@ public function shouldReturn(string $method, $return) * Should automatically handle different connections for read/write * queries if needed. * - * @param mixed ...$binds + * @param mixed $binds * - * @return BaseResult|bool|Query + * @return BaseResult|bool|Query * * @todo BC set $queryClass default as null in 4.1 */ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, string $queryClass = '') { + /** @var class-string $queryClass */ $queryClass = str_replace('Connection', 'Query', static::class); $query = new $queryClass($this); @@ -81,23 +93,24 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s $this->lastQuery = $query; - // Run the query - if (false === ($this->resultID = $this->simpleQuery($query->getQuery()))) { + $this->resultID = $this->simpleQuery($query->getQuery()); + + if ($this->resultID === false) { $query->setDuration($startTime, $startTime); // @todo deal with errors - return false; } $query->setDuration($startTime); // resultID is not false, so it must be successful - if ($query->isWriteType($sql)) { + if ($query->isWriteType()) { return true; } // query is not write-type, so it must be read-type query; return QueryResult + /** @var class-string $resultClass */ $resultClass = str_replace('Connection', 'Result', static::class); return new $resultClass($this->connID, $this->resultID); @@ -106,7 +119,7 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s /** * Connect to the database. * - * @return mixed + * @return false|object|resource */ public function connect(bool $persistent = false) { @@ -153,7 +166,7 @@ public function getVersion(): string /** * Executes the query against the database. * - * @return bool|object + * @return false|object|resource */ protected function execute(string $sql) { @@ -171,9 +184,7 @@ public function affectedRows(): int /** * Returns the last error code and message. * - * Must return an array with keys 'code' and 'message': - * - * return ['code' => null, 'message' => null); + * @return array{code: int, message: string} */ public function error(): array { @@ -183,9 +194,6 @@ public function error(): array ]; } - /** - * Insert ID - */ public function insertID(): int { return $this->connID->insert_id; @@ -211,16 +219,25 @@ protected function _listColumns($table = ''): string return ''; } + /** + * @return list + */ protected function _fieldData(string $table): array { return []; } + /** + * @return array + */ protected function _indexData(string $table): array { return []; } + /** + * @return array + */ protected function _foreignKeyData(string $table): array { return []; diff --git a/system/Test/Mock/MockEvents.php b/system/Test/Mock/MockEvents.php index bb0b4d9f..4ed1ea67 100644 --- a/system/Test/Mock/MockEvents.php +++ b/system/Test/Mock/MockEvents.php @@ -18,7 +18,7 @@ class MockEvents extends Events { /** - * @return array + * @return array, 2: list}> */ public function getListeners() { @@ -26,7 +26,7 @@ public function getListeners() } /** - * @return array + * @return list */ public function getEventsFile() { diff --git a/system/Test/Mock/MockFileLogger.php b/system/Test/Mock/MockFileLogger.php index 8737e724..5a815b22 100644 --- a/system/Test/Mock/MockFileLogger.php +++ b/system/Test/Mock/MockFileLogger.php @@ -16,21 +16,24 @@ use CodeIgniter\Log\Handlers\FileHandler; /** - * Class MockFileLogger - * * Extends FileHandler, exposing some inner workings */ class MockFileLogger extends FileHandler { /** * Where would the log be written? + * + * @var string */ public $destination; + /** + * @param array{handles?: list, path?: string, fileExtension?: string, filePermissions?: int} $config + */ public function __construct(array $config) { parent::__construct($config); - $this->handles = $config['handles'] ?? []; + $this->destination = $this->path . 'log-' . date('Y-m-d') . '.' . $this->fileExtension; } } diff --git a/system/Test/Mock/MockInputOutput.php b/system/Test/Mock/MockInputOutput.php index 7f4370a2..6aa4779f 100644 --- a/system/Test/Mock/MockInputOutput.php +++ b/system/Test/Mock/MockInputOutput.php @@ -31,16 +31,14 @@ final class MockInputOutput extends InputOutput /** * Output lines. * - * @var array - * @phpstan-var list + * @var list */ private array $outputs = []; /** * Sets user inputs. * - * @param array $inputs - * @phpstan-param list $inputs + * @param list $inputs */ public function setInputs(array $inputs): void { @@ -80,6 +78,8 @@ public function getOutput(?int $index = null): string /** * Returns the outputs array. + * + * @return list */ public function getOutputs(): array { diff --git a/system/Test/Mock/MockLogger.php b/system/Test/Mock/MockLogger.php index 5130b2ea..411c1a66 100644 --- a/system/Test/Mock/MockLogger.php +++ b/system/Test/Mock/MockLogger.php @@ -13,77 +13,79 @@ namespace CodeIgniter\Test\Mock; +use CodeIgniter\Log\Handlers\HandlerInterface; +use Config\Logger; use Tests\Support\Log\Handlers\TestHandler; -class MockLogger +class MockLogger extends Logger { - /* - |-------------------------------------------------------------------------- - | Error Logging Threshold - |-------------------------------------------------------------------------- - | - | You can enable error logging by setting a threshold over zero. The - | threshold determines what gets logged. Any values below or equal to the - | threshold will be logged. Threshold options are: - | - | 0 = Disables logging, Error logging TURNED OFF - | 1 = Emergency Messages - System is unusable - | 2 = Alert Messages - Action Must Be Taken Immediately - | 3 = Critical Messages - Application component unavailable, unexpected exception. - | 4 = Runtime Errors - Don't need immediate action, but should be monitored. - | 5 = Warnings - Exceptional occurrences that are not errors. - | 6 = Notices - Normal but significant events. - | 7 = Info - Interesting events, like user logging in, etc. - | 8 = Debug - Detailed debug information. - | 9 = All Messages - | - | You can also pass an array with threshold levels to show individual error types - | - | array(1, 2, 3, 8) = Emergency, Alert, Critical, and Debug messages - | - | For a live site you'll usually enable Critical or higher (3) to be logged otherwise - | your log files will fill up very fast. - | + /** + *-------------------------------------------------------------------------- + * Error Logging Threshold + *-------------------------------------------------------------------------- + * + * You can enable error logging by setting a threshold over zero. The + * threshold determines what gets logged. Any values below or equal to the + * threshold will be logged. Threshold options are: + * + * 0 = Disables logging, Error logging TURNED OFF + * 1 = Emergency Messages - System is unusable + * 2 = Alert Messages - Action Must Be Taken Immediately + * 3 = Critical Messages - Application component unavailable, unexpected exception. + * 4 = Runtime Errors - Don't need immediate action, but should be monitored. + * 5 = Warnings - Exceptional occurrences that are not errors. + * 6 = Notices - Normal but significant events. + * 7 = Info - Interesting events, like user logging in, etc. + * 8 = Debug - Detailed debug information. + * 9 = All Messages + * + * You can also pass an array with threshold levels to show individual error types + * + * array(1, 2, 3, 8) = Emergency, Alert, Critical, and Debug messages + * + * For a live site you'll usually enable Critical or higher (3) to be logged otherwise + * your log files will fill up very fast. + * + * @var int|list */ - public $threshold = 9; - /* - |-------------------------------------------------------------------------- - | Date Format for Logs - |-------------------------------------------------------------------------- - | - | Each item that is logged has an associated date. You can use PHP date - | codes to set your own date formatting - | + /** + *-------------------------------------------------------------------------- + * Date Format for Logs + *-------------------------------------------------------------------------- + * + * Each item that is logged has an associated date. You can use PHP date + * codes to set your own date formatting */ - public $dateFormat = 'Y-m-d'; + public string $dateFormat = 'Y-m-d'; - /* - |-------------------------------------------------------------------------- - | Log Handlers - |-------------------------------------------------------------------------- - | - | The logging system supports multiple actions to be taken when something - | is logged. This is done by allowing for multiple Handlers, special classes - | designed to write the log to their chosen destinations, whether that is - | a file on the server, a cloud-based service, or even taking actions such - | as emailing the dev team. - | - | Each handler is defined by the class name used for that handler, and it - | MUST implement the CodeIgniter\Log\Handlers\HandlerInterface interface. - | - | The value of each key is an array of configuration items that are sent - | to the constructor of each handler. The only required configuration item - | is the 'handles' element, which must be an array of integer log levels. - | This is most easily handled by using the constants defined in the - | Psr\Log\LogLevel class. - | - | Handlers are executed in the order defined in this array, starting with - | the handler on top and continuing down. - | + /** + *-------------------------------------------------------------------------- + * Log Handlers + *-------------------------------------------------------------------------- + * + * The logging system supports multiple actions to be taken when something + * is logged. This is done by allowing for multiple Handlers, special classes + * designed to write the log to their chosen destinations, whether that is + * a file on the server, a cloud-based service, or even taking actions such + * as emailing the dev team. + * + * Each handler is defined by the class name used for that handler, and it + * MUST implement the CodeIgniter\Log\Handlers\HandlerInterface interface. + * + * The value of each key is an array of configuration items that are sent + * to the constructor of each handler. The only required configuration item + * is the 'handles' element, which must be an array of integer log levels. + * This is most easily handled by using the constants defined in the + * Psr\Log\LogLevel class. + * + * Handlers are executed in the order defined in this array, starting with + * the handler on top and continuing down. + * + * @var array, array|string>> */ - public $handlers = [ + public array $handlers = [ // File Handler TestHandler::class => [ // The log levels that this handler will handle. diff --git a/system/Test/Mock/MockResult.php b/system/Test/Mock/MockResult.php index 9c5f55e9..bc5cd8ac 100644 --- a/system/Test/Mock/MockResult.php +++ b/system/Test/Mock/MockResult.php @@ -31,6 +31,8 @@ public function getFieldCount(): int /** * Generates an array of column names in the result set. + * + * @return array{} */ public function getFieldNames(): array { @@ -39,6 +41,8 @@ public function getFieldNames(): array /** * Generates an array of objects representing field meta-data. + * + * @return array{} */ public function getFieldData(): array { @@ -73,7 +77,7 @@ public function dataSeek($n = 0) * * Overridden by driver classes. * - * @return mixed + * @return array{} */ protected function fetchAssoc() { @@ -83,13 +87,11 @@ protected function fetchAssoc() /** * Returns the result set as an object. * - * Overridden by child classes. - * - * @param string $className + * @param class-string $className * - * @return object|stdClass + * @return object */ - protected function fetchObject($className = 'stdClass') + protected function fetchObject($className = stdClass::class) { return new $className(); } diff --git a/system/Test/Mock/MockServices.php b/system/Test/Mock/MockServices.php index 5d1076a6..c71bb612 100644 --- a/system/Test/Mock/MockServices.php +++ b/system/Test/Mock/MockServices.php @@ -18,9 +18,16 @@ class MockServices extends BaseService { + /** + * @var array + */ public $psr4 = [ 'Tests/Support' => TESTPATH . '_support/', ]; + + /** + * @var array + */ public $classmap = []; public function __construct() diff --git a/system/Test/Mock/MockSession.php b/system/Test/Mock/MockSession.php index 13eca596..b581f81d 100644 --- a/system/Test/Mock/MockSession.php +++ b/system/Test/Mock/MockSession.php @@ -30,9 +30,9 @@ class MockSession extends Session * * @var list */ - public $cookies = []; + public array $cookies = []; - public $didRegenerate = false; + public bool $didRegenerate = false; /** * Sets the driver as the session handler in PHP. diff --git a/system/ThirdParty/Escaper/Escaper.php b/system/ThirdParty/Escaper/Escaper.php index 4fce36bd..39d9b0b1 100644 --- a/system/ThirdParty/Escaper/Escaper.php +++ b/system/ThirdParty/Escaper/Escaper.php @@ -30,7 +30,7 @@ * * @final */ -class Escaper +class Escaper implements EscaperInterface { /** * Entity Map mapping Unicode codepoints to any available named HTML entities. @@ -183,24 +183,13 @@ public function getEncoding() return $this->encoding; } - /** - * Escape a string for the HTML Body context where there are very few characters - * of special meaning. Internally this will use htmlspecialchars(). - * - * @return ($string is non-empty-string ? non-empty-string : string) - */ + /** @inheritDoc */ public function escapeHtml(string $string) { return htmlspecialchars($string, $this->htmlSpecialCharsFlags, $this->encoding); } - /** - * Escape a string for the HTML Attribute context. We use an extended set of characters - * to escape that are not covered by htmlspecialchars() to cover cases where an attribute - * might be unquoted or quoted illegally (e.g. backticks are valid quotes for IE). - * - * @return ($string is non-empty-string ? non-empty-string : string) - */ + /** @inheritDoc */ public function escapeHtmlAttr(string $string) { $string = $this->toUtf8($string); @@ -214,17 +203,7 @@ public function escapeHtmlAttr(string $string) return $this->fromUtf8($result); } - /** - * Escape a string for the Javascript context. This does not use json_encode(). An extended - * set of characters are escaped beyond ECMAScript's rules for Javascript literal string - * escaping in order to prevent misinterpretation of Javascript as HTML leading to the - * injection of special characters and entities. The escaping used should be tolerant - * of cases where HTML escaping was not applied on top of Javascript escaping correctly. - * Backslash escaping is not used as it still leaves the escaped character as-is and so - * is not useful in a HTML context. - * - * @return ($string is non-empty-string ? non-empty-string : string) - */ + /** @inheritDoc */ public function escapeJs(string $string) { $string = $this->toUtf8($string); @@ -238,24 +217,13 @@ public function escapeJs(string $string) return $this->fromUtf8($result); } - /** - * Escape a string for the URI or Parameter contexts. This should not be used to escape - * an entire URI - only a subcomponent being inserted. The function is a simple proxy - * to rawurlencode() which now implements RFC 3986 since PHP 5.3 completely. - * - * @return ($string is non-empty-string ? non-empty-string : string) - */ + /** @inheritDoc */ public function escapeUrl(string $string) { return rawurlencode($string); } - /** - * Escape a string for the CSS context. CSS escaping can be applied to any string being - * inserted into CSS and escapes everything except alphanumerics. - * - * @return ($string is non-empty-string ? non-empty-string : string) - */ + /** @inheritDoc */ public function escapeCss(string $string) { $string = $this->toUtf8($string); diff --git a/system/ThirdParty/Escaper/EscaperInterface.php b/system/ThirdParty/Escaper/EscaperInterface.php new file mode 100644 index 00000000..3930db88 --- /dev/null +++ b/system/ThirdParty/Escaper/EscaperInterface.php @@ -0,0 +1,58 @@ +cache->get($cacheName)) { + $output = $this->cache->get($cacheName); + + if (is_string($output) && $output !== '') { return $output; } @@ -116,8 +118,7 @@ public function render(string $library, $params = null, int $ttl = 0, ?string $c * If a string, it should be in the format "key1=value key2=value". * It will be split and returned as an array. * - * @param array|string|null $params - * @phpstan-param array|string|float|null $params + * @param array|float|string|null $params * * @return array */ diff --git a/system/View/Filters.php b/system/View/Filters.php index 0be48b6d..c74e385c 100644 --- a/system/View/Filters.php +++ b/system/View/Filters.php @@ -77,8 +77,8 @@ public static function default($value, string $default): string /** * Escapes the given value with our `esc()` helper function. * - * @param string $value - * @phpstan-param 'html'|'js'|'css'|'url'|'attr'|'raw' $context + * @param string $value + * @param 'attr'|'css'|'html'|'js'|'raw'|'url' $context */ public static function esc($value, string $context = 'html'): string { diff --git a/system/View/Parser.php b/system/View/Parser.php index a2393bb4..85d0ff84 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -117,10 +117,14 @@ public function render(string $view, ?array $options = null, ?bool $saveData = n $cacheName = $options['cache_name'] ?? str_replace('.php', '', $view); // Was it cached? - if (isset($options['cache']) && ($output = cache($cacheName))) { - $this->logPerformance($start, microtime(true), $view); + if (isset($options['cache'])) { + $output = cache($cacheName); + + if (is_string($output) && $output !== '') { + $this->logPerformance($start, microtime(true), $view); - return $output; + return $output; + } } $file = $this->viewPath . $view; @@ -198,10 +202,9 @@ public function renderString(string $template, ?array $options = null, ?bool $sa * so that the variable is correctly handled within the * parsing itself, and contexts (including raw) are respected. * - * @param array $data - * @param non-empty-string|null $context The context to escape it for. - * If 'raw', no escaping will happen. - * @phpstan-param null|'html'|'js'|'css'|'url'|'attr'|'raw' $context + * @param array $data + * @param 'attr'|'css'|'html'|'js'|'raw'|'url'|null $context The context to escape it for. + * If 'raw', no escaping will happen. */ public function setData(array $data = [], ?string $context = null): RendererInterface { diff --git a/system/View/RendererInterface.php b/system/View/RendererInterface.php index 5fdf8794..d42dcee8 100644 --- a/system/View/RendererInterface.php +++ b/system/View/RendererInterface.php @@ -46,10 +46,9 @@ public function renderString(string $view, ?array $options = null, bool $saveDat /** * Sets several pieces of view data at once. * - * @param array $data - * @param non-empty-string|null $context The context to escape it for. - * If 'raw', no escaping will happen. - * @phpstan-param null|'html'|'js'|'css'|'url'|'attr'|'raw' $context + * @param array $data + * @param 'attr'|'css'|'html'|'js'|'raw'|'url'|null $context The context to escape it for. + * If 'raw', no escaping will happen. * * @return RendererInterface */ @@ -58,10 +57,9 @@ public function setData(array $data = [], ?string $context = null); /** * Sets a single piece of view data. * - * @param mixed $value - * @param non-empty-string|null $context The context to escape it for. - * If 'raw', no escaping will happen. - * @phpstan-param null|'html'|'js'|'css'|'url'|'attr'|'raw' $context + * @param mixed $value + * @param 'attr'|'css'|'html'|'js'|'raw'|'url'|null $context The context to escape it for. + * If 'raw', no escaping will happen. * * @return RendererInterface */ diff --git a/system/View/Table.php b/system/View/Table.php index a063e71c..df4b870b 100644 --- a/system/View/Table.php +++ b/system/View/Table.php @@ -108,8 +108,7 @@ public function __construct($config = []) /** * Set the template * - * @param array $template - * @phpstan-param array|string $template + * @param array|string $template * * @return bool */ diff --git a/system/View/View.php b/system/View/View.php index 6a282096..184f2528 100644 --- a/system/View/View.php +++ b/system/View/View.php @@ -186,7 +186,9 @@ public function render(string $view, ?array $options = null, ?bool $saveData = n $this->renderVars['cacheName'] = $cacheName; - if ($output = cache($this->renderVars['cacheName'])) { + $output = cache($this->renderVars['cacheName']); + + if (is_string($output) && $output !== '') { $this->logPerformance( $this->renderVars['start'], microtime(true), @@ -335,9 +337,8 @@ public function excerpt(string $string, int $length = 20): string /** * Sets several pieces of view data at once. * - * @param non-empty-string|null $context The context to escape it for. - * If 'raw', no escaping will happen. - * @phpstan-param null|'html'|'js'|'css'|'url'|'attr'|'raw' $context + * @param 'attr'|'css'|'html'|'js'|'raw'|'url'|null $context The context to escape it for. + * If 'raw', no escaping will happen. */ public function setData(array $data = [], ?string $context = null): RendererInterface { @@ -354,10 +355,9 @@ public function setData(array $data = [], ?string $context = null): RendererInte /** * Sets a single piece of view data. * - * @param mixed $value - * @param non-empty-string|null $context The context to escape it for. - * If 'raw', no escaping will happen. - * @phpstan-param null|'html'|'js'|'css'|'url'|'attr'|'raw' $context + * @param mixed $value + * @param 'attr'|'css'|'html'|'js'|'raw'|'url'|null $context The context to escape it for. + * If 'raw', no escaping will happen. */ public function setVar(string $name, $value = null, ?string $context = null): RendererInterface { diff --git a/system/util_bootstrap.php b/system/util_bootstrap.php new file mode 100644 index 00000000..5423954d --- /dev/null +++ b/system/util_bootstrap.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +use CodeIgniter\Boot; +use Config\Paths; + +error_reporting(E_ALL); +ini_set('display_errors', '1'); +ini_set('display_startup_errors', '1'); + +/* + * --------------------------------------------------------------- + * DEFINE ENVIRONMENT + * --------------------------------------------------------------- + * + * As this bootstrap file is primarily used by internal scripts + * across the framework and other CodeIgniter projects, we need + * to make sure it recognizes that we're in development. + */ + +$_SERVER['CI_ENVIRONMENT'] = 'development'; +define('ENVIRONMENT', 'development'); +defined('CI_DEBUG') || define('CI_DEBUG', true); + +/* + * --------------------------------------------------------------- + * SET UP OUR PATH CONSTANTS + * --------------------------------------------------------------- + * + * The path constants provide convenient access to the folders + * throughout the application. We have to set them up here + * so they are available in the config files that are loaded. + */ + +defined('HOMEPATH') || define('HOMEPATH', realpath(rtrim(getcwd(), '\\/ ')) . DIRECTORY_SEPARATOR); + +$source = match (true) { + is_dir(HOMEPATH . 'app/') => HOMEPATH, + is_dir('vendor/codeigniter4/framework/') => 'vendor/codeigniter4/framework/', + is_dir('vendor/codeigniter4/codeigniter4/') => 'vendor/codeigniter4/codeigniter4/', + default => throw new RuntimeException('Unable to determine the source directory.'), +}; + +defined('CONFIGPATH') || define('CONFIGPATH', realpath($source . 'app/Config') . DIRECTORY_SEPARATOR); +defined('PUBLICPATH') || define('PUBLICPATH', realpath($source . 'public') . DIRECTORY_SEPARATOR); +unset($source); + +require CONFIGPATH . 'Paths.php'; +$paths = new Paths(); + +defined('CIPATH') || define('CIPATH', realpath($paths->systemDirectory . '/../') . DIRECTORY_SEPARATOR); +defined('FCPATH') || define('FCPATH', PUBLICPATH); + +if (is_dir($paths->testsDirectory . '/_support/') && ! defined('SUPPORTPATH')) { + define('SUPPORTPATH', realpath($paths->testsDirectory . '/_support/') . DIRECTORY_SEPARATOR); +} + +if (is_dir(HOMEPATH . 'vendor/')) { + define('VENDORPATH', realpath(HOMEPATH . 'vendor/') . DIRECTORY_SEPARATOR); + define('COMPOSER_PATH', (string) realpath(HOMEPATH . 'vendor/autoload.php')); +} + +/* + *--------------------------------------------------------------- + * BOOTSTRAP THE APPLICATION + *--------------------------------------------------------------- + * + * This process sets up the path constants, loads and registers + * our autoloader, along with Composer's, loads our constants + * and fires up an environment-specific bootstrapping. + */ + +require $paths->systemDirectory . '/Boot.php'; +Boot::bootConsole($paths); diff --git a/writable/debugbar/index.html b/writable/debugbar/index.html new file mode 100644 index 00000000..b702fbc3 --- /dev/null +++ b/writable/debugbar/index.html @@ -0,0 +1,11 @@ + + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + From 68d1a5896106f869452dd369a690dd5bc75160fb Mon Sep 17 00:00:00 2001 From: paulbalandan Date: Sat, 2 Aug 2025 13:36:13 +0000 Subject: [PATCH 2/3] Release v4.6.3 --- app/Config/Filters.php | 5 ++++- preload.php | 1 + system/CodeIgniter.php | 2 +- system/Config/Filters.php | 5 ++++- system/Debug/Toolbar/Collectors/Routes.php | 2 +- system/Email/Email.php | 22 +++++++++++++++++----- system/Router/RouteCollection.php | 2 +- system/Test/Mock/MockConnection.php | 4 ++-- 8 files changed, 31 insertions(+), 12 deletions(-) diff --git a/app/Config/Filters.php b/app/Config/Filters.php index eb46a1d7..9c83ae94 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -65,7 +65,10 @@ class Filters extends BaseFilters * List of filter aliases that are always * applied before and after every request. * - * @var array>>|array> + * @var array{ + * before: array|string}>|list, + * after: array|string}>|list + * } */ public array $globals = [ 'before' => [ diff --git a/preload.php b/preload.php index 86322d5e..9d16bb31 100644 --- a/preload.php +++ b/preload.php @@ -57,6 +57,7 @@ class preload '/system/Config/Routes.php', '/system/Language/', '/system/bootstrap.php', + '/system/util_bootstrap.php', '/system/rewrite.php', '/Views/', // Errors occur. diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 3c81f64c..c87ff78e 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -55,7 +55,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.6.2'; + public const CI_VERSION = '4.6.3'; /** * App startup time. diff --git a/system/Config/Filters.php b/system/Config/Filters.php index 7eddc721..80662ede 100644 --- a/system/Config/Filters.php +++ b/system/Config/Filters.php @@ -78,7 +78,10 @@ class Filters extends BaseConfig * List of filter aliases that are always * applied before and after every request. * - * @var array>>|array> + * @var array{ + * before: array|string}>|list, + * after: array|string}>|list + * } */ public array $globals = [ 'before' => [ diff --git a/system/Debug/Toolbar/Collectors/Routes.php b/system/Debug/Toolbar/Collectors/Routes.php index 47c808e7..f8005065 100644 --- a/system/Debug/Toolbar/Collectors/Routes.php +++ b/system/Debug/Toolbar/Collectors/Routes.php @@ -51,7 +51,7 @@ class Routes extends BaseCollector * Returns the data of this collector to be formatted in the toolbar * * @return array{ - * matchedRoute: arraynewline . 'Content-Disposition: ' . $attachment['disposition'] . ';' . $this->newline . 'Content-Transfer-Encoding: base64' . $this->newline - . ($attachment['cid'] === '' ? '' : 'Content-ID: <' . $attachment['cid'] . '>' . $this->newline) + . (isset($attachment['cid']) && $attachment['cid'] !== '' ? 'Content-ID: <' . $attachment['cid'] . '>' . $this->newline : '') . $this->newline . $attachment['content'] . $this->newline; } @@ -1886,7 +1886,7 @@ protected function SMTPEnd() */ protected function SMTPConnect() { - if (is_resource($this->SMTPConnect)) { + if ($this->isSMTPConnected()) { return true; } @@ -1910,7 +1910,7 @@ protected function SMTPConnect() $this->SMTPTimeout, ); - if (! is_resource($this->SMTPConnect)) { + if (! $this->isSMTPConnected()) { $this->setErrorMessage(lang('Email.SMTPError', [$errno . ' ' . $errstr])); return false; @@ -2227,7 +2227,7 @@ protected function mimeTypes($ext = '') public function __destruct() { - if ($this->SMTPConnect !== null) { + if ($this->isSMTPConnected()) { try { $this->sendCommand('quit'); } catch (ErrorException $e) { @@ -2284,4 +2284,16 @@ protected function setArchiveValues(): array return $this->archive; } + + /** + * Checks if there is an active SMTP connection. + * + * @return bool True if SMTP connection is established and open, false otherwise + */ + protected function isSMTPConnected(): bool + { + return $this->SMTPConnect !== null + && $this->SMTPConnect !== false + && get_debug_type($this->SMTPConnect) !== 'resource (closed)'; + } } diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 66c3ace6..7379d014 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -1724,7 +1724,7 @@ public function resetRoutes() * @return array< * string, * array{ - * filter?: string|list, namespace?: string, hostname?: string, + * filter?: list|string, namespace?: string, hostname?: string, * subdomain?: string, offset?: int, priority?: int, as?: string, * redirect?: int * } diff --git a/system/Test/Mock/MockConnection.php b/system/Test/Mock/MockConnection.php index 2801a067..d1416059 100644 --- a/system/Test/Mock/MockConnection.php +++ b/system/Test/Mock/MockConnection.php @@ -27,8 +27,8 @@ class MockConnection extends BaseConnection { /** * @var array{ - * connect?: object|resource|false|list, - * execute?: object|resource|false, + * connect?: false|list|object|resource, + * execute?: false|object|resource, * } */ protected $returnValues = []; From e4d3702037a34549582453dbf59b7b67877ae82e Mon Sep 17 00:00:00 2001 From: paulbalandan Date: Fri, 12 Dec 2025 10:37:42 +0000 Subject: [PATCH 3/3] Release v4.6.4 --- app/Config/Database.php | 1 + app/Controllers/BaseController.php | 35 ++--- composer.json | 2 +- preload.php | 4 +- system/API/ResponseTrait.php | 18 +-- system/BaseModel.php | 85 +++++++---- system/CLI/BaseCommand.php | 4 +- system/CLI/CLI.php | 6 +- system/CLI/Commands.php | 2 +- system/Cache/Handlers/PredisHandler.php | 4 + system/Cache/Handlers/RedisHandler.php | 2 +- system/CodeIgniter.php | 2 +- system/Commands/Database/CreateDatabase.php | 2 +- .../Generators/Views/migration.tpl.php | 2 +- .../AutoRouterImproved/AutoRouteCollector.php | 2 +- system/Common.php | 66 +++++--- system/Config/Services.php | 8 +- system/Controller.php | 2 - system/Cookie/Cookie.php | 18 ++- system/DataCaster/DataCaster.php | 30 ++-- system/Database/BaseBuilder.php | 19 ++- system/Database/BaseUtils.php | 2 +- system/Database/Config.php | 4 +- system/Database/Database.php | 8 +- system/Database/MigrationRunner.php | 8 +- system/Database/MySQLi/Connection.php | 2 +- system/Database/OCI8/Connection.php | 15 +- system/Database/Postgre/Connection.php | 2 +- system/Database/Postgre/Forge.php | 2 +- system/Database/SQLSRV/Builder.php | 2 +- system/Database/SQLSRV/Connection.php | 34 ++++- system/Database/SQLSRV/Forge.php | 25 ++- system/Database/SQLite3/Connection.php | 2 +- system/Debug/BaseExceptionHandler.php | 4 + system/Debug/ExceptionHandler.php | 59 ++++++++ system/Debug/ExceptionHandlerInterface.php | 4 + system/Debug/Timer.php | 2 +- system/Debug/Toolbar/Collectors/Database.php | 9 +- system/Debug/Toolbar/Collectors/Logs.php | 4 +- system/Debug/Toolbar/Views/toolbar.js | 9 +- system/Email/Email.php | 4 +- system/Entity/Cast/ArrayCast.php | 9 -- system/Entity/Cast/BaseCast.php | 19 --- system/Entity/Cast/BooleanCast.php | 6 - system/Entity/Cast/CSVCast.php | 9 -- system/Entity/Cast/CastInterface.php | 2 - system/Entity/Cast/DatetimeCast.php | 3 - system/Entity/Cast/FloatCast.php | 6 - system/Entity/Cast/IntBoolCast.php | 2 - system/Entity/Cast/IntegerCast.php | 6 - system/Entity/Cast/JsonCast.php | 6 - system/Entity/Cast/ObjectCast.php | 6 - system/Entity/Cast/StringCast.php | 6 - system/Entity/Cast/TimestampCast.php | 6 - system/Entity/Cast/URICast.php | 6 - system/Events/Events.php | 2 +- system/Filters/PageCache.php | 8 +- system/HTTP/Exceptions/RedirectException.php | 2 +- system/HTTP/Response.php | 2 +- system/HTTP/URI.php | 6 +- system/HTTP/UserAgent.php | 15 +- system/Helpers/cookie_helper.php | 33 ++-- system/Helpers/form_helper.php | 2 +- .../Honeypot/Exceptions/HoneypotException.php | 2 + system/I18n/Exceptions/I18nException.php | 2 +- system/I18n/TimeTrait.php | 6 +- system/Images/Handlers/ImageMagickHandler.php | 20 +-- system/Language/Language.php | 142 ++++++++++-------- system/Language/en/Honeypot.php | 19 +++ system/Language/en/Language.php | 1 + system/Language/en/Router.php | 2 +- system/Model.php | 2 +- system/Pager/PagerRenderer.php | 10 +- system/Router/AutoRouterImproved.php | 2 +- system/Router/Exceptions/RouterException.php | 2 +- system/Router/RouteCollection.php | 2 +- system/Security/CheckPhpIni.php | 73 +++++---- system/Test/CIUnitTestCase.php | 41 ++--- system/Test/DatabaseTestTrait.php | 12 +- system/Test/Fabricator.php | 2 +- system/Test/FeatureTestTrait.php | 27 +++- system/Test/Mock/MockCache.php | 2 +- system/Test/Mock/MockLanguage.php | 11 +- system/ThirdParty/Escaper/Escaper.php | 2 +- system/ThirdParty/Kint/CallFinder.php | 63 +++++++- system/ThirdParty/Kint/Kint.php | 23 ++- .../Kint/Parser/ClassStaticsPlugin.php | 4 +- system/ThirdParty/Kint/Parser/ColorPlugin.php | 2 +- system/ThirdParty/Kint/Parser/DomPlugin.php | 74 +++++---- system/ThirdParty/Kint/Parser/HtmlPlugin.php | 2 +- .../ThirdParty/Kint/Parser/IteratorPlugin.php | 4 - system/ThirdParty/Kint/Parser/Parser.php | 22 ++- .../ThirdParty/Kint/Parser/ProfilePlugin.php | 24 +++ .../Kint/Parser/SimpleXMLElementPlugin.php | 60 ++++---- system/ThirdParty/Kint/Parser/TracePlugin.php | 6 +- .../Kint/Renderer/Rich/BinaryPlugin.php | 2 +- .../ThirdParty/Kint/Renderer/RichRenderer.php | 4 +- system/ThirdParty/Kint/Utils.php | 8 +- .../Value/Context/ClassDeclaredContext.php | 25 ++- system/ThirdParty/Kint/Value/MethodValue.php | 2 +- .../Representation/AbstractRepresentation.php | 4 +- .../CallableDefinitionRepresentation.php | 3 +- .../Representation/ColorRepresentation.php | 33 +++- .../Representation/SourceRepresentation.php | 4 +- system/ThirdParty/Kint/Value/StringValue.php | 5 +- system/ThirdParty/Kint/init.php | 3 +- .../Kint/resources/compiled/aante-dark.css | 2 +- .../Kint/resources/compiled/aante-light.css | 2 +- .../Kint/resources/compiled/main.js | 2 +- .../Kint/resources/compiled/original.css | 2 +- .../Kint/resources/compiled/plain.css | 2 +- .../resources/compiled/solarized-dark.css | 2 +- .../Kint/resources/compiled/solarized.css | 2 +- system/Throttle/Throttler.php | 2 - system/Validation/FormatRules.php | 10 +- system/Validation/Validation.php | 2 +- system/View/Cell.php | 6 +- system/View/Cells/Cell.php | 2 +- 118 files changed, 910 insertions(+), 562 deletions(-) create mode 100644 system/Language/en/Honeypot.php diff --git a/app/Config/Database.php b/app/Config/Database.php index 29f6f4a1..d7939bc2 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -182,6 +182,7 @@ class Database extends Config 'port' => 3306, 'foreignKeys' => true, 'busyTimeout' => 1000, + 'synchronous' => null, 'dateFormat' => [ 'date' => 'Y-m-d', 'datetime' => 'Y-m-d H:i:s', diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index 689405bd..ab45a771 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -3,44 +3,28 @@ namespace App\Controllers; use CodeIgniter\Controller; -use CodeIgniter\HTTP\CLIRequest; -use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Psr\Log\LoggerInterface; /** - * Class BaseController - * * BaseController provides a convenient place for loading components * and performing functions that are needed by all your controllers. + * * Extend this class in any new controllers: + * ``` * class Home extends BaseController + * ``` * - * For security be sure to declare any new methods as protected or private. + * For security, be sure to declare any new methods as protected or private. */ abstract class BaseController extends Controller { - /** - * Instance of the main Request object. - * - * @var CLIRequest|IncomingRequest - */ - protected $request; - - /** - * An array of helpers to be loaded automatically upon - * class instantiation. These helpers will be available - * to all other controllers that extend BaseController. - * - * @var list - */ - protected $helpers = []; - /** * Be sure to declare properties for any property fetch you initialized. * The creation of dynamic property is deprecated in PHP 8.2. */ + // protected $session; /** @@ -48,11 +32,14 @@ abstract class BaseController extends Controller */ public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) { - // Do Not Edit This Line + // Load here all helpers you want to be available in your controllers that extend BaseController. + // Caution: Do not put the this below the parent::initController() call below. + // $this->helpers = ['form', 'url']; + + // Caution: Do not edit this line. parent::initController($request, $response, $logger); // Preload any models, libraries, etc, here. - - // E.g.: $this->session = service('session'); + // $this->session = service('session'); } } diff --git a/composer.json b/composer.json index 499a97ea..7a6e1c58 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "codeigniter/coding-standard": "^1.7", "fakerphp/faker": "^1.24", "friendsofphp/php-cs-fixer": "^3.47.1", - "kint-php/kint": "^6.0", + "kint-php/kint": "^6.1", "mikey179/vfsstream": "^1.6.12", "nexusphp/cs-config": "^3.6", "phpunit/phpunit": "^10.5.16 || ^11.2", diff --git a/preload.php b/preload.php index 9d16bb31..288b30b4 100644 --- a/preload.php +++ b/preload.php @@ -101,7 +101,9 @@ public function load(): void } require_once $file[0]; - echo 'Loaded: ' . $file[0] . "\n"; + // Uncomment only for debugging (to inspect which files are included). + // Never use this in production - preload scripts must not generate output. + // echo 'Loaded: ' . $file[0] . "\n"; } } } diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 318af4b2..f64cc671 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -15,8 +15,8 @@ use CodeIgniter\Format\Format; use CodeIgniter\Format\FormatterInterface; +use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\IncomingRequest; -use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; /** @@ -24,10 +24,10 @@ * consistent HTTP responses under a variety of common * situations when working as an API. * - * @property RequestInterface $request - * @property ResponseInterface $response - * @property bool $stringAsHtml Whether to treat string data as HTML in JSON response. - * Setting `true` is only for backward compatibility. + * @property CLIRequest|IncomingRequest $request + * @property ResponseInterface $response + * @property bool $stringAsHtml Whether to treat string data as HTML in JSON response. + * Setting `true` is only for backward compatibility. */ trait ResponseTrait { @@ -122,9 +122,9 @@ protected function respond($data = null, ?int $status = null, string $message = /** * Used for generic failures that no custom methods exist for. * - * @param list|string $messages - * @param int $status HTTP status code - * @param string|null $code Custom, API-specific, error code + * @param array|string $messages + * @param int $status HTTP status code + * @param string|null $code Custom, API-specific, error code * * @return ResponseInterface */ @@ -230,7 +230,7 @@ protected function failNotFound(string $description = 'Not Found', ?string $code /** * Used when the data provided by the client cannot be validated on one or more fields. * - * @param list|string $errors + * @param array|string $errors * * @return ResponseInterface */ diff --git a/system/BaseModel.php b/system/BaseModel.php index 4f1cf3dc..fdc07ba2 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -769,7 +769,7 @@ protected function shouldUpdate($row): bool { $id = $this->getIdValue($row); - return ! ($id === null || $id === [] || $id === ''); + return ! in_array($id, [null, [], ''], true); } /** @@ -909,22 +909,7 @@ public function insertBatch(?array $set = null, ?bool $escape = null, int $batch if (is_array($set)) { foreach ($set as &$row) { - // If $row is using a custom class with public or protected - // properties representing the collection elements, we need to grab - // them as an array. - if (is_object($row) && ! $row instanceof stdClass) { - $row = $this->objectToArray($row, false, true); - } - - // If it's still a stdClass, go ahead and convert to - // an array so doProtectFields and other model methods - // don't have to do special checks. - if (is_object($row)) { - $row = (array) $row; - } - - // Convert any Time instances to appropriate $dateFormat - $row = $this->timeToString($row); + $row = $this->transformDataRowToArray($row); // Validate every row. if (! $this->skipValidation && ! $this->validate($row)) { @@ -1051,21 +1036,7 @@ public function updateBatch(?array $set = null, ?string $index = null, int $batc { if (is_array($set)) { foreach ($set as &$row) { - // If $row is using a custom class with public or protected - // properties representing the collection elements, we need to grab - // them as an array. - if (is_object($row) && ! $row instanceof stdClass) { - // For updates the index field is needed even if it is not changed. - // So set $onlyChanged to false. - $row = $this->objectToArray($row, false, true); - } - - // If it's still a stdClass, go ahead and convert to - // an array so doProtectFields and other model methods - // don't have to do special checks. - if (is_object($row)) { - $row = (array) $row; - } + $row = $this->transformDataRowToArray($row); // Validate data before saving. if (! $this->skipValidation && ! $this->validate($row)) { @@ -1220,7 +1191,9 @@ public function replace(?array $row = null, bool $returnSQL = false) return false; } - $row = $this->setUpdatedField((array) $row, $this->setDate()); + $row = (array) $row; + $row = $this->setCreatedField($row, $this->setDate()); + $row = $this->setUpdatedField($row, $this->setDate()); return $this->doReplace($row, $returnSQL); } @@ -1694,6 +1667,52 @@ protected function trigger(string $event, array $eventData) return $eventData; } + /** + * If the model is using casts, this will convert the data + * in $row according to the rules defined in `$casts`. + * + * @param object|row_array|null $row Row data + * + * @return object|row_array|null Converted row data + * + * @used-by insertBatch() + * @used-by updateBatch() + * + * @throws ReflectionException + * @deprecated Since 4.6.4, temporary solution - will be removed in 4.7 + */ + protected function transformDataRowToArray(array|object|null $row): array|object|null + { + // If casts are used, convert the data first + if ($this->useCasts()) { + if (is_array($row)) { + $row = $this->converter->toDataSource($row); + } elseif ($row instanceof stdClass) { + $row = (array) $row; + $row = $this->converter->toDataSource($row); + } elseif ($row instanceof Entity) { + $row = $this->converter->extract($row); + } elseif (is_object($row)) { + $row = $this->converter->extract($row); + } + } elseif (is_object($row) && ! $row instanceof stdClass) { + // If $row is using a custom class with public or protected + // properties representing the collection elements, we need to grab + // them as an array. + $row = $this->objectToArray($row, false, true); + } + + // If it's still a stdClass, go ahead and convert to + // an array so doProtectFields and other model methods + // don't have to do special checks. + if (is_object($row)) { + $row = (array) $row; + } + + // Convert any Time instances to appropriate $dateFormat + return $this->timeToString($row); + } + /** * Sets the return type of the results to be as an associative array. * diff --git a/system/CLI/BaseCommand.php b/system/CLI/BaseCommand.php index afeb80eb..57df52d4 100644 --- a/system/CLI/BaseCommand.php +++ b/system/CLI/BaseCommand.php @@ -142,7 +142,7 @@ public function showHelp() { CLI::write(lang('CLI.helpUsage'), 'yellow'); - if (isset($this->usage)) { + if ($this->usage !== null) { $usage = $this->usage; } else { $usage = $this->name; @@ -154,7 +154,7 @@ public function showHelp() CLI::write($this->setPad($usage, 0, 0, 2)); - if (isset($this->description)) { + if ($this->description !== null) { CLI::newLine(); CLI::write(lang('CLI.helpDescription'), 'yellow'); CLI::write($this->setPad($this->description, 0, 0, 2)); diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index b8ea4714..b7c9ed64 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -318,11 +318,7 @@ public static function promptByMultipleKeys(string $text, array $options): array if ($opts === []) { $extraOutput = $extraOutputDefault; } else { - $optsKey = []; - - foreach (array_keys($opts) as $key) { - $optsKey[] = $key; - } + $optsKey = array_keys($opts); $extraOutput = '[' . $extraOutputDefault . ', ' . implode(', ', $optsKey) . ']'; $extraOutput = 'You can specify multiple values separated by commas.' . PHP_EOL . $extraOutput; } diff --git a/system/CLI/Commands.php b/system/CLI/Commands.php index aecd22c3..5630ee0f 100644 --- a/system/CLI/Commands.php +++ b/system/CLI/Commands.php @@ -129,7 +129,7 @@ public function discoverCommands() $class = new $className($this->logger, $this); - if (isset($class->group) && ! isset($this->commands[$class->name])) { + if ($class->group !== null && ! isset($this->commands[$class->name])) { $this->commands[$class->name] = [ 'class' => $className, 'file' => $file, diff --git a/system/Cache/Handlers/PredisHandler.php b/system/Cache/Handlers/PredisHandler.php index 250ea74a..8d0ce999 100644 --- a/system/Cache/Handlers/PredisHandler.php +++ b/system/Cache/Handlers/PredisHandler.php @@ -162,6 +162,10 @@ public function deleteMatching(string $pattern) $matchedKeys[] = $key; } + if ($matchedKeys === []) { + return 0; + } + return $this->redis->del($matchedKeys); } diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index 3a13ee07..06a089e2 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -195,7 +195,7 @@ public function deleteMatching(string $pattern) } } while ($iterator > 0); - return $this->redis->del($matchedKeys); + return (int) $this->redis->del($matchedKeys); } /** diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index c87ff78e..4156c994 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -55,7 +55,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.6.3'; + public const CI_VERSION = '4.6.4'; /** * App startup time. diff --git a/system/Commands/Database/CreateDatabase.php b/system/Commands/Database/CreateDatabase.php index 0c8d2016..facd060a 100644 --- a/system/Commands/Database/CreateDatabase.php +++ b/system/Commands/Database/CreateDatabase.php @@ -109,7 +109,7 @@ public function run(array $params) $config->{$group}['database'] = $name; if ($name !== ':memory:') { - $dbName = ! str_contains($name, DIRECTORY_SEPARATOR) ? WRITEPATH . $name : $name; + $dbName = str_contains($name, DIRECTORY_SEPARATOR) ? $name : WRITEPATH . $name; if (is_file($dbName)) { CLI::error("Database \"{$dbName}\" already exists.", 'light_gray', 'red'); diff --git a/system/Commands/Generators/Views/migration.tpl.php b/system/Commands/Generators/Views/migration.tpl.php index 321895e6..124b1b83 100644 --- a/system/Commands/Generators/Views/migration.tpl.php +++ b/system/Commands/Generators/Views/migration.tpl.php @@ -15,7 +15,7 @@ public function up() 'id' => ['type' => 'VARCHAR', 'constraint' => 128, 'null' => false], 'ip_address' => ['type' => 'VARCHAR', 'constraint' => 45, 'null' => false], - 'timestamp timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL', + '`timestamp` timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL', 'data' => ['type' => 'BLOB', 'null' => false], 'ip_address inet NOT NULL', diff --git a/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php b/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php index f4a5204f..d29f6a9b 100644 --- a/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php +++ b/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php @@ -99,7 +99,7 @@ public function get(): array * * @return list> */ - private function addFilters($routes) + private function addFilters(array $routes): array { $filterCollector = new FilterCollector(true); diff --git a/system/Common.php b/system/Common.php index 3f69838e..41b16100 100644 --- a/system/Common.php +++ b/system/Common.php @@ -216,9 +216,19 @@ function config(string $name, bool $getShared = true) /** * Simpler way to create a new Cookie instance. * - * @param string $name Name of the cookie - * @param string $value Value of the cookie - * @param array $options Array of options to be passed to the cookie + * @param string $name Name of the cookie + * @param string $value Value of the cookie + * @param array{ + * prefix?: string, + * max-age?: int|numeric-string, + * expires?: DateTimeInterface|int|string, + * path?: string, + * domain?: string, + * secure?: bool, + * httponly?: bool, + * samesite?: string, + * raw?: bool + * } $options Cookie configuration options * * @throws CookieException */ @@ -352,7 +362,27 @@ function csp_script_nonce(): string * If $getShared === false then a new connection instance will be provided, * otherwise it will all calls will return the same instance. * - * @param array|ConnectionInterface|string|null $db + * @param array{ + * DSN?: string, + * hostname?: string, + * username?: string, + * password?: string, + * database?: string, + * DBDriver?: 'MySQLi'|'OCI8'|'Postgre'|'SQLite3'|'SQLSRV', + * DBPrefix?: string, + * pConnect?: bool, + * DBDebug?: bool, + * charset?: string, + * DBCollat?: string, + * swapPre?: string, + * encrypt?: bool, + * compress?: bool, + * strictOn?: bool, + * failover?: array, + * port?: int, + * dateFormat?: array, + * foreignKeys?: bool + * }|ConnectionInterface|string|null $db * * @return BaseConnection */ @@ -402,13 +432,13 @@ function env(string $key, $default = null) * If $data is an array, then it loops over it, escaping each * 'value' of the key/value pairs. * - * @param array|string $data - * @param 'attr'|'css'|'html'|'js'|'raw'|'url' $context - * @param string|null $encoding Current encoding for escaping. - * If not UTF-8, we convert strings from this encoding - * pre-escaping and back to this encoding post-escaping. + * @param array|string>|string $data + * @param 'attr'|'css'|'html'|'js'|'raw'|'url' $context + * @param string|null $encoding Current encoding for escaping. + * If not UTF-8, we convert strings from this encoding + * pre-escaping and back to this encoding post-escaping. * - * @return array|string + * @return ($data is string ? string : array|string>) * * @throws InvalidArgumentException */ @@ -560,7 +590,7 @@ function function_usable(string $functionName): bool * 2. {namespace}/Helpers * 3. system/Helpers * - * @param array|string $filenames + * @param list|string $filenames * * @throws FileNotFoundException */ @@ -729,6 +759,8 @@ function is_windows(?bool $mock = null): bool * A convenience method to translate a string or array of them and format * the result with the intl extension's MessageFormatter. * + * @param array $args + * * @return list|string */ function lang(string $line, array $args = [], ?string $locale = null) @@ -1089,7 +1121,7 @@ function stringify_attributes($attributes, bool $js = false): string { $atts = ''; - if ($attributes === '' || $attributes === [] || $attributes === null) { + if (in_array($attributes, ['', [], null], true)) { return $atts; } @@ -1194,7 +1226,7 @@ function view_cell(string $library, $params = null, int $ttl = 0, ?string $cache /** * Get the class "basename" of the given object / class. * - * @param object|string $class + * @param class-string|object $class * * @return string * @@ -1212,9 +1244,9 @@ function class_basename($class) /** * Returns all traits used by a class, its parent classes and trait of their traits. * - * @param object|string $class + * @param class-string|object $class * - * @return array + * @return array * * @codeCoverageIgnore */ @@ -1238,9 +1270,9 @@ function class_uses_recursive($class) /** * Returns all traits used by a trait and its traits. * - * @param string $trait + * @param class-string $trait * - * @return array + * @return array * * @codeCoverageIgnore */ diff --git a/system/Config/Services.php b/system/Config/Services.php index c9266a89..c2a73f83 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -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 !== null && $handler !== '' && $handler !== '0' ? $handler : $config->defaultHandler; + $handler = in_array($handler, [null, '', '0'], true) ? $config->defaultHandler : $handler; $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 !== null && $locale !== '' && $locale !== '0' ? $locale : $requestLocale; + $locale = in_array($locale, [null, '', '0'], true) ? $requestLocale : $locale; 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 !== null && $viewPath !== '' && $viewPath !== '0' ? $viewPath : (new Paths())->viewDirectory; + $viewPath = in_array($viewPath, [null, '', '0'], true) ? (new Paths())->viewDirectory : $viewPath; $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 !== null && $viewPath !== '' && $viewPath !== '0' ? $viewPath : (new Paths())->viewDirectory; + $viewPath = in_array($viewPath, [null, '', '0'], true) ? (new Paths())->viewDirectory : $viewPath; $config ??= config(ViewConfig::class); return new View($config, $viewPath, AppServices::get('locator'), CI_DEBUG, AppServices::get('logger')); diff --git a/system/Controller.php b/system/Controller.php index 9c6e7ee3..af74f3a3 100644 --- a/system/Controller.php +++ b/system/Controller.php @@ -25,8 +25,6 @@ use Psr\Log\LoggerInterface; /** - * Class Controller - * * @see \CodeIgniter\ControllerTest */ class Controller diff --git a/system/Cookie/Cookie.php b/system/Cookie/Cookie.php index ad461c72..e3d2488e 100644 --- a/system/Cookie/Cookie.php +++ b/system/Cookie/Cookie.php @@ -225,9 +225,19 @@ public static function fromHeaderString(string $cookie, bool $raw = false) /** * Construct a new Cookie instance. * - * @param string $name The cookie's name - * @param string $value The cookie's value - * @param array{prefix?: string, max-age?: int|numeric-string, expires?: DateTimeInterface|int|string, path?: string, domain?: string, secure?: bool, httponly?: bool, samesite?: string, raw?: bool} $options The cookie's options + * @param string $name The cookie's name + * @param string $value The cookie's value + * @param array{ + * prefix?: string, + * max-age?: int|numeric-string, + * expires?: DateTimeInterface|int|string, + * path?: string, + * domain?: string, + * secure?: bool, + * httponly?: bool, + * samesite?: string, + * raw?: bool, + * } $options The cookie's options * * @throws CookieException */ @@ -497,7 +507,7 @@ public function withExpired() */ public function withPath(?string $path) { - $path = $path !== null && $path !== '' && $path !== '0' ? $path : self::$defaults['path']; + $path = in_array($path, [null, '', '0'], true) ? self::$defaults['path'] : $path; $this->validatePrefix($this->prefix, $this->secure, $path, $this->domain); $cookie = clone $this; diff --git a/system/DataCaster/DataCaster.php b/system/DataCaster/DataCaster.php index 81ebe233..5ef45796 100644 --- a/system/DataCaster/DataCaster.php +++ b/system/DataCaster/DataCaster.php @@ -28,6 +28,12 @@ use CodeIgniter\Entity\Exceptions\CastException; use CodeIgniter\Exceptions\InvalidArgumentException; +/** + * @phpstan-type cast_handlers array> + * + * @see CodeIgniter\DataCaster\DataCasterTest + * @see CodeIgniter\Entity\EntityTest + */ final class DataCaster { /** @@ -38,9 +44,9 @@ final class DataCaster private array $types = []; /** - * Convert handlers + * Convert handlers. * - * @var array [type => classname] + * @var cast_handlers [type => classname] */ private array $castHandlers = [ 'array' => ArrayCast::class, @@ -59,10 +65,10 @@ final class DataCaster ]; /** - * @param array|null $castHandlers Custom convert handlers - * @param array|null $types [field => type] - * @param object|null $helper Helper object. - * @param bool $strict Strict mode? Set to false for casts for Entity. + * @param cast_handlers|null $castHandlers Custom convert handlers + * @param array|null $types [field => type] + * @param object|null $helper Helper object. + * @param bool $strict Strict mode? Set to `false` for casts for Entity. */ public function __construct( ?array $castHandlers = null, @@ -70,7 +76,7 @@ public function __construct( private readonly ?object $helper = null, private readonly bool $strict = true, ) { - $this->castHandlers = array_merge($this->castHandlers, $castHandlers); + $this->castHandlers = array_merge($this->castHandlers, $castHandlers ?? []); if ($types !== null) { $this->setTypes($types); @@ -113,12 +119,16 @@ public function setTypes(array $types): static * Add ? at the beginning of the type (i.e. ?string) to get `null` * instead of casting $value when $value is null. * - * @param mixed $value The value to convert - * @param string $field The field name - * @param 'get'|'set' $method Allowed to "get" and "set" + * @param mixed $value The value to convert + * @param string $field The field name + * @param string $method Allowed to "get" and "set" */ public function castAs(mixed $value, string $field, string $method = 'get'): mixed { + if ($method !== 'get' && $method !== 'set') { + throw CastException::forInvalidMethod($method); + } + // If the type is not defined, return as it is. if (! isset($this->types[$field])) { return $value; diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 82442fbc..fd33bf40 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -41,7 +41,7 @@ class BaseBuilder /** * QB SELECT data * - * @var array + * @var list */ protected $QBSelect = []; @@ -1152,7 +1152,7 @@ protected function _like($field, string $match = '', string $type = 'AND ', stri return $this; } - $keyValue = ! is_array($field) ? [$field => $match] : $field; + $keyValue = is_array($field) ? $field : [$field => $match]; foreach ($keyValue as $k => $v) { if ($insensitiveSearch) { @@ -2113,7 +2113,7 @@ public function onConstraint($set) if (is_string($set)) { $set = explode(',', $set); - $set = array_map(static fn ($key): string => trim($key), $set); + $set = array_map(trim(...), $set); } if ($set instanceof RawSql) { @@ -2157,7 +2157,7 @@ public function setQueryAsData($query, ?string $alias = null, $columns = null): if (is_string($query)) { if ($columns !== null && is_string($columns)) { $columns = explode(',', $columns); - $columns = array_map(static fn ($key): string => trim($key), $columns); + $columns = array_map(trim(...), $columns); } $columns = (array) $columns; @@ -3065,7 +3065,7 @@ protected function compileSelect($selectOverride = false): string if ($selectOverride !== false) { $sql = $selectOverride; } else { - $sql = (! $this->QBDistinct) ? 'SELECT ' : 'SELECT DISTINCT '; + $sql = $this->QBDistinct ? 'SELECT DISTINCT ' : 'SELECT '; if (empty($this->QBSelect)) { $sql .= '*'; @@ -3257,6 +3257,9 @@ protected function compileOrderBy(): string { if (is_array($this->QBOrderBy) && $this->QBOrderBy !== []) { foreach ($this->QBOrderBy as &$orderBy) { + if (is_string($orderBy)) { + continue; + } if ($orderBy['escape'] !== false && ! $this->isLiteral($orderBy['field'])) { $orderBy['field'] = $this->db->protectIdentifiers($orderBy['field']); } @@ -3264,11 +3267,7 @@ protected function compileOrderBy(): string $orderBy = $orderBy['field'] . $orderBy['direction']; } - return $this->QBOrderBy = "\nORDER BY " . implode(', ', $this->QBOrderBy); - } - - if (is_string($this->QBOrderBy)) { - return $this->QBOrderBy; + return "\nORDER BY " . implode(', ', $this->QBOrderBy); } return ''; diff --git a/system/Database/BaseUtils.php b/system/Database/BaseUtils.php index 26957ffe..a9310e1d 100644 --- a/system/Database/BaseUtils.php +++ b/system/Database/BaseUtils.php @@ -250,7 +250,7 @@ public function getXMLFromResult(ResultInterface $query, array $params = []): st $xml .= $tab . '<' . $element . '>' . $newline; foreach ($row as $key => $val) { - $val = (! empty($val)) ? xml_convert((string) $val) : ''; + $val = empty($val) ? '' : xml_convert((string) $val); $xml .= $tab . $tab . '<' . $key . '>' . $val . '' . $newline; } diff --git a/system/Database/Config.php b/system/Database/Config.php index 6f25ad3c..fcf700ee 100644 --- a/system/Database/Config.php +++ b/system/Database/Config.php @@ -81,7 +81,9 @@ public static function connect($group = null, bool $getShared = true) $connection = static::$factory->load($config, $group); - static::$instances[$group] = $connection; + if ($getShared) { + static::$instances[$group] = $connection; + } return $connection; } diff --git a/system/Database/Database.php b/system/Database/Database.php index 80e902d5..5582e402 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 === 0 || $dsn === '' || $dsn === '0' || $dsn === [] || $dsn === false || $dsn === null) { + if (in_array($dsn, [0, '', '0', [], false, null], true)) { throw new InvalidArgumentException('Your DSN connection string is invalid.'); } @@ -136,9 +136,9 @@ protected function parseDSN(array $params): array */ protected function initDriver(string $driver, string $class, $argument): object { - $classname = (! str_contains($driver, '\\')) - ? "CodeIgniter\\Database\\{$driver}\\{$class}" - : $driver . '\\' . $class; + $classname = str_contains($driver, '\\') + ? $driver . '\\' . $class + : "CodeIgniter\\Database\\{$driver}\\{$class}"; return new $classname($argument); } diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index fdff8658..01c4e5ff 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -47,7 +47,7 @@ class MigrationRunner * * @var string|null */ - protected $namespace; + protected $namespace = APP_NAMESPACE; /** * The database Group to migrate. @@ -136,8 +136,6 @@ public function __construct(MigrationsConfig $config, $db = null) $this->enabled = $config->enabled ?? false; $this->table = $config->table ?? 'migrations'; - $this->namespace = APP_NAMESPACE; - // Even if a DB connection is passed, since it is a test, // it is assumed to use the default group name $this->group = is_string($db) ? $db : config(Database::class)->defaultGroup; @@ -659,7 +657,7 @@ public function getHistory(string $group = 'default'): array $query = $builder->orderBy('id', 'ASC')->get(); - return ! empty($query) ? $query->getResultObject() : []; + return empty($query) ? [] : $query->getResultObject(); } /** @@ -676,7 +674,7 @@ public function getBatchHistory(int $batch, $order = 'asc'): array ->orderBy('id', $order) ->get(); - return ! empty($query) ? $query->getResultObject() : []; + return empty($query) ? [] : $query->getResultObject(); } /** diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 2a9f9d34..a1c0dcce 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -123,7 +123,7 @@ public function connect(bool $persistent = false) $this->mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); } - if (isset($this->strictOn)) { + if ($this->strictOn !== null) { if ($this->strictOn) { $this->mysqli->options( MYSQLI_INIT_COMMAND, diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index 67921d2c..796b4129 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -349,12 +349,25 @@ protected function _fieldData(string $table): array $retval[$i]->max_length = $length; $retval[$i]->nullable = $query[$i]->NULLABLE === 'Y'; - $retval[$i]->default = $query[$i]->DATA_DEFAULT; + $retval[$i]->default = $this->normalizeDefault($query[$i]->DATA_DEFAULT); } return $retval; } + /** + * Removes trailing whitespace from default values + * returned in database column metadata queries. + */ + private function normalizeDefault(?string $default): ?string + { + if ($default === null) { + return $default; + } + + return rtrim($default); + } + /** * Returns an array of objects with index data * diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 9b480975..71549ad0 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -382,7 +382,7 @@ protected function _indexData(string $table): array $obj = new stdClass(); $obj->name = $row->indexname; $_fields = explode(',', preg_replace('/^.*\((.+?)\)$/', '$1', trim($row->indexdef))); - $obj->fields = array_map(static fn ($v): string => trim($v), $_fields); + $obj->fields = array_map(trim(...), $_fields); if (str_starts_with($row->indexdef, 'CREATE UNIQUE INDEX pk')) { $obj->type = 'PRIMARY'; diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index 8b18d7d4..d8e7d588 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -109,7 +109,7 @@ protected function _alterTable(string $alterType, string $table, $processedField if (! empty($field['default'])) { $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($field['name']) - . " SET DEFAULT {$field['default']}"; + . " SET {$field['default']}"; } $nullable = true; // Nullable by default. diff --git a/system/Database/SQLSRV/Builder.php b/system/Database/SQLSRV/Builder.php index ec4d4119..0b17a52f 100644 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -584,7 +584,7 @@ protected function compileSelect($selectOverride = false): string if ($selectOverride !== false) { $sql = $selectOverride; } else { - $sql = (! $this->QBDistinct) ? 'SELECT ' : 'SELECT DISTINCT '; + $sql = $this->QBDistinct ? 'SELECT DISTINCT ' : 'SELECT '; // SQL Server can't work with select * if group by is specified if (empty($this->QBSelect) && $this->QBGroupBy !== [] && is_array($this->QBGroupBy)) { diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index 7ef4f319..1ebd7390 100644 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -266,7 +266,7 @@ protected function _indexData(string $table): array $obj->name = $row->index_name; $_fields = explode(',', trim($row->index_keys)); - $obj->fields = array_map(static fn ($v): string => trim($v), $_fields); + $obj->fields = array_map(trim(...), $_fields); if (str_contains($row->index_description, 'primary key located on')) { $obj->type = 'PRIMARY'; @@ -384,12 +384,42 @@ protected function _fieldData(string $table): array ); $retVal[$i]->nullable = $query[$i]->IS_NULLABLE !== 'NO'; - $retVal[$i]->default = $query[$i]->COLUMN_DEFAULT; + $retVal[$i]->default = $this->normalizeDefault($query[$i]->COLUMN_DEFAULT); } return $retVal; } + /** + * Normalizes SQL Server COLUMN_DEFAULT values. + * Removes wrapping parentheses and handles basic conversions. + */ + private function normalizeDefault(?string $default): ?string + { + if ($default === null) { + return null; + } + + $default = trim($default); + + // Remove outer parentheses (handles both single and double wrapping) + while (preg_match('/^\((.*)\)$/', $default, $matches)) { + $default = trim($matches[1]); + } + + // Handle NULL literal + if (strcasecmp($default, 'NULL') === 0) { + return null; + } + + // Handle string literals - remove quotes and unescape + if (preg_match("/^'(.*)'$/s", $default, $matches)) { + return str_replace("''", "'", $matches[1]); + } + + return $default; + } + /** * Begin Transaction */ diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index f35d8e7b..b64c25a0 100644 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -253,8 +253,27 @@ protected function _alterTable(string $alterType, string $table, $processedField } if (! empty($field['default'])) { - $sqls[] = $sql . ' ALTER COLUMN ADD CONSTRAINT ' . $this->db->escapeIdentifiers($field['name']) . '_def' - . " DEFAULT {$field['default']} FOR " . $this->db->escapeIdentifiers($field['name']); + $fullTable = $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table); + $colName = $field['name']; // bare, for sys.columns lookup + + // find the existing default constraint name for this column + $findSql = <<db->query($findSql)->getRowArray(); + if (isset($toDrop['constraint_name']) && $toDrop['constraint_name'] !== '') { + $sqls[] = $sql . ' DROP CONSTRAINT ' . $this->db->escapeIdentifiers($toDrop['constraint_name']); + } + + $sqls[] = $sql . ' ADD CONSTRAINT ' . $this->db->escapeIdentifiers($field['name'] . '_def') + . "{$field['default']} FOR " . $this->db->escapeIdentifiers($field['name']); } $nullable = true; // Nullable by default. @@ -379,7 +398,7 @@ protected function _attributeType(array &$attributes) // https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver16#remarks $maxLength = max( array_map( - static fn ($value): int => strlen($value), + strlen(...), $attributes['CONSTRAINT'], ), ); diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 0d3290b3..ec73a75b 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -107,7 +107,7 @@ public function connect(bool $persistent = false) $this->database = WRITEPATH . $this->database; } - $sqlite = (! isset($this->password) || $this->password !== '') + $sqlite = ($this->password === null || $this->password === '') ? new SQLite3($this->database) : new SQLite3($this->database, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->password); diff --git a/system/Debug/BaseExceptionHandler.php b/system/Debug/BaseExceptionHandler.php index 3966b2ee..b9180a98 100644 --- a/system/Debug/BaseExceptionHandler.php +++ b/system/Debug/BaseExceptionHandler.php @@ -13,6 +13,8 @@ namespace CodeIgniter\Debug; +use CodeIgniter\HTTP\CLIRequest; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Config\Exceptions as ExceptionsConfig; @@ -54,6 +56,8 @@ public function __construct(ExceptionsConfig $config) /** * The main entry point into the handler. * + * @param CLIRequest|IncomingRequest $request + * * @return void */ abstract public function handle( diff --git a/system/Debug/ExceptionHandler.php b/system/Debug/ExceptionHandler.php index 68ab5feb..805b5691 100644 --- a/system/Debug/ExceptionHandler.php +++ b/system/Debug/ExceptionHandler.php @@ -13,8 +13,10 @@ namespace CodeIgniter\Debug; +use Closure; use CodeIgniter\API\ResponseTrait; use CodeIgniter\Exceptions\PageNotFoundException; +use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; @@ -41,6 +43,8 @@ final class ExceptionHandler extends BaseExceptionHandler implements ExceptionHa /** * Determines the correct way to display the error. + * + * @param CLIRequest|IncomingRequest $request */ public function handle( Throwable $exception, @@ -82,6 +86,12 @@ public function handle( ? $this->collectVars($exception, $statusCode) : ''; + // Sanitize data to remove non-JSON-serializable values (resources, closures) + // before formatting for API responses (JSON, XML, etc.) + if ($data !== '') { + $data = $this->sanitizeData($data); + } + $this->respond($data, $statusCode)->send(); if (ENVIRONMENT !== 'testing') { @@ -164,4 +174,53 @@ private function isDisplayErrorsEnabled(): bool true, ); } + + /** + * Sanitizes data to remove non-JSON-serializable values like resources and closures. + * This is necessary for API responses that need to be JSON/XML encoded. + * + * @param array $seen Used internally to prevent infinite recursion + */ + private function sanitizeData(mixed $data, array &$seen = []): mixed + { + $type = gettype($data); + + switch ($type) { + case 'resource': + case 'resource (closed)': + return '[Resource #' . (int) $data . ']'; + + case 'array': + $result = []; + + foreach ($data as $key => $value) { + $result[$key] = $this->sanitizeData($value, $seen); + } + + return $result; + + case 'object': + $oid = spl_object_id($data); + if (isset($seen[$oid])) { + return '[' . $data::class . ' Object *RECURSION*]'; + } + $seen[$oid] = true; + + if ($data instanceof Closure) { + return '[Closure]'; + } + + $result = []; + + foreach ((array) $data as $key => $value) { + $cleanKey = preg_replace('/^\x00.*\x00/', '', (string) $key); + $result[$cleanKey] = $this->sanitizeData($value, $seen); + } + + return $result; + + default: + return $data; + } + } } diff --git a/system/Debug/ExceptionHandlerInterface.php b/system/Debug/ExceptionHandlerInterface.php index 05915c63..7f91b3c5 100644 --- a/system/Debug/ExceptionHandlerInterface.php +++ b/system/Debug/ExceptionHandlerInterface.php @@ -13,6 +13,8 @@ namespace CodeIgniter\Debug; +use CodeIgniter\HTTP\CLIRequest; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Throwable; @@ -21,6 +23,8 @@ interface ExceptionHandlerInterface { /** * Determines the correct way to display the error. + * + * @param CLIRequest|IncomingRequest $request */ public function handle( Throwable $exception, diff --git a/system/Debug/Timer.php b/system/Debug/Timer.php index 37e99019..98b1299a 100644 --- a/system/Debug/Timer.php +++ b/system/Debug/Timer.php @@ -46,7 +46,7 @@ class Timer public function start(string $name, ?float $time = null) { $this->timers[strtolower($name)] = [ - 'start' => ! empty($time) ? $time : microtime(true), + 'start' => empty($time) ? microtime(true) : $time, 'end' => null, ]; diff --git a/system/Debug/Toolbar/Collectors/Database.php b/system/Debug/Toolbar/Collectors/Database.php index df876422..be57931b 100644 --- a/system/Debug/Toolbar/Collectors/Database.php +++ b/system/Debug/Toolbar/Collectors/Database.php @@ -104,7 +104,7 @@ public static function collect(Query $query) static::$queries[] = [ 'query' => $query, 'string' => $queryString, - 'duplicate' => in_array($queryString, array_column(static::$queries, 'string', null), true), + 'duplicate' => in_array($queryString, array_column(static::$queries, 'string'), true), 'trace' => $backtrace, ]; } @@ -147,8 +147,7 @@ protected function formatTimelineData(): array */ public function display(): array { - $data = []; - $data['queries'] = array_map(static function (array $query): array { + return ['queries' => array_map(static function (array $query): array { $isDuplicate = $query['duplicate'] === true; $firstNonSystemLine = ''; @@ -195,9 +194,7 @@ public function display(): array 'trace-file' => $firstNonSystemLine, 'qid' => md5($query['query'] . Time::now()->format('0.u00 U')), ]; - }, static::$queries); - - return $data; + }, static::$queries)]; } /** diff --git a/system/Debug/Toolbar/Collectors/Logs.php b/system/Debug/Toolbar/Collectors/Logs.php index c6e22fc0..f9fdabec 100644 --- a/system/Debug/Toolbar/Collectors/Logs.php +++ b/system/Debug/Toolbar/Collectors/Logs.php @@ -47,7 +47,7 @@ class Logs extends BaseCollector * * @var list */ - protected $data; + protected $data = []; /** * Returns the data of this collector to be formatted in the toolbar. @@ -68,7 +68,7 @@ public function isEmpty(): bool { $this->collectLogs(); - return $this->data !== []; + return $this->data === []; } /** diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index ddaa2386..9e4547b6 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -337,6 +337,11 @@ var ciDebugBar = { const OUTER_ELEMENTS = ["HTML", "BODY", "HEAD"]; var getValidElementInner = function (node, reverse) { + // handle null node + if (node === null) { + return null; + } + // handle invalid tags if (OUTER_ELEMENTS.indexOf(node.nodeName) !== -1) { for (var i = 0; i < document.body.children.length; ++i) { @@ -440,7 +445,7 @@ var ciDebugBar = { var debugPath = document.createElement("div"); // path var childArray = startElement[0].parentNode.childNodes; // target child array var parent = startElement[0].parentNode; - var start, end; + let start, end; // setup container debugDiv.classList.add("debug-view"); @@ -785,7 +790,7 @@ var ciDebugBar = { '">' + row.innerText.replace( patt, - '' + '' ) + '' + ""; diff --git a/system/Email/Email.php b/system/Email/Email.php index 9ed8465d..19282e7e 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -839,7 +839,7 @@ public function setNewline($newline = "\n") */ public function setCRLF($CRLF = "\n") { - $this->CRLF = ! in_array($CRLF, ["\n", "\r\n", "\r"], true) ? "\n" : $CRLF; + $this->CRLF = in_array($CRLF, ["\n", "\r\n", "\r"], true) ? $CRLF : "\n"; return $this; } @@ -2222,7 +2222,7 @@ protected function mimeTypes($ext = '') { $mime = Mimes::guessTypeFromExtension(strtolower($ext)); - return ! empty($mime) ? $mime : 'application/x-unknown-content-type'; + return empty($mime) ? 'application/x-unknown-content-type' : $mime; } public function __destruct() diff --git a/system/Entity/Cast/ArrayCast.php b/system/Entity/Cast/ArrayCast.php index 1c810645..6860b414 100644 --- a/system/Entity/Cast/ArrayCast.php +++ b/system/Entity/Cast/ArrayCast.php @@ -13,14 +13,8 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class ArrayCast - */ class ArrayCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): array { if (is_string($value) && (str_starts_with($value, 'a:') || str_starts_with($value, 's:'))) { @@ -30,9 +24,6 @@ public static function get($value, array $params = []): array return (array) $value; } - /** - * {@inheritDoc} - */ public static function set($value, array $params = []): string { return serialize($value); diff --git a/system/Entity/Cast/BaseCast.php b/system/Entity/Cast/BaseCast.php index 34cb28e7..c93e32c0 100644 --- a/system/Entity/Cast/BaseCast.php +++ b/system/Entity/Cast/BaseCast.php @@ -13,32 +13,13 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class BaseCast - */ abstract class BaseCast implements CastInterface { - /** - * Get - * - * @param array|bool|float|int|object|string|null $value Data - * @param array $params Additional param - * - * @return array|bool|float|int|object|string|null - */ public static function get($value, array $params = []) { return $value; } - /** - * Set - * - * @param array|bool|float|int|object|string|null $value Data - * @param array $params Additional param - * - * @return array|bool|float|int|object|string|null - */ public static function set($value, array $params = []) { return $value; diff --git a/system/Entity/Cast/BooleanCast.php b/system/Entity/Cast/BooleanCast.php index 8d95ba24..7ac9f0ba 100644 --- a/system/Entity/Cast/BooleanCast.php +++ b/system/Entity/Cast/BooleanCast.php @@ -13,14 +13,8 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class BooleanCast - */ class BooleanCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): bool { return (bool) $value; diff --git a/system/Entity/Cast/CSVCast.php b/system/Entity/Cast/CSVCast.php index 7b8be55d..c78a1315 100644 --- a/system/Entity/Cast/CSVCast.php +++ b/system/Entity/Cast/CSVCast.php @@ -13,22 +13,13 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class CSVCast - */ class CSVCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): array { return explode(',', $value); } - /** - * {@inheritDoc} - */ public static function set($value, array $params = []): string { return implode(',', $value); diff --git a/system/Entity/Cast/CastInterface.php b/system/Entity/Cast/CastInterface.php index 9d790e8e..8f837fe8 100644 --- a/system/Entity/Cast/CastInterface.php +++ b/system/Entity/Cast/CastInterface.php @@ -14,8 +14,6 @@ namespace CodeIgniter\Entity\Cast; /** - * Interface CastInterface - * * The methods work at (1)(4) only. * [App Code] --- (1) --> [Entity] --- (2) --> [Database] * [App Code] <-- (4) --- [Entity] <-- (3) --- [Database] diff --git a/system/Entity/Cast/DatetimeCast.php b/system/Entity/Cast/DatetimeCast.php index 88b7b292..72206ca8 100644 --- a/system/Entity/Cast/DatetimeCast.php +++ b/system/Entity/Cast/DatetimeCast.php @@ -17,9 +17,6 @@ use DateTime; use Exception; -/** - * Class DatetimeCast - */ class DatetimeCast extends BaseCast { /** diff --git a/system/Entity/Cast/FloatCast.php b/system/Entity/Cast/FloatCast.php index 642e849e..1a767c09 100644 --- a/system/Entity/Cast/FloatCast.php +++ b/system/Entity/Cast/FloatCast.php @@ -13,14 +13,8 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class FloatCast - */ class FloatCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): float { return (float) $value; diff --git a/system/Entity/Cast/IntBoolCast.php b/system/Entity/Cast/IntBoolCast.php index fb1c470e..94e65df8 100644 --- a/system/Entity/Cast/IntBoolCast.php +++ b/system/Entity/Cast/IntBoolCast.php @@ -14,8 +14,6 @@ namespace CodeIgniter\Entity\Cast; /** - * Int Bool Cast - * * DB column: int (0/1) <--> Class property: bool */ final class IntBoolCast extends BaseCast diff --git a/system/Entity/Cast/IntegerCast.php b/system/Entity/Cast/IntegerCast.php index c0ecec73..84bf0921 100644 --- a/system/Entity/Cast/IntegerCast.php +++ b/system/Entity/Cast/IntegerCast.php @@ -13,14 +13,8 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class IntegerCast - */ class IntegerCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): int { return (int) $value; diff --git a/system/Entity/Cast/JsonCast.php b/system/Entity/Cast/JsonCast.php index ef389269..245de29f 100644 --- a/system/Entity/Cast/JsonCast.php +++ b/system/Entity/Cast/JsonCast.php @@ -17,14 +17,8 @@ use JsonException; use stdClass; -/** - * Class JsonCast - */ class JsonCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []) { $associative = in_array('array', $params, true); diff --git a/system/Entity/Cast/ObjectCast.php b/system/Entity/Cast/ObjectCast.php index 1bee213d..10affad4 100644 --- a/system/Entity/Cast/ObjectCast.php +++ b/system/Entity/Cast/ObjectCast.php @@ -13,14 +13,8 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class ObjectCast - */ class ObjectCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): object { return (object) $value; diff --git a/system/Entity/Cast/StringCast.php b/system/Entity/Cast/StringCast.php index e4ed04b9..d538f433 100644 --- a/system/Entity/Cast/StringCast.php +++ b/system/Entity/Cast/StringCast.php @@ -13,14 +13,8 @@ namespace CodeIgniter\Entity\Cast; -/** - * Class StringCast - */ class StringCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): string { return (string) $value; diff --git a/system/Entity/Cast/TimestampCast.php b/system/Entity/Cast/TimestampCast.php index a445bade..44bdf788 100644 --- a/system/Entity/Cast/TimestampCast.php +++ b/system/Entity/Cast/TimestampCast.php @@ -15,14 +15,8 @@ use CodeIgniter\Entity\Exceptions\CastException; -/** - * Class TimestampCast - */ class TimestampCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []) { $value = strtotime($value); diff --git a/system/Entity/Cast/URICast.php b/system/Entity/Cast/URICast.php index d0d510b1..a66bfba3 100644 --- a/system/Entity/Cast/URICast.php +++ b/system/Entity/Cast/URICast.php @@ -15,14 +15,8 @@ use CodeIgniter\HTTP\URI; -/** - * Class URICast - */ class URICast extends BaseCast { - /** - * {@inheritDoc} - */ public static function get($value, array $params = []): URI { return $value instanceof URI ? $value : new URI($value); diff --git a/system/Events/Events.php b/system/Events/Events.php index ae68e205..4c255f02 100644 --- a/system/Events/Events.php +++ b/system/Events/Events.php @@ -85,7 +85,7 @@ public static function initialize() } $files = array_filter(array_map( - static fn (string $file): false|string => realpath($file), + realpath(...), $files, )); diff --git a/system/Filters/PageCache.php b/system/Filters/PageCache.php index b0013c81..7a294980 100644 --- a/system/Filters/PageCache.php +++ b/system/Filters/PageCache.php @@ -46,13 +46,7 @@ public function before(RequestInterface $request, $arguments = null) $response = service('response'); - $cachedResponse = $this->pageCache->get($request, $response); - - if ($cachedResponse instanceof ResponseInterface) { - return $cachedResponse; - } - - return null; + return $this->pageCache->get($request, $response); } /** diff --git a/system/HTTP/Exceptions/RedirectException.php b/system/HTTP/Exceptions/RedirectException.php index abb9be1e..a16dfc1a 100644 --- a/system/HTTP/Exceptions/RedirectException.php +++ b/system/HTTP/Exceptions/RedirectException.php @@ -80,7 +80,7 @@ public function getResponse(): ResponseInterface $location = $this->response->getHeaderLine('Location'); - service(('logger'))->info(sprintf( + service('logger')->info(sprintf( 'REDIRECTED ROUTE at %s', $location !== '' ? $location : substr($this->response->getHeaderLine('Refresh'), 6), )); diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index 9de285b8..3c59f2e9 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -221,7 +221,7 @@ public function getStatusCode(): int public function getReasonPhrase() { if ($this->reason === '') { - return ! empty($this->statusCode) ? static::$statusCodes[$this->statusCode] : ''; + return empty($this->statusCode) ? '' : static::$statusCodes[$this->statusCode]; } return $this->reason; diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 5bcf11de..6045f0fd 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -181,9 +181,9 @@ public static function createURIString( } if ((string) $path !== '') { - $uri .= ! str_ends_with($uri, '/') - ? '/' . ltrim($path, '/') - : ltrim($path, '/'); + $uri .= str_ends_with($uri, '/') + ? ltrim($path, '/') + : '/' . ltrim($path, '/'); } if ((string) $query !== '') { diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 5e8eece6..ba9e544a 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -109,8 +109,10 @@ public function __construct(?UserAgents $config = null) { $this->config = $config ?? config(UserAgents::class); - if (isset($_SERVER['HTTP_USER_AGENT'])) { - $this->agent = trim($_SERVER['HTTP_USER_AGENT']); + $userAgent = service('superglobals')->server('HTTP_USER_AGENT'); + + if ($userAgent !== null) { + $this->agent = trim($userAgent); $this->compileData(); } } @@ -175,10 +177,11 @@ public function isMobile(?string $key = null): bool public function isReferral(): bool { if (! isset($this->referrer)) { - if (empty($_SERVER['HTTP_REFERER'])) { + $referer = service('superglobals')->server('HTTP_REFERER'); + if ($referer === null || $referer === '') { $this->referrer = false; } else { - $refererHost = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); + $refererHost = @parse_url($referer, PHP_URL_HOST); $ownHost = parse_url(\base_url(), PHP_URL_HOST); $this->referrer = ($refererHost && $refererHost !== $ownHost); @@ -241,7 +244,9 @@ public function getMobile(): string */ public function getReferrer(): string { - return empty($_SERVER['HTTP_REFERER']) ? '' : trim($_SERVER['HTTP_REFERER']); + $referrer = service('superglobals')->server('HTTP_REFERER'); + + return $referrer === null ? '' : trim($referrer); } /** diff --git a/system/Helpers/cookie_helper.php b/system/Helpers/cookie_helper.php index 18239fe2..fb1c1f36 100644 --- a/system/Helpers/cookie_helper.php +++ b/system/Helpers/cookie_helper.php @@ -24,16 +24,29 @@ * * Accepts seven parameters, or you can submit an associative * array in the first parameter containing all the values. + * ['', 'value', 'expire', '', '', '', '', '', 'name'] * - * @param array|Cookie|string $name Cookie name / array containing binds / Cookie object - * @param string $value The value of the cookie - * @param int $expire The number of seconds until expiration - * @param string $domain For site-wide cookie. Usually: .yourdomain.com - * @param string $path The cookie path - * @param string $prefix The cookie prefix ('': the default prefix) - * @param bool|null $secure True makes the cookie secure - * @param bool|null $httpOnly True makes the cookie accessible via http(s) only (no javascript) - * @param string|null $sameSite The cookie SameSite value + * @param array{ + * name?: string, + * value?: string, + * prefix?: string, + * max-age?: int|numeric-string, + * expire?: DateTimeInterface|int|string, + * path?: string, + * domain?: string, + * secure?: bool, + * httponly?: bool, + * samesite?: string, + * raw?: bool + * }|Cookie|string $name Cookie name / array containing binds / Cookie object + * @param string $value The value of the cookie + * @param int $expire The number of seconds until expiration + * @param string $domain For site-wide cookie. Usually: .yourdomain.com + * @param string $path The cookie path + * @param string $prefix The cookie prefix ('': the default prefix) + * @param bool|null $secure True makes the cookie secure + * @param bool|null $httpOnly True makes the cookie accessible via http(s) only (no javascript) + * @param string|null $sameSite The cookie SameSite value * * @see \CodeIgniter\HTTP\Response::setCookie() */ @@ -62,7 +75,7 @@ function set_cookie( * '': the prefix in Config\Cookie * null: no prefix * - * @return array|string|null + * @return array|string|null * * @see \CodeIgniter\HTTP\IncomingRequest::getCookie() */ diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 7b89bb9e..a9415821 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -350,7 +350,7 @@ function form_checkbox($data = '', string $value = '', bool $checked = false, $e { $defaults = [ 'type' => 'checkbox', - 'name' => (! is_array($data) ? $data : ''), + 'name' => is_array($data) ? '' : $data, 'value' => $value, ]; diff --git a/system/Honeypot/Exceptions/HoneypotException.php b/system/Honeypot/Exceptions/HoneypotException.php index 7ff493e4..5185f6d3 100644 --- a/system/Honeypot/Exceptions/HoneypotException.php +++ b/system/Honeypot/Exceptions/HoneypotException.php @@ -41,6 +41,8 @@ public static function forNoNameField() * Thrown when the hidden value of config is false. * * @return static + * + * @deprecated 4.6.4 Never used. */ public static function forNoHiddenValue() { diff --git a/system/I18n/Exceptions/I18nException.php b/system/I18n/Exceptions/I18nException.php index 79c9e832..ec9dd327 100644 --- a/system/I18n/Exceptions/I18nException.php +++ b/system/I18n/Exceptions/I18nException.php @@ -72,7 +72,7 @@ public static function forInvalidOverDay(string $lastDay, string $day) */ public static function forInvalidHour(string $hour) { - return new static(lang('Time.invalidHour', [$hour])); + return new static(lang('Time.invalidHours', [$hour])); } /** diff --git a/system/I18n/TimeTrait.php b/system/I18n/TimeTrait.php index 42278c96..7742c73a 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 !== null && $locale !== '' && $locale !== '0' ? $locale : Locale::getDefault(); + $this->locale = in_array($locale, [null, '', '0'], true) ? Locale::getDefault() : $locale; $time ??= ''; @@ -940,7 +940,7 @@ public function sameAs($testTime, ?string $timezone = null): bool if ($testTime instanceof DateTimeInterface) { $testTime = $testTime->format('Y-m-d H:i:s.u O'); } elseif (is_string($testTime)) { - $timezone = $timezone !== null && $timezone !== '' && $timezone !== '0' ? $timezone : $this->timezone; + $timezone = in_array($timezone, [null, '', '0'], true) ? $this->timezone : $timezone; $timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); $testTime = new DateTime($testTime, $timezone); $testTime = $testTime->format('Y-m-d H:i:s.u O'); @@ -1102,7 +1102,7 @@ public function getUTCObject($time, ?string $timezone = null) if ($time instanceof static) { $time = $time->toDateTime(); } elseif (is_string($time)) { - $timezone = $timezone !== null && $timezone !== '' && $timezone !== '0' ? $timezone : $this->timezone; + $timezone = in_array($timezone, [null, '', '0'], true) ? $this->timezone : $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 233ea2d0..bae055b1 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -72,7 +72,7 @@ public function __construct($config = null) */ public function _resize(bool $maintainRatio = false) { - $source = ! empty($this->resource) ? $this->resource : $this->image()->getPathname(); + $source = empty($this->resource) ? $this->image()->getPathname() : $this->resource; $destination = $this->getResourcePath(); $escape = '\\'; @@ -99,7 +99,7 @@ public function _resize(bool $maintainRatio = false) */ public function _crop() { - $source = ! empty($this->resource) ? $this->resource : $this->image()->getPathname(); + $source = empty($this->resource) ? $this->image()->getPathname() : $this->resource; $destination = $this->getResourcePath(); $extent = ' '; @@ -126,7 +126,7 @@ protected function _rotate(int $angle) { $angle = '-rotate ' . $angle; - $source = ! empty($this->resource) ? $this->resource : $this->image()->getPathname(); + $source = empty($this->resource) ? $this->image()->getPathname() : $this->resource; $destination = $this->getResourcePath(); $action = ' ' . $angle . ' ' . escapeshellarg($source) . ' ' . escapeshellarg($destination); @@ -147,7 +147,7 @@ protected function _flatten(int $red = 255, int $green = 255, int $blue = 255) { $flatten = "-background 'rgb({$red},{$green},{$blue})' -flatten"; - $source = ! empty($this->resource) ? $this->resource : $this->image()->getPathname(); + $source = empty($this->resource) ? $this->image()->getPathname() : $this->resource; $destination = $this->getResourcePath(); $action = ' ' . $flatten . ' ' . escapeshellarg($source) . ' ' . escapeshellarg($destination); @@ -168,7 +168,7 @@ protected function _flip(string $direction) { $angle = $direction === 'horizontal' ? '-flop' : '-flip'; - $source = ! empty($this->resource) ? $this->resource : $this->image()->getPathname(); + $source = empty($this->resource) ? $this->image()->getPathname() : $this->resource; $destination = $this->getResourcePath(); $action = ' ' . $angle . ' ' . escapeshellarg($source) . ' ' . escapeshellarg($destination); @@ -317,12 +317,8 @@ protected function ensureResource() */ protected function supportedFormatCheck() { - switch ($this->image()->imageType) { - case IMAGETYPE_WEBP: - if (! in_array('WEBP', Imagick::queryFormats(), true)) { - throw ImageException::forInvalidImageCreate(lang('images.webpNotSupported')); - } - break; + if ($this->image()->imageType === IMAGETYPE_WEBP && ! in_array('WEBP', Imagick::queryFormats(), true)) { + throw ImageException::forInvalidImageCreate(lang('Images.webpNotSupported')); } } @@ -411,7 +407,7 @@ protected function _text(string $text, array $options = []) // Text $cmd .= ' -annotate 0 ' . escapeshellarg($text); - $source = ! empty($this->resource) ? $this->resource : $this->image()->getPathname(); + $source = empty($this->resource) ? $this->image()->getPathname() : $this->resource; $destination = $this->getResourcePath(); $cmd = ' ' . escapeshellarg($source) . ' ' . $cmd . ' ' . escapeshellarg($destination); diff --git a/system/Language/Language.php b/system/Language/Language.php index a4034bdf..652d17a5 100644 --- a/system/Language/Language.php +++ b/system/Language/Language.php @@ -21,6 +21,8 @@ * * Locale-based, built on top of PHP internationalization. * + * @phpstan-type LoadedStrings array|string>|string|list> + * * @see \CodeIgniter\Language\LanguageTest */ class Language @@ -30,20 +32,19 @@ class Language * from files for faster retrieval on * second use. * - * @var array + * @var array> */ protected $language = []; /** - * The current language/locale to work with. + * The current locale to work with. * - * @var string + * @var non-empty-string */ protected $locale; /** - * Boolean value whether the intl - * libraries exist on the system. + * Boolean value whether the `intl` extension exists on the system. * * @var bool */ @@ -53,10 +54,13 @@ class Language * Stores filenames that have been * loaded so that we don't load them again. * - * @var array + * @var array> */ protected $loadedFiles = []; + /** + * @param non-empty-string $locale + */ public function __construct(string $locale) { $this->locale = $locale; @@ -69,6 +73,8 @@ public function __construct(string $locale) /** * Sets the current locale to use when performing string lookups. * + * @param non-empty-string|null $locale + * * @return $this */ public function setLocale(?string $locale = null) @@ -89,22 +95,24 @@ public function getLocale(): string * Parses the language string for a file, loads the file, if necessary, * getting the line. * + * @param array $args + * * @return list|string */ public function getLine(string $line, array $args = []) { - // if no file is given, just parse the line + // 1. Format the line as-is if it does not have a file. if (! str_contains($line, '.')) { return $this->formatMessage($line, $args); } - // Parse out the file name and the actual alias. - // Will load the language file and strings. + // 2. Get the formatted line using the file and line extracted from $line and the current locale. [$file, $parsedLine] = $this->parseLine($line, $this->locale); $output = $this->getTranslationOutput($this->locale, $file, $parsedLine); - if ($output === null && strpos($this->locale, '-')) { + // 3. If not found, try the locale without region (e.g., 'en-US' -> 'en'). + if ($output === null && str_contains($this->locale, '-')) { [$locale] = explode('-', $this->locale, 2); [$file, $parsedLine] = $this->parseLine($line, $locale); @@ -112,57 +120,66 @@ public function getLine(string $line, array $args = []) $output = $this->getTranslationOutput($locale, $file, $parsedLine); } - // if still not found, try English + // 4. If still not found, try English. if ($output === null) { [$file, $parsedLine] = $this->parseLine($line, 'en'); $output = $this->getTranslationOutput('en', $file, $parsedLine); } + // 5. Fallback to the original line if no translation was found. $output ??= $line; return $this->formatMessage($output, $args); } /** - * @return array|string|null + * @return list|string|null */ protected function getTranslationOutput(string $locale, string $file, string $parsedLine) { $output = $this->language[$locale][$file][$parsedLine] ?? null; + if ($output !== null) { return $output; } - foreach (explode('.', $parsedLine) as $row) { - if (! isset($current)) { - $current = $this->language[$locale][$file] ?? null; - } + // Fallback: try to traverse dot notation + $current = $this->language[$locale][$file] ?? null; + + if (is_array($current)) { + foreach (explode('.', $parsedLine) as $segment) { + $output = $current[$segment] ?? null; + + if ($output === null) { + break; + } - $output = $current[$row] ?? null; - if (is_array($output)) { - $current = $output; + if (is_array($output)) { + $current = $output; + } } - } - if ($output !== null) { - return $output; + if ($output !== null && ! is_array($output)) { + return $output; + } } - $row = current(explode('.', $parsedLine)); - $key = substr($parsedLine, strlen($row) + 1); + // Final fallback: try two-level access manually + [$first, $rest] = explode('.', $parsedLine, 2) + ['', '']; - return $this->language[$locale][$file][$row][$key] ?? null; + return $this->language[$locale][$file][$first][$rest] ?? null; } /** * Parses the language string which should include the * filename as the first segment (separated by period). + * + * @return array{non-empty-string, non-empty-string} */ protected function parseLine(string $line, string $locale): array { - $file = substr($line, 0, strpos($line, '.')); - $line = substr($line, strlen($file) + 1); + [$file, $line] = explode('.', $line, 2); if (! isset($this->language[$locale][$file]) || ! array_key_exists($line, $this->language[$locale][$file])) { $this->load($file, $locale); @@ -174,10 +191,10 @@ protected function parseLine(string $line, string $locale): array /** * Advanced message formatting. * - * @param array|string $message - * @param list $args + * @param list|string $message + * @param array $args * - * @return array|string + * @return ($message is list ? list : string) */ protected function formatMessage($message, array $args = []) { @@ -194,32 +211,27 @@ protected function formatMessage($message, array $args = []) } $formatted = MessageFormatter::formatMessage($this->locale, $message, $args); + if ($formatted === false) { // Format again to get the error message. try { - $fmt = new MessageFormatter($this->locale, $message); - $formatted = $fmt->format($args); - $fmtError = '"' . $fmt->getErrorMessage() . '" (' . $fmt->getErrorCode() . ')'; + $formatter = new MessageFormatter($this->locale, $message); + $formatted = $formatter->format($args); + $fmtError = sprintf('"%s" (%d)', $formatter->getErrorMessage(), $formatter->getErrorCode()); } catch (IntlException $e) { - $fmtError = '"' . $e->getMessage() . '" (' . $e->getCode() . ')'; + $fmtError = sprintf('"%s" (%d)', $e->getMessage(), $e->getCode()); } - $argsString = implode( - ', ', - array_map(static fn ($element): string => '"' . $element . '"', $args), - ); - $argsUrlEncoded = implode( - ', ', - array_map(static fn ($element): string => '"' . rawurlencode($element) . '"', $args), - ); - - log_message( - 'error', - 'Language.invalidMessageFormat: $message: "' . $message - . '", $args: ' . $argsString - . ' (urlencoded: ' . $argsUrlEncoded . '),' - . ' MessageFormatter Error: ' . $fmtError, - ); + $argsAsString = sprintf('"%s"', implode('", "', $args)); + $urlEncodedArgs = sprintf('"%s"', implode('", "', array_map(rawurlencode(...), $args))); + + log_message('error', sprintf( + 'Invalid message format: $message: "%s", $args: %s (urlencoded: %s), MessageFormatter Error: %s', + $message, + $argsAsString, + $urlEncodedArgs, + $fmtError, + )); return $message . "\n【Warning】Also, invalid string(s) was passed to the Language class. See log file for details."; } @@ -232,7 +244,7 @@ protected function formatMessage($message, array $args = []) * will return the file's contents, otherwise will merge with * the existing language lines. * - * @return list|null + * @return ($return is true ? LoadedStrings : null) */ protected function load(string $file, string $locale, bool $return = false) { @@ -270,8 +282,9 @@ protected function load(string $file, string $locale, bool $return = false) } /** - * A simple method for including files that can be - * overridden during testing. + * A simple method for including files that can be overridden during testing. + * + * @return LoadedStrings */ protected function requireFile(string $path): array { @@ -279,20 +292,25 @@ protected function requireFile(string $path): array $strings = []; foreach ($files as $file) { - // On some OS's we were seeing failures - // on this command returning boolean instead - // of array during testing, so we've removed - // the require_once for now. if (is_file($file)) { - $strings[] = require $file; + // On some OS, we were seeing failures on this command returning boolean instead + // of array during testing, so we've removed the require_once for now. + $loadedStrings = require $file; + + if (is_array($loadedStrings)) { + /** @var LoadedStrings $loadedStrings */ + $strings[] = $loadedStrings; + } } } - if (isset($strings[1])) { - $string = array_shift($strings); + $count = count($strings); + + if ($count > 1) { + $base = array_shift($strings); - $strings = array_replace_recursive($string, ...$strings); - } elseif (isset($strings[0])) { + $strings = array_replace_recursive($base, ...$strings); + } elseif ($count === 1) { $strings = $strings[0]; } diff --git a/system/Language/en/Honeypot.php b/system/Language/en/Honeypot.php new file mode 100644 index 00000000..65676da5 --- /dev/null +++ b/system/Language/en/Honeypot.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +// Honeypot language settings +return [ + 'noTemplate' => 'The HTML template for the Honeypot is not configured.', + 'noNameField' => 'The name of the Honeypot field is not set.', + 'theClientIsABot' => 'The Honeypot client may be a bot.', +]; diff --git a/system/Language/en/Language.php b/system/Language/en/Language.php index 16456c3c..2d638c8e 100644 --- a/system/Language/en/Language.php +++ b/system/Language/en/Language.php @@ -13,5 +13,6 @@ // "Language" language settings return [ + // @deprecated v4.6.3 - never used 'invalidMessageFormat' => 'Invalid message format: "{0}", args: "{1}"', ]; diff --git a/system/Language/en/Router.php b/system/Language/en/Router.php index 7bb55860..a073d98d 100644 --- a/system/Language/en/Router.php +++ b/system/Language/en/Router.php @@ -16,5 +16,5 @@ 'invalidParameter' => 'A parameter does not match the expected type.', 'missingDefaultRoute' => 'Unable to determine what should be displayed. A default route has not been specified in the routing file.', 'invalidDynamicController' => 'A dynamic controller is not allowed for security reasons. Route handler: "{0}"', - 'invalidControllerName' => 'The namespace delimiter is a backslash (\), not a slash (/). Route handler: "{0}"', + 'invalidControllerName' => 'The namespace delimiter is a backslash (\\), not a slash (/). Route handler: "{0}"', ]; diff --git a/system/Model.php b/system/Model.php index 44ed52a0..60759b9e 100644 --- a/system/Model.php +++ b/system/Model.php @@ -396,7 +396,7 @@ protected function doInsert(array $row) // If insertion succeeded then save the insert ID if ($result) { - $this->insertID = ! $this->useAutoIncrement ? $row[$this->primaryKey] : $this->db->insertID(); + $this->insertID = $this->useAutoIncrement ? $this->db->insertID() : $row[$this->primaryKey]; } return $result; diff --git a/system/Pager/PagerRenderer.php b/system/Pager/PagerRenderer.php index 2352a984..f970ee55 100644 --- a/system/Pager/PagerRenderer.php +++ b/system/Pager/PagerRenderer.php @@ -29,9 +29,12 @@ class PagerRenderer /** * First page number in the set of links to be displayed. * + * `first` and `last` will be updated by `setSurroundCount()`. + * You must call `setSurroundCount()` after instantiation. + * * @var int */ - protected $first; + protected $first = 1; /** * Last page number in the set of links to be displayed. @@ -102,10 +105,7 @@ class PagerRenderer */ public function __construct(array $details) { - // `first` and `last` will be updated by `setSurroundCount()`. - // You must call `setSurroundCount()` after instantiation. - $this->first = 1; - $this->last = $details['pageCount']; + $this->last = $details['pageCount']; $this->current = $details['currentPage']; $this->total = $details['total']; diff --git a/system/Router/AutoRouterImproved.php b/system/Router/AutoRouterImproved.php index c14c8fe9..a00c04d2 100644 --- a/system/Router/AutoRouterImproved.php +++ b/system/Router/AutoRouterImproved.php @@ -209,7 +209,7 @@ private function searchLastDefaultController(): bool } $namespaces = array_map( - fn ($segment): string => $this->translateURI($segment), + $this->translateURI(...), $segments, ); diff --git a/system/Router/Exceptions/RouterException.php b/system/Router/Exceptions/RouterException.php index 37864728..7a65603d 100644 --- a/system/Router/Exceptions/RouterException.php +++ b/system/Router/Exceptions/RouterException.php @@ -28,7 +28,7 @@ class RouterException extends FrameworkException implements ExceptionInterface */ public static function forInvalidParameterType() { - return new static(lang('Router.invalidParameterType')); + return new static(lang('Router.invalidParameter')); } /** diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 7379d014..feb161bd 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -1574,7 +1574,7 @@ private function checkHostname($hostname): bool // Has multiple hostnames if (is_array($hostname)) { - $hostnameLower = array_map('strtolower', $hostname); + $hostnameLower = array_map(strtolower(...), $hostname); return in_array(strtolower($this->httpHost), $hostnameLower, true); } diff --git a/system/Security/CheckPhpIni.php b/system/Security/CheckPhpIni.php index 35431c35..949c1d10 100644 --- a/system/Security/CheckPhpIni.php +++ b/system/Security/CheckPhpIni.php @@ -17,7 +17,7 @@ use CodeIgniter\View\Table; /** - * Checks php.ini settings + * Checks php.ini settings in production environment. * * @used-by \CodeIgniter\Commands\Utilities\PhpIniCheck * @see \CodeIgniter\Security\CheckPhpIniTest @@ -27,30 +27,33 @@ class CheckPhpIni /** * @param bool $isCli Set false if you run via Web * - * @return string|null HTML string or void in CLI + * @return ($isCli is true ? null : string) */ public static function run(bool $isCli = true, ?string $argument = null) { - $output = static::checkIni($argument); + $output = self::checkIni($argument); $thead = ['Directive', 'Global', 'Current', 'Recommended', 'Remark']; - $tbody = []; - // CLI if ($isCli) { - self::outputForCli($output, $thead, $tbody); + self::outputForCli($output, $thead); return null; } - // Web - return self::outputForWeb($output, $thead, $tbody); + return self::outputForWeb($output, $thead); } - private static function outputForCli(array $output, array $thead, array $tbody): void + /** + * @param array $output + * @param array{string, string, string, string, string} $thead + */ + private static function outputForCli(array $output, array $thead): void { + $tbody = []; + foreach ($output as $directive => $values) { - $current = $values['current'] ?? ''; + $current = $values['current']; $notRecommended = false; if ($values['recommended'] !== '') { @@ -64,16 +67,27 @@ private static function outputForCli(array $output, array $thead, array $tbody): } $directive = $notRecommended ? CLI::color($directive, 'red') : $directive; - $tbody[] = [ - $directive, $values['global'], $current, $values['recommended'], $values['remark'], + + $tbody[] = [ + $directive, + $values['global'], + $current, + $values['recommended'], + $values['remark'], ]; } CLI::table($tbody, $thead); } - private static function outputForWeb(array $output, array $thead, array $tbody): string + /** + * @param array $output + * @param array{string, string, string, string, string} $thead + */ + private static function outputForWeb(array $output, array $thead): string { + $tbody = []; + foreach ($output as $directive => $values) { $current = $values['current']; $notRecommended = false; @@ -95,27 +109,27 @@ private static function outputForWeb(array $output, array $thead, array $tbody): $directive = $notRecommended ? '' . $directive . '' : $directive; + $tbody[] = [ - $directive, $values['global'], $current, $values['recommended'], $values['remark'], + $directive, + $values['global'], + $current, + $values['recommended'], + $values['remark'], ]; } - $table = new Table(); - $template = [ - 'table_open' => '', - ]; - $table->setTemplate($template); - + $table = new Table(); + $table->setTemplate(['table_open' => '
']); $table->setHeading($thead); return '
' . $table->generate($tbody) . '
'; } /** - * @internal Used for testing purposes only. - * @testTag + * @return array */ - public static function checkIni(?string $argument = null): array + private static function checkIni(?string $argument = null): array { // Default items $items = [ @@ -151,17 +165,19 @@ public static function checkIni(?string $argument = null): array 'opcache.interned_strings_buffer' => ['recommended' => '16'], 'opcache.max_accelerated_files' => ['remark' => 'Adjust based on the number of PHP files in your project (e.g.: find your_project/ -iname \'*.php\'|wc -l)'], 'opcache.max_wasted_percentage' => ['recommended' => '10'], - 'opcache.validate_timestamps' => ['recommended' => '0', 'remark' => 'When you disabled, opcache hold your code into shared memory. Restart webserver needed'], + 'opcache.validate_timestamps' => ['recommended' => '0', 'remark' => 'When disabled, opcache will hold your code into shared memory. Restart webserver as needed'], 'opcache.revalidate_freq' => [], 'opcache.file_cache' => ['remark' => 'Location file caching, It should improve performance when SHM memory is full'], - 'opcache.file_cache_only' => ['remark' => 'Opcode caching in shared memory, Disabled when you using Windows'], - 'opcache.file_cache_fallback' => ['remark' => 'Set enable when you using Windows'], - 'opcache.save_comments' => ['recommended' => '0', 'remark' => 'Enable when you using package require docblock annotation'], + 'opcache.file_cache_only' => ['remark' => 'Opcode caching in shared memory, Disabled when you are using Windows'], + 'opcache.file_cache_fallback' => ['remark' => 'Enable when you are using Windows'], + 'opcache.save_comments' => ['recommended' => '0', 'remark' => 'Enable when your code requires to read docblock annotations at runtime'], ]; } $output = []; - $ini = ini_get_all(); + + $ini = ini_get_all(); + assert(is_array($ini)); foreach ($items as $key => $values) { $hasKeyInIni = array_key_exists($key, $ini); @@ -173,7 +189,6 @@ public static function checkIni(?string $argument = null): array ]; } - // [directive => [current_value, recommended_value]] return $output; } } diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index 4780a1bb..81d66913 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -19,6 +19,7 @@ use CodeIgniter\Database\MigrationRunner; use CodeIgniter\Database\Seeder; use CodeIgniter\Events\Events; +use CodeIgniter\HTTP\Header; use CodeIgniter\Router\RouteCollection; use CodeIgniter\Session\Handlers\ArrayHandler; use CodeIgniter\Test\Mock\MockCache; @@ -72,6 +73,8 @@ abstract class CIUnitTestCase extends TestCase /** * Store of identified traits. + * + * @var array|null */ private ?array $traits = null; @@ -109,9 +112,9 @@ abstract class CIUnitTestCase extends TestCase /** * The seed file(s) used for all tests within this test case. - * Should be fully-namespaced or relative to $basePath + * Should be fully-namespaced or relative to $basePath. * - * @var class-string|list> + * @var ''|class-string|list> */ protected $seed = ''; @@ -127,9 +130,9 @@ abstract class CIUnitTestCase extends TestCase * The namespace(s) to help us find the migration classes. * `null` is equivalent to running `spark migrate --all`. * Note that running "all" runs migrations in date order, - * but specifying namespaces runs them in namespace order (then date) + * but specifying namespaces runs them in namespace order (then date). * - * @var array|string|null + * @var list|string|null */ protected $namespace = 'Tests\Support'; @@ -156,17 +159,17 @@ abstract class CIUnitTestCase extends TestCase protected $migrations; /** - * Seeder instance + * Seeder instance. * - * @var Seeder + * @var Seeder|null */ protected $seeder; /** * Stores information needed to remove any - * rows inserted via $this->hasInDatabase(); + * rows inserted via $this->hasInDatabase(). * - * @var array + * @var list> */ protected $insertCache = []; @@ -186,27 +189,27 @@ abstract class CIUnitTestCase extends TestCase * Values to be set in the SESSION global * before running the test. * - * @var array + * @var array */ protected $session = []; /** - * Enabled auto clean op buffer after request call + * Enabled auto clean op buffer after request call. * * @var bool */ protected $clean = true; /** - * Custom request's headers + * Custom request's headers. * - * @var array + * @var array> */ protected $headers = []; /** * Allows for formatting the request body to what - * the controller is going to expect + * the controller is going to expect. * * @var string */ @@ -276,7 +279,7 @@ protected function tearDown(): void * Checks for traits with corresponding * methods for setUp or tearDown. * - * @param string $stage 'setUp' or 'tearDown' + * @param 'setUp'|'tearDown' $stage */ private function callTraitMethods(string $stage): void { @@ -298,7 +301,7 @@ private function callTraitMethods(string $stage): void // -------------------------------------------------------------------- /** - * Resets shared instanced for all Factories components + * Resets shared instanced for all Factories components. * * @return void */ @@ -308,7 +311,7 @@ protected function resetFactories() } /** - * Resets shared instanced for all Services + * Resets shared instanced for all Services. * * @return void */ @@ -318,7 +321,7 @@ protected function resetServices(bool $initAutoloader = true) } /** - * Injects the mock Cache driver to prevent filesystem collisions + * Injects the mock Cache driver to prevent filesystem collisions. * * @return void */ @@ -328,7 +331,7 @@ protected function mockCache() } /** - * Injects the mock email driver so no emails really send + * Injects the mock email driver so no emails really send. * * @return void */ @@ -338,7 +341,7 @@ protected function mockEmail() } /** - * Injects the mock session driver into Services + * Injects the mock session driver into Services. * * @return void */ diff --git a/system/Test/DatabaseTestTrait.php b/system/Test/DatabaseTestTrait.php index c0d4473c..14c47c95 100644 --- a/system/Test/DatabaseTestTrait.php +++ b/system/Test/DatabaseTestTrait.php @@ -14,11 +14,13 @@ namespace CodeIgniter\Test; use CodeIgniter\Database\BaseBuilder; +use CodeIgniter\Database\BaseConnection; use CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\MigrationRunner; +use CodeIgniter\Database\Seeder; use CodeIgniter\Test\Constraints\SeeInDatabase; use Config\Database; use Config\Migrations; -use Config\Services; use PHPUnit\Framework\Attributes\AfterClass; /** @@ -27,6 +29,12 @@ * Provides functionality for refreshing/seeding * the database during testing. * + * @property BaseConnection $db + * @property list> $insertCache + * @property Seeder|null $seeder + * @property MigrationRunner|null $migrations + * @property list|string|null $namespace + * * @mixin CIUnitTestCase */ trait DatabaseTestTrait @@ -88,7 +96,7 @@ public function loadDependencies() $config = new Migrations(); $config->enabled = true; - $this->migrations = Services::migrations($config, $this->db, false); + $this->migrations = service('migrations', $config, $this->db, false); $this->migrations->setSilent(false); } diff --git a/system/Test/Fabricator.php b/system/Test/Fabricator.php index 1a7c5c8f..b366464d 100644 --- a/system/Test/Fabricator.php +++ b/system/Test/Fabricator.php @@ -168,7 +168,7 @@ public static function resetCounts() */ public static function getCount(string $table): int { - return ! isset(self::$tableCounts[$table]) ? 0 : self::$tableCounts[$table]; + return self::$tableCounts[$table] ?? 0; } /** diff --git a/system/Test/FeatureTestTrait.php b/system/Test/FeatureTestTrait.php index eaf49a0a..c3b97444 100644 --- a/system/Test/FeatureTestTrait.php +++ b/system/Test/FeatureTestTrait.php @@ -13,13 +13,17 @@ namespace CodeIgniter\Test; +use Closure; use CodeIgniter\Events\Events; use CodeIgniter\HTTP\Exceptions\RedirectException; +use CodeIgniter\HTTP\Header; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\Method; use CodeIgniter\HTTP\Request; +use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\HTTP\SiteURI; use CodeIgniter\HTTP\URI; +use CodeIgniter\Router\RouteCollection; use Config\App; use Config\Services; use Exception; @@ -30,6 +34,12 @@ * * Provides additional utilities for doing full HTTP testing * against your application in trait format. + * + * @property array $session + * @property array> $headers + * @property RouteCollection|null $routes + * + * @mixin CIUnitTestCase */ trait FeatureTestTrait { @@ -42,7 +52,12 @@ trait FeatureTestTrait * ['GET', 'home', 'Home::index'], * ] * - * @param array|null $routes Array to set routes + * @param array + * }>|null $routes Array to set routes * * @return $this */ @@ -84,7 +99,7 @@ protected function withRoutes(?array $routes = null) /** * Sets any values that should exist during this session. * - * @param array|null $values Array of values, or null to use the current $_SESSION + * @param array|null $values Array of values, or null to use the current $_SESSION * * @return $this */ @@ -103,7 +118,7 @@ public function withSession(?array $values = null) * 'Authorization' => 'Token' * ]) * - * @param array $headers Array of headers + * @param array> $headers Array of headers * * @return $this */ @@ -213,7 +228,7 @@ public function call(string $method, string $path, ?array $params = null) ->run($routes, true); // Reset directory if it has been set - service('router')->setDirectory(null); + service('router')->setDirectory(); return new TestResponse($response); } @@ -385,11 +400,11 @@ protected function populateGlobals(string $name, Request $request, ?array $param $request->setGlobal($name, $params); $request->setGlobal( 'request', - $request->fetchGlobal('post') + $request->fetchGlobal('get'), + (array) $request->fetchGlobal('post') + (array) $request->fetchGlobal('get'), ); } - $_SESSION = $this->session ?? []; + $_SESSION = $this->session; return $request; } diff --git a/system/Test/Mock/MockCache.php b/system/Test/Mock/MockCache.php index 67f8aee8..6699ad26 100644 --- a/system/Test/Mock/MockCache.php +++ b/system/Test/Mock/MockCache.php @@ -62,7 +62,7 @@ public function get(string $key) { $key = static::validateKey($key, $this->prefix); - return array_key_exists($key, $this->cache) ? $this->cache[$key] : null; + return $this->cache[$key] ?? null; } /** diff --git a/system/Test/Mock/MockLanguage.php b/system/Test/Mock/MockLanguage.php index 4ae1196c..3102bb91 100644 --- a/system/Test/Mock/MockLanguage.php +++ b/system/Test/Mock/MockLanguage.php @@ -15,13 +15,16 @@ use CodeIgniter\Language\Language; +/** + * @phpstan-import-type LoadedStrings from Language + */ class MockLanguage extends Language { /** * Stores the data that should be * returned by the 'requireFile()' method. * - * @var mixed + * @var LoadedStrings|null */ protected $data; @@ -30,18 +33,24 @@ class MockLanguage extends Language * 'requireFile()' method to allow easy overrides * during testing. * + * @param LoadedStrings $data + * * @return $this */ public function setData(string $file, array $data, ?string $locale = null) { $this->language[$locale ?? $this->locale][$file] = $data; + $this->data = $data; + return $this; } /** * Provides an override that allows us to set custom * data to be returned easily during testing. + * + * @return LoadedStrings */ protected function requireFile(string $path): array { diff --git a/system/ThirdParty/Escaper/Escaper.php b/system/ThirdParty/Escaper/Escaper.php index 39d9b0b1..25119a0e 100644 --- a/system/ThirdParty/Escaper/Escaper.php +++ b/system/ThirdParty/Escaper/Escaper.php @@ -247,7 +247,7 @@ public function escapeCss(string $string) protected function htmlAttrMatcher($matches) { $chr = $matches[0]; - $ord = ord($chr); + $ord = ord($chr[0]); /** * The following replaces characters undefined in HTML with the diff --git a/system/ThirdParty/Kint/CallFinder.php b/system/ThirdParty/Kint/CallFinder.php index 11c40ad8..cc34123a 100644 --- a/system/ThirdParty/Kint/CallFinder.php +++ b/system/ThirdParty/Kint/CallFinder.php @@ -148,11 +148,11 @@ class CallFinder ]; /** - * @psalm-param callable-array|callable-string $function - * - * @psalm-return list, modifiers: list}> + * @psalm-param callable-string|(callable-array&list{class-string, non-empty-string}) $function * * @return array List of matching calls on the relevant line + * + * @psalm-return list, modifiers: list}> */ public static function getFunctionCalls(string $source, int $line, $function): array { @@ -198,6 +198,11 @@ public static function getFunctionCalls(string $source, int $line, $function): a self::$operator[T_NEW] = true; // @codeCoverageIgnore } + if (KINT_PHP85) { + /** @psalm-suppress UndefinedConstant */ + self::$operator[T_PIPE] = true; + } + /** @psalm-var list */ $tokens = \token_get_all($source); $function_calls = []; @@ -212,10 +217,6 @@ public static function getFunctionCalls(string $source, int $line, $function): a $function = \strtolower($function[1]); } else { $class = null; - /** - * @psalm-suppress RedundantFunctionCallGivenDocblockType - * Psalm bug #11075 - */ $function = \strtolower($function); } @@ -294,6 +295,8 @@ public static function getFunctionCalls(string $source, int $line, $function): a $params = []; // All our collected parameters $shortparam = []; // The short version of the parameter $param_start = $offset; // The distance to the start of the parameter + $quote = null; // Buffer to store quote type for shortparam + $in_ternary = false; // Loop through the following tokens until the function call ends while (isset($tokens[$offset])) { @@ -341,6 +344,26 @@ public static function getFunctionCalls(string $source, int $line, $function): a $instring = !$instring; $shortparam[] = $token; + } elseif (T_START_HEREDOC === $token[0]) { + if (1 === $depth) { + $quote = \ltrim($token[1], " \t<")[0]; + if ("'" !== $quote) { + $quote = '"'; + } + $shortparam[] = [T_START_HEREDOC, $quote]; + $instring = true; + } + + ++$depth; + } elseif (T_END_HEREDOC === $token[0]) { + --$depth; + + if (1 === $depth) { + if ($realtokens) { + $shortparam[] = '...'; + } + $shortparam[] = [T_END_HEREDOC, $quote]; + } } elseif (1 === $depth) { if (',' === $token[0]) { $params[] = [ @@ -349,6 +372,7 @@ public static function getFunctionCalls(string $source, int $line, $function): a ]; $shortparam = []; $paramrealtokens = false; + $in_ternary = false; $param_start = $offset + 1; } elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) { $quote = $token[1][0]; @@ -364,6 +388,15 @@ public static function getFunctionCalls(string $source, int $line, $function): a } $shortparam[] = $token; } else { + // We can't tell the order of named parameters or if they're splatting + // without parsing the called function and that's too much work for this + // edge case so we'll just skip parameters altogether. + if ('?' === $token) { + $in_ternary = true; + } elseif (!$in_ternary && ':' === $token) { + $params = []; + break; + } $shortparam[] = $token; } } @@ -400,13 +433,26 @@ public static function getFunctionCalls(string $source, int $line, $function): a $literal = false; $new_without_parens = false; - foreach ($name as $token) { + foreach ($name as $name_index => $token) { + if (KINT_PHP85 && T_CLONE === $token[0]) { + $nextReal = self::realTokenIndex($name, $name_index + 1); + + if (null !== $nextReal && '(' === $name[$nextReal]) { + continue; + } + } + if (self::tokenIsOperator($token)) { $expression = true; break; } } + if (!$expression && T_START_HEREDOC === $name[0][0]) { + $expression = true; + $literal = true; + } + // As of 8.4 new is only an expression when parentheses are // omitted. In that case we can cheat and add them ourselves. // @@ -593,6 +639,7 @@ private static function tokensTrim(array $tokens): array return \array_reverse($tokens); } + /** @psalm-return list */ private static function tokensFormatted(array $tokens): array { $tokens = self::tokensTrim($tokens); diff --git a/system/ThirdParty/Kint/Kint.php b/system/ThirdParty/Kint/Kint.php index 6c55593d..4348aab0 100644 --- a/system/ThirdParty/Kint/Kint.php +++ b/system/ThirdParty/Kint/Kint.php @@ -36,6 +36,7 @@ use Kint\Renderer\TextRenderer; use Kint\Value\Context\BaseContext; use Kint\Value\Context\ContextInterface; +use Kint\Value\TraceFrameValue; use Kint\Value\UninitializedValue; /** @@ -43,8 +44,16 @@ * Psalm bug #8523 * * @psalm-import-type CallParameter from CallFinder + * @psalm-import-type TraceFrame from TraceFrameValue * - * @psalm-type KintMode = Kint::MODE_*|bool + * @psalm-type KintMode = array-key|bool + * @psalm-type KintCallInfo = array{ + * params: ?list, + * modifiers: array, + * callee: ?callable, + * caller: ?callable, + * trace: TraceFrame[], + * } * * @psalm-api */ @@ -396,9 +405,9 @@ public static function getBasesFromParamInfo(array $params, int $argc): array * @param array[] $trace Backtrace * @param array $args Arguments * - * @return array Call info - * * @psalm-param list $trace + * + * @return KintCallInfo Call info */ public static function getCallInfo(array $aliases, array $trace, array $args): array { @@ -622,8 +631,8 @@ protected static function getSingleCall(array $frame, array $args): ?array foreach ($keys as $key) { $call['parameters'][] = [ - 'name' => \substr($param['name'], 3).'['.\var_export($key, true).']', - 'path' => \substr($param['path'], 3).'['.\var_export($key, true).']', + 'name' => ((string) \substr($param['name'], 3)).'['.\var_export($key, true).']', + 'path' => ((string) \substr($param['path'], 3)).'['.\var_export($key, true).']', 'expression' => false, 'literal' => false, 'new_without_parens' => false, @@ -634,8 +643,8 @@ protected static function getSingleCall(array $frame, array $args): ?array // through array_values so we can't access them directly at all for ($j = 0; $j + $i < $argc; ++$j) { $call['parameters'][] = [ - 'name' => 'array_values('.\substr($param['name'], 3).')['.$j.']', - 'path' => 'array_values('.\substr($param['path'], 3).')['.$j.']', + 'name' => 'array_values('.((string) \substr($param['name'], 3)).')['.$j.']', + 'path' => 'array_values('.((string) \substr($param['path'], 3)).')['.$j.']', 'expression' => false, 'literal' => false, 'new_without_parens' => false, diff --git a/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php b/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php index 8a18a00a..0b5e616f 100644 --- a/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php +++ b/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php @@ -136,7 +136,9 @@ private function buildStaticValue(ReflectionProperty $pr, int $depth): AbstractV $context->access_path = '\\'.$context->owner_class.'::$'.$context->name; } - $pr->setAccessible(true); + if (KINT_PHP81 === false) { + $pr->setAccessible(true); + } /** * @psalm-suppress TooFewArguments diff --git a/system/ThirdParty/Kint/Parser/ColorPlugin.php b/system/ThirdParty/Kint/Parser/ColorPlugin.php index fc160a43..ffdedd7c 100644 --- a/system/ThirdParty/Kint/Parser/ColorPlugin.php +++ b/system/ThirdParty/Kint/Parser/ColorPlugin.php @@ -57,7 +57,7 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa $trimmed = \strtolower(\trim($var)); - if (!isset(ColorRepresentation::$color_map[$trimmed]) && !\preg_match('/^(?:(?:rgb|hsl)[^\\)]{6,}\\)|#[0-9a-fA-F]{3,8})$/', $trimmed)) { + if (!isset(ColorRepresentation::$color_map[$trimmed]) && !\preg_match('/^(?:(?:rgb|hsl)a?[^\\)]{6,}\\)|#[0-9a-f]{3,8})$/', $trimmed)) { return $v; } diff --git a/system/ThirdParty/Kint/Parser/DomPlugin.php b/system/ThirdParty/Kint/Parser/DomPlugin.php index 358c0b91..7de5eb8d 100644 --- a/system/ThirdParty/Kint/Parser/DomPlugin.php +++ b/system/ThirdParty/Kint/Parser/DomPlugin.php @@ -55,11 +55,12 @@ use Kint\Value\Representation\ContainerRepresentation; use Kint\Value\StringValue; use LogicException; +use ReflectionClass; class DomPlugin extends AbstractPlugin implements PluginBeginInterface { /** - * Reflection doesn't work below 8.1, also it won't show readonly status. + * Reflection doesn't show readonly status. * * In order to ensure this is stable enough we're only going to provide * properties for element and node. If subclasses like attr or document @@ -102,12 +103,9 @@ class DomPlugin extends AbstractPlugin implements PluginBeginInterface 'previousElementSibling' => true, 'nextElementSibling' => true, 'innerHTML' => false, - 'outerHTML' => false, + 'outerHTML' => true, 'substitutedNodeValue' => false, - ]; - - public const DOM_NS_VERSIONS = [ - 'outerHTML' => KINT_PHP85, + 'children' => true, ]; /** @@ -208,6 +206,9 @@ class DomPlugin extends AbstractPlugin implements PluginBeginInterface */ public static bool $verbose = false; + /** @psalm-var array> cache of properties for getKnownProperties */ + protected static array $property_cache = []; + protected ClassMethodsPlugin $methods_plugin; protected ClassStaticsPlugin $statics_plugin; @@ -259,12 +260,14 @@ public function parseBegin(&$var, ContextInterface $c): ?AbstractValue /** @psalm-param Node|DOMNode $var */ private function parseProperty(object $var, string $prop, ContextInterface $c): AbstractValue { - if (!isset($var->{$prop})) { + // Suppress deprecation message + if (@!isset($var->{$prop})) { return new FixedWidthValue($c, null); } $parser = $this->getParser(); - $value = $var->{$prop}; + // Suppress deprecation message + @$value = $var->{$prop}; if (\is_scalar($value)) { return $parser->parse($value, $c); @@ -450,29 +453,42 @@ private function parseNode(object $var, ContextInterface $c): DomNodeValue */ public static function getKnownProperties(object $var): array { - if ($var instanceof Node) { - $known_properties = self::NODE_PROPS; - if ($var instanceof Element) { - $known_properties += self::ELEMENT_PROPS; - } - - if ($var instanceof Document) { - $known_properties['textContent'] = true; - } + if (KINT_PHP81) { + $r = new ReflectionClass($var); + $classname = $r->getName(); + + if (!isset(self::$property_cache[$classname])) { + self::$property_cache[$classname] = []; + + foreach ($r->getProperties() as $prop) { + if ($prop->isStatic()) { + continue; + } + + $declaring = $prop->getDeclaringClass()->getName(); + $name = $prop->name; + + if (\in_array($declaring, [Node::class, Element::class], true)) { + $readonly = self::NODE_PROPS[$name] ?? self::ELEMENT_PROPS[$name]; + } elseif (\in_array($declaring, [DOMNode::class, DOMElement::class], true)) { + $readonly = self::DOMNODE_PROPS[$name] ?? self::DOMELEMENT_PROPS[$name]; + } else { + continue; + } + + self::$property_cache[$classname][$prop->name] = $readonly; + } - if ($var instanceof Attr || $var instanceof CharacterData) { - $known_properties['nodeValue'] = false; - } + if ($var instanceof Document) { + self::$property_cache[$classname]['textContent'] = true; + } - foreach (self::DOM_NS_VERSIONS as $key => $val) { - /** - * @psalm-var bool $val - * Psalm bug #4509 - */ - if (false === $val) { - unset($known_properties[$key]); // @codeCoverageIgnore + if ($var instanceof Attr || $var instanceof CharacterData) { + self::$property_cache[$classname]['nodeValue'] = false; } } + + $known_properties = self::$property_cache[$classname]; } else { $known_properties = self::DOMNODE_PROPS; if ($var instanceof DOMElement) { @@ -480,10 +496,6 @@ public static function getKnownProperties(object $var): array } foreach (self::DOM_VERSIONS as $key => $val) { - /** - * @psalm-var bool $val - * Psalm bug #4509 - */ if (false === $val) { unset($known_properties[$key]); // @codeCoverageIgnore } diff --git a/system/ThirdParty/Kint/Parser/HtmlPlugin.php b/system/ThirdParty/Kint/Parser/HtmlPlugin.php index 4df7ad19..bc1a4f26 100644 --- a/system/ThirdParty/Kint/Parser/HtmlPlugin.php +++ b/system/ThirdParty/Kint/Parser/HtmlPlugin.php @@ -52,7 +52,7 @@ public function getTriggers(): int public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue { - if ('' !== \strtolower(\substr($var, 0, 15))) { + if ('' !== \strtolower((string) \substr($var, 0, 15))) { return $v; } diff --git a/system/ThirdParty/Kint/Parser/IteratorPlugin.php b/system/ThirdParty/Kint/Parser/IteratorPlugin.php index 31b037ac..fc31d78f 100644 --- a/system/ThirdParty/Kint/Parser/IteratorPlugin.php +++ b/system/ThirdParty/Kint/Parser/IteratorPlugin.php @@ -86,10 +86,6 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa $c = $v->getContext(); foreach (self::$blacklist as $class) { - /** - * @psalm-suppress RedundantCondition - * Psalm bug #11076 - */ if ($var instanceof $class) { $base = new BaseContext($class.' Iterator Contents'); $base->depth = $c->getDepth() + 1; diff --git a/system/ThirdParty/Kint/Parser/Parser.php b/system/ThirdParty/Kint/Parser/Parser.php index a3c2fbb0..5b28157a 100644 --- a/system/ThirdParty/Kint/Parser/Parser.php +++ b/system/ThirdParty/Kint/Parser/Parser.php @@ -229,7 +229,7 @@ protected function noRecurseCall(): void $bt = \debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS); \reset($bt); - /** @psalm-var class-string $caller_frame['class'] */ + /** @psalm-var array{class: class-string, function: string, ...} $caller_frame */ $caller_frame = \next($bt); foreach ($bt as $frame) { @@ -401,7 +401,10 @@ private function parseObject(object &$var, ContextInterface $c): AbstractValue $properties = []; foreach ($props as $rprop) { - $rprop->setAccessible(true); + if (KINT_PHP81 === false) { + $rprop->setAccessible(true); + } + $name = $rprop->getName(); // Casting object to array: @@ -460,6 +463,21 @@ private function parseObject(object &$var, ContextInterface $c): AbstractValue } } } + + if (KINT_PHP8412) { + $proto_prop = $rprop; + while (($parent_class = $proto_prop->getDeclaringClass()->getParentClass()) && + $parent_class->hasProperty($name) && + ($parent_prop = $parent_class->getProperty($name)) && + !$parent_prop->isPrivate()) { + $proto_prop = $parent_prop; + } + + $proto_class = $proto_prop->getDeclaringClass()->getName(); + if ($proto_class !== $child->owner_class) { + $child->proto_class = $proto_prop->getDeclaringClass()->getName(); + } + } } else { $child = new ClassOwnedContext($name, $rprop->getDeclaringClass()->getName()); } diff --git a/system/ThirdParty/Kint/Parser/ProfilePlugin.php b/system/ThirdParty/Kint/Parser/ProfilePlugin.php index ec5bb0a1..836357b2 100644 --- a/system/ThirdParty/Kint/Parser/ProfilePlugin.php +++ b/system/ThirdParty/Kint/Parser/ProfilePlugin.php @@ -71,11 +71,19 @@ public function parseBegin(&$var, ContextInterface $c): ?AbstractValue $this->instance_count_stack[$hash] ??= 0; if (0 === $this->instance_count_stack[$hash]) { + /** + * @psalm-suppress PossiblyFalseIterator + * Psalm bug #11392 + */ foreach (\class_parents($var) as $class) { $this->class_count_stack[$class] ??= 0; ++$this->class_count_stack[$class]; } + /** + * @psalm-suppress PossiblyFalseIterator + * Psalm bug #11392 + */ foreach (\class_implements($var) as $iface) { $this->class_count_stack[$iface] ??= 0; ++$this->class_count_stack[$iface]; @@ -94,10 +102,18 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa --$this->instance_count_stack[$v->getSplObjectHash()]; if (0 === $this->instance_count_stack[$v->getSplObjectHash()]) { + /** + * @psalm-suppress PossiblyFalseIterator + * Psalm bug #11392 + */ foreach (\class_parents($var) as $class) { --$this->class_count_stack[$class]; } + /** + * @psalm-suppress PossiblyFalseIterator + * Psalm bug #11392 + */ foreach (\class_implements($var) as $iface) { --$this->class_count_stack[$iface]; } @@ -130,6 +146,10 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa $this->class_complexity[$v->getClassName()] ??= 0; $this->class_complexity[$v->getClassName()] += $sub_complexity; + /** + * @psalm-suppress PossiblyFalseIterator + * Psalm bug #11392 + */ foreach (\class_parents($var) as $class) { $this->class_complexity[$class] ??= 0; if (0 === $this->class_count_stack[$class]) { @@ -137,6 +157,10 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa } } + /** + * @psalm-suppress PossiblyFalseIterator + * Psalm bug #11392 + */ foreach (\class_implements($var) as $iface) { $this->class_complexity[$iface] ??= 0; if (0 === $this->class_count_stack[$iface]) { diff --git a/system/ThirdParty/Kint/Parser/SimpleXMLElementPlugin.php b/system/ThirdParty/Kint/Parser/SimpleXMLElementPlugin.php index 4511c0b7..65b16717 100644 --- a/system/ThirdParty/Kint/Parser/SimpleXMLElementPlugin.php +++ b/system/ThirdParty/Kint/Parser/SimpleXMLElementPlugin.php @@ -208,41 +208,45 @@ protected function getChildren(ContextInterface $c, SimpleXMLElement $var): arra $contents = []; foreach ($namespaces as $nsAlias => $_) { - if ((bool) $nsChildren = $var->children($nsAlias, true)) { - $nsap = []; - foreach ($nsChildren as $name => $child) { - $base = new ClassOwnedContext((string) $name, SimpleXMLElement::class); - $base->depth = $cdepth + 1; + $nsChildren = $var->children($nsAlias, true); + if (!(bool) $nsChildren) { + continue; + } - if ('' !== $nsAlias) { - $base->name = $nsAlias.':'.$name; - } + $nsap = []; - if (null !== $ap) { - if ('' === $nsAlias) { - $base->access_path = $ap.'->'; - } else { - $base->access_path = $ap.'->children('.\var_export($nsAlias, true).', true)->'; - } + foreach ($nsChildren as $name => $child) { + $base = new ClassOwnedContext((string) $name, SimpleXMLElement::class); + $base->depth = $cdepth + 1; - if (Utils::isValidPhpName((string) $name)) { - $base->access_path .= (string) $name; - } else { - $base->access_path .= '{'.\var_export((string) $name, true).'}'; - } + if ('' !== $nsAlias) { + $base->name = $nsAlias.':'.$name; + } - if (isset($nsap[$base->access_path])) { - ++$nsap[$base->access_path]; - $base->access_path .= '['.$nsap[$base->access_path].']'; - } else { - $nsap[$base->access_path] = 0; - } + if (null !== $ap) { + if ('' === $nsAlias) { + $base->access_path = $ap.'->'; + } else { + $base->access_path = $ap.'->children('.\var_export($nsAlias, true).', true)->'; + } + + if (Utils::isValidPhpName((string) $name)) { + $base->access_path .= (string) $name; + } else { + $base->access_path .= '{'.\var_export((string) $name, true).'}'; } - $v = $this->parseElement($child, $base); - $v->flags |= AbstractValue::FLAG_GENERATED; - $contents[] = $v; + if (isset($nsap[$base->access_path])) { + ++$nsap[$base->access_path]; + $base->access_path .= '['.$nsap[$base->access_path].']'; + } else { + $nsap[$base->access_path] = 0; + } } + + $v = $this->parseElement($child, $base); + $v->flags |= AbstractValue::FLAG_GENERATED; + $contents[] = $v; } } diff --git a/system/ThirdParty/Kint/Parser/TracePlugin.php b/system/ThirdParty/Kint/Parser/TracePlugin.php index 53345647..d01ebd9d 100644 --- a/system/ThirdParty/Kint/Parser/TracePlugin.php +++ b/system/ThirdParty/Kint/Parser/TracePlugin.php @@ -91,11 +91,11 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa $index = $frame->getContext()->getName(); - if (!isset($trace[$index]) || Utils::traceFrameIsListed($trace[$index], self::$blacklist)) { + if (!isset($trace[$index]['file']) || Utils::traceFrameIsListed($trace[$index], self::$blacklist)) { continue; } - if (isset($trace[$index]['file']) && false !== ($realfile = \realpath($trace[$index]['file']))) { + if (false !== ($realfile = \realpath($trace[$index]['file']))) { foreach ($path_blacklist as $path) { if (0 === \strpos($realfile, $path)) { continue 2; @@ -144,7 +144,7 @@ protected static function normalizePaths(array $paths): array foreach ($paths as $path) { $realpath = \realpath($path); - if (\is_dir($realpath)) { + if (false !== $realpath && \is_dir($realpath)) { $realpath .= DIRECTORY_SEPARATOR; } diff --git a/system/ThirdParty/Kint/Renderer/Rich/BinaryPlugin.php b/system/ThirdParty/Kint/Renderer/Rich/BinaryPlugin.php index 54c40335..75921625 100644 --- a/system/ThirdParty/Kint/Renderer/Rich/BinaryPlugin.php +++ b/system/ThirdParty/Kint/Renderer/Rich/BinaryPlugin.php @@ -49,7 +49,7 @@ public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string $lines = \str_split($r->getValue(), self::$line_length); foreach ($lines as $index => $line) { - $out .= \sprintf('%08X', $index * self::$line_length).":\t"; + $out .= ((string) \sprintf('%08X', $index * self::$line_length)).":\t"; $chunks = \str_split(\str_pad(\bin2hex($line), 2 * self::$line_length, ' '), 2 * self::$chunk_length); diff --git a/system/ThirdParty/Kint/Renderer/RichRenderer.php b/system/ThirdParty/Kint/Renderer/RichRenderer.php index 3a8fdc6f..a494938d 100644 --- a/system/ThirdParty/Kint/Renderer/RichRenderer.php +++ b/system/ThirdParty/Kint/Renderer/RichRenderer.php @@ -290,7 +290,7 @@ public function renderHeader(AbstractValue $v): string } if (null !== ($s = $v->getDisplayValue())) { - $s = \preg_replace('/\\s+/', ' ', $s); + $s = (string) \preg_replace('/\\s+/', ' ', $s); if (self::$strlen_max) { $s = Utils::truncateString($s, self::$strlen_max); @@ -322,7 +322,7 @@ public function renderChildren(AbstractValue $v): string $output = '
'; if (1 === \count($tabs) && $tabs[0]->labelIsImplicit()) { - $output .= \reset($contents); + $output .= (string) \reset($contents); } else { $output .= '
    '; diff --git a/system/ThirdParty/Kint/Utils.php b/system/ThirdParty/Kint/Utils.php index 0fcdc478..f39d2c1d 100644 --- a/system/ThirdParty/Kint/Utils.php +++ b/system/ThirdParty/Kint/Utils.php @@ -313,7 +313,7 @@ public static function errorSanitizeString(string $input): string return $input; } - return \strtok($input, "\0"); // @codeCoverageIgnore + return (string) \strtok($input, "\0"); // @codeCoverageIgnore } /** @psalm-pure */ @@ -435,7 +435,7 @@ public static function substr(string $string, int $start, ?int $length = null, $ return ''; } - return \substr($string, $start, $length ?? PHP_INT_MAX); + return (string) \substr($string, $start, $length ?? PHP_INT_MAX); } public static function shortenPath(string $file): string @@ -512,7 +512,7 @@ public static function composerGetExtras(string $key = 'kint'): array $installed = $folder.'/composer/installed.json'; if (\file_exists($installed) && \is_readable($installed)) { - $packages = \json_decode(\file_get_contents($installed), true); + $packages = \json_decode((string) \file_get_contents($installed), true); if (!\is_array($packages)) { continue; @@ -529,7 +529,7 @@ public static function composerGetExtras(string $key = 'kint'): array $folder = \dirname($folder); if (\file_exists($folder.'/composer.json') && \is_readable($folder.'/composer.json')) { - $composer = \json_decode(\file_get_contents($folder.'/composer.json'), true); + $composer = \json_decode((string) \file_get_contents($folder.'/composer.json'), true); if (\is_array($composer['extra'][$key] ?? null)) { $extras = \array_replace($extras, $composer['extra'][$key]); diff --git a/system/ThirdParty/Kint/Value/Context/ClassDeclaredContext.php b/system/ThirdParty/Kint/Value/Context/ClassDeclaredContext.php index 79aeeb72..9243833a 100644 --- a/system/ThirdParty/Kint/Value/Context/ClassDeclaredContext.php +++ b/system/ThirdParty/Kint/Value/Context/ClassDeclaredContext.php @@ -38,6 +38,9 @@ abstract class ClassDeclaredContext extends ClassOwnedContext /** @psalm-var self::ACCESS_* */ public int $access; + /** @psalm-var class-string */ + public ?string $proto_class = null; + /** * @psalm-param class-string $owner_class * @psalm-param self::ACCESS_* $access @@ -69,12 +72,22 @@ public function isAccessible(?string $scope): bool return $scope === $this->owner_class; } - if (\is_a($scope, $this->owner_class, true)) { - return true; - } - - if (\is_a($this->owner_class, $scope, true)) { - return true; + if (KINT_PHP8412 && null !== $this->proto_class) { + if (\is_a($scope, $this->proto_class, true)) { + return true; + } + + if (\is_a($this->proto_class, $scope, true)) { + return true; + } + } else { + if (\is_a($scope, $this->owner_class, true)) { + return true; + } + + if (\is_a($this->owner_class, $scope, true)) { + return true; + } } return false; diff --git a/system/ThirdParty/Kint/Value/MethodValue.php b/system/ThirdParty/Kint/Value/MethodValue.php index 42467275..6f2e94c0 100644 --- a/system/ThirdParty/Kint/Value/MethodValue.php +++ b/system/ThirdParty/Kint/Value/MethodValue.php @@ -127,7 +127,7 @@ public function getPhpDocUrl(): ?string $funcname = \str_replace('_', '-', \strtolower($c->getName())); if (0 === \strpos($funcname, '--') && 0 !== \strpos($funcname, '-', 2)) { - $funcname = \substr($funcname, 2); + $funcname = (string) \substr($funcname, 2); } return 'https://www.php.net/'.$class.'.'.$funcname; diff --git a/system/ThirdParty/Kint/Value/Representation/AbstractRepresentation.php b/system/ThirdParty/Kint/Value/Representation/AbstractRepresentation.php index 3c8180e6..5c80ee5b 100644 --- a/system/ThirdParty/Kint/Value/Representation/AbstractRepresentation.php +++ b/system/ThirdParty/Kint/Value/Representation/AbstractRepresentation.php @@ -39,7 +39,9 @@ abstract class AbstractRepresentation implements RepresentationInterface public function __construct(string $label, ?string $name = null, bool $implicit_label = false) { $this->label = $label; - $this->name = \preg_replace('/[^a-z0-9]+/', '_', \strtolower($name ?? $label)); + /** @psalm-var string $name */ + $name = \preg_replace('/[^a-z0-9]+/', '_', \strtolower($name ?? $label)); + $this->name = $name; $this->implicit_label = $implicit_label; } diff --git a/system/ThirdParty/Kint/Value/Representation/CallableDefinitionRepresentation.php b/system/ThirdParty/Kint/Value/Representation/CallableDefinitionRepresentation.php index 4625f343..a26467e9 100644 --- a/system/ThirdParty/Kint/Value/Representation/CallableDefinitionRepresentation.php +++ b/system/ThirdParty/Kint/Value/Representation/CallableDefinitionRepresentation.php @@ -104,7 +104,8 @@ public function getDocstringWithoutComments(): ?string return null; } - $string = \substr($ds, 3, -2); + $string = (string) \substr($ds, 3, -2); + /** @psalm-var string $string */ $string = \preg_replace('/^\\s*\\*\\s*?(\\S|$)/m', '\\1', $string); return \trim($string); diff --git a/system/ThirdParty/Kint/Value/Representation/ColorRepresentation.php b/system/ThirdParty/Kint/Value/Representation/ColorRepresentation.php index 4221ccce..27315789 100644 --- a/system/ThirdParty/Kint/Value/Representation/ColorRepresentation.php +++ b/system/ThirdParty/Kint/Value/Representation/ColorRepresentation.php @@ -335,7 +335,7 @@ protected function setValues(string $value): void $this->setValuesFromHex(self::$color_map[$value]); $variant = self::COLOR_NAME; } elseif ('#' === $value[0]) { - $variant = $this->setValuesFromHex(\substr($value, 1)); + $variant = $this->setValuesFromHex((string) \substr($value, 1)); } else { $variant = $this->setValuesFromFunction($value); } @@ -381,7 +381,7 @@ protected function setValuesFromHex(string $hex): int $this->b = \hexdec($hex[2]) * 0x11; break; case self::COLOR_HEX_8: - $this->a = \hexdec(\substr($hex, 6, 2)) / 0xFF; + $this->a = \hexdec((string) \substr($hex, 6, 2)) / 0xFF; // no break case self::COLOR_HEX_6: $hex = \str_split($hex, 2); @@ -397,6 +397,9 @@ protected function setValuesFromHex(string $hex): int /** @psalm-return self::COLOR_* */ protected function setValuesFromFunction(string $value): int { + // We're not even going to attempt to support other color functions or + // angle values. If that's ever going to be a thing we'll depend on a + // color library and use php scopes for the phar file. if (!\preg_match('/^((?:rgb|hsl)a?)\\s*\\(([0-9\\.%,\\s\\/\\-]+)\\)$/i', $value, $match)) { throw new InvalidArgumentException('Couldn\'t parse color function string'); } @@ -419,14 +422,30 @@ protected function setValuesFromFunction(string $value): int throw new InvalidArgumentException('Color functions must be one of rgb/rgba/hsl/hsla'); // @codeCoverageIgnore } - $params = \preg_replace('/[,\\s\\/]+/', ',', \trim($match[2])); - $params = \explode(',', $params); - $params = \array_map('trim', $params); + \preg_match('/^\\s*([^,\\s\\/]+)([,\\s]+)((?1))((?2))((?1))(?:([,\\s\\/]+)((?1)))?\\s*$/', $match[2], $match); + + if (!$match || + \trim($match[2]) !== \trim($match[4]) || + (isset($match[6]) && ( + ('' === \trim($match[2]) && '/' !== \trim($match[6])) || + ('' !== \trim($match[2]) && \trim($match[2]) !== \trim($match[6])) + )) + ) { + throw new InvalidArgumentException('Couldn\'t parse color function string'); + } - if (\count($params) < 3 || \count($params) > 4) { - throw new InvalidArgumentException('Color functions must have 3 or 4 arguments'); + $params = [ + $match[1], + $match[3], + $match[5], + ]; + + if (isset($match[7])) { + $params[] = $match[7]; } + $params = \array_map('trim', $params); + foreach ($params as $i => &$color) { if (false !== \strpos($color, '%')) { $color = (float) \str_replace('%', '', $color); diff --git a/system/ThirdParty/Kint/Value/Representation/SourceRepresentation.php b/system/ThirdParty/Kint/Value/Representation/SourceRepresentation.php index 1645409d..9d737752 100644 --- a/system/ThirdParty/Kint/Value/Representation/SourceRepresentation.php +++ b/system/ThirdParty/Kint/Value/Representation/SourceRepresentation.php @@ -108,7 +108,9 @@ private static function readSource(string $filename, int $start_line = 1, ?int $ throw new RuntimeException("Couldn't read file"); } - $source = \preg_split("/\r\n|\n|\r/", \file_get_contents($filename)); + /** @psalm-var non-empty-array $source */ + $source = \preg_split("/\r\n|\n|\r/", (string) \file_get_contents($filename)); + /** @psalm-var array $source */ $source = \array_combine(\range(1, \count($source)), $source); $source = \array_slice($source, $start_line - 1, $length, true); diff --git a/system/ThirdParty/Kint/Value/StringValue.php b/system/ThirdParty/Kint/Value/StringValue.php index ca2c17f4..c7ff67e2 100644 --- a/system/ThirdParty/Kint/Value/StringValue.php +++ b/system/ThirdParty/Kint/Value/StringValue.php @@ -72,7 +72,10 @@ public function getValueUtf8(): string return $this->value; } - return \mb_convert_encoding($this->value, 'UTF-8', $this->encoding); + /** @psalm-var string $encoded */ + $encoded = \mb_convert_encoding($this->value, 'UTF-8', $this->encoding); + + return $encoded; } /** @psalm-api */ diff --git a/system/ThirdParty/Kint/init.php b/system/ThirdParty/Kint/init.php index 60e58f66..9787a159 100644 --- a/system/ThirdParty/Kint/init.php +++ b/system/ThirdParty/Kint/init.php @@ -44,6 +44,7 @@ \define('KINT_PHP82', \version_compare(PHP_VERSION, '8.2') >= 0); \define('KINT_PHP83', \version_compare(PHP_VERSION, '8.3') >= 0); \define('KINT_PHP84', \version_compare(PHP_VERSION, '8.4') >= 0); +\define('KINT_PHP8412', \version_compare(PHP_VERSION, '8.4.12') >= 0); \define('KINT_PHP85', \version_compare(PHP_VERSION, '8.5') >= 0); // Dynamic default settings @@ -58,7 +59,7 @@ // Suppressed for unreadable document roots (related to open_basedir) if (false !== @\realpath($_SERVER['DOCUMENT_ROOT'])) { - /** @psalm-suppress PropertyTypeCoercion */ + /** @psalm-suppress InvalidPropertyAssignmentValue */ Utils::$path_aliases[\realpath($_SERVER['DOCUMENT_ROOT'])] = ''; } } diff --git a/system/ThirdParty/Kint/resources/compiled/aante-dark.css b/system/ThirdParty/Kint/resources/compiled/aante-dark.css index 36f8e75b..7095cba4 100644 --- a/system/ThirdParty/Kint/resources/compiled/aante-dark.css +++ b/system/ThirdParty/Kint/resources/compiled/aante-dark.css @@ -1 +1 @@ -.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}input.kint-note-input{width:100%}.kint-rich{--main-background: #f8f8f8;--secondary-background: #f8f8f8;--variable-type-color: #06f;--variable-type-color-hover: #f00;--border-color: #d7d7d7;--border-color-hover: #aaa;--alternative-background: #fff;--highlight-color: #cfc;--caret-image: url("data:image/svg+xml;utf8,")}.kint-rich .kint-focused{box-shadow:0 0 3px 2px var(--variable-type-color-hover)}.kint-rich dt{font-weight:normal}.kint-rich dt.kint-parent{margin-top:4px}.kint-rich dl dl{margin-top:4px;padding-left:25px;border-left:none}.kint-rich>dl>dt{background:var(--secondary-background)}.kint-rich ul{margin:0;padding-left:0}.kint-rich ul:not(.kint-tabs)>li{border-left:0}.kint-rich ul.kint-tabs{background:var(--secondary-background);border:var(--border);border-width:0 1px 1px 1px;padding:4px 0 0 12px;margin-left:-1px;margin-top:-1px}.kint-rich ul.kint-tabs li,.kint-rich ul.kint-tabs li+li{margin:0 0 0 4px}.kint-rich ul.kint-tabs li{border-bottom-width:0;height:calc(var(--spacing)*6 + 1px)}.kint-rich ul.kint-tabs li:first-child{margin-left:0}.kint-rich ul.kint-tabs li.kint-active-tab{border-top:var(--border);background:var(--alternative-background);font-weight:bold;padding-top:0;border-bottom:1px solid var(--alternative-background) !important;margin-bottom:-1px}.kint-rich ul.kint-tabs li.kint-active-tab:hover{border-bottom:1px solid var(--alternative-background)}.kint-rich ul>li>pre{border:var(--border)}.kint-rich dt:hover+dd>ul{border-color:var(--border-color-hover)}.kint-rich pre{background:var(--alternative-background);margin-top:4px;margin-left:25px}.kint-rich .kint-source{margin-left:-1px}.kint-rich .kint-source .kint-highlight{background:var(--highlight-color)}.kint-rich .kint-parent.kint-show>.kint-search{border-bottom-width:1px}.kint-rich table td{background:var(--alternative-background)}.kint-rich table td>dl{padding:0;margin:0}.kint-rich table td>dl>dt.kint-parent{margin:0}.kint-rich table td:first-child,.kint-rich table td,.kint-rich table th{padding:2px 4px}.kint-rich table dd,.kint-rich table dt{background:var(--alternative-background)}.kint-rich table tr:hover>td{box-shadow:none;background:var(--highlight-color)}.kint-rich{--main-background: #070707;--secondary-background: #070707;--variable-type-color: #ff9900;--variable-type-color-hover: aqua;--border-color: #282828;--border-color-hover: #555555;--backdrop-color: rgba(0, 0, 0, 0.9);--text-color: #e2e1e1;--variable-name-color: #e2e1e1;--alternative-background: black;--highlight-color: #330033;--caret-image: url("data:image/svg+xml;utf8,")} +.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}.kint-rich{--main-background: #070707;--secondary-background: #070707;--variable-type-color: #ff9900;--variable-type-color-hover: aqua;--border-color: #282828;--border-color-hover: #555555;--backdrop-color: rgba(0, 0, 0, 0.9);--text-color: #e2e1e1;--variable-name-color: #e2e1e1;--alternative-background: black;--highlight-color: #330033;--caret-image: url("data:image/svg+xml;utf8,")} diff --git a/system/ThirdParty/Kint/resources/compiled/aante-light.css b/system/ThirdParty/Kint/resources/compiled/aante-light.css index 08bd4030..621594dd 100644 --- a/system/ThirdParty/Kint/resources/compiled/aante-light.css +++ b/system/ThirdParty/Kint/resources/compiled/aante-light.css @@ -1 +1 @@ -.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}input.kint-note-input{width:100%}.kint-rich{--main-background: #f8f8f8;--secondary-background: #f8f8f8;--variable-type-color: #06f;--variable-type-color-hover: #f00;--border-color: #d7d7d7;--border-color-hover: #aaa;--alternative-background: #fff;--highlight-color: #cfc;--caret-image: url("data:image/svg+xml;utf8,")}.kint-rich .kint-focused{box-shadow:0 0 3px 2px var(--variable-type-color-hover)}.kint-rich dt{font-weight:normal}.kint-rich dt.kint-parent{margin-top:4px}.kint-rich dl dl{margin-top:4px;padding-left:25px;border-left:none}.kint-rich>dl>dt{background:var(--secondary-background)}.kint-rich ul{margin:0;padding-left:0}.kint-rich ul:not(.kint-tabs)>li{border-left:0}.kint-rich ul.kint-tabs{background:var(--secondary-background);border:var(--border);border-width:0 1px 1px 1px;padding:4px 0 0 12px;margin-left:-1px;margin-top:-1px}.kint-rich ul.kint-tabs li,.kint-rich ul.kint-tabs li+li{margin:0 0 0 4px}.kint-rich ul.kint-tabs li{border-bottom-width:0;height:calc(var(--spacing)*6 + 1px)}.kint-rich ul.kint-tabs li:first-child{margin-left:0}.kint-rich ul.kint-tabs li.kint-active-tab{border-top:var(--border);background:var(--alternative-background);font-weight:bold;padding-top:0;border-bottom:1px solid var(--alternative-background) !important;margin-bottom:-1px}.kint-rich ul.kint-tabs li.kint-active-tab:hover{border-bottom:1px solid var(--alternative-background)}.kint-rich ul>li>pre{border:var(--border)}.kint-rich dt:hover+dd>ul{border-color:var(--border-color-hover)}.kint-rich pre{background:var(--alternative-background);margin-top:4px;margin-left:25px}.kint-rich .kint-source{margin-left:-1px}.kint-rich .kint-source .kint-highlight{background:var(--highlight-color)}.kint-rich .kint-parent.kint-show>.kint-search{border-bottom-width:1px}.kint-rich table td{background:var(--alternative-background)}.kint-rich table td>dl{padding:0;margin:0}.kint-rich table td>dl>dt.kint-parent{margin:0}.kint-rich table td:first-child,.kint-rich table td,.kint-rich table th{padding:2px 4px}.kint-rich table dd,.kint-rich table dt{background:var(--alternative-background)}.kint-rich table tr:hover>td{box-shadow:none;background:var(--highlight-color)} +.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}.kint-rich{--main-background: #f8f8f8;--secondary-background: #f8f8f8;--variable-type-color: #06f;--variable-type-color-hover: #f00;--border-color: #d7d7d7;--border-color-hover: #aaa;--alternative-background: #fff;--highlight-color: #cfc;--caret-image: url("data:image/svg+xml;utf8,")}.kint-rich .kint-focused{box-shadow:0 0 3px 2px var(--variable-type-color-hover)}.kint-rich dt{font-weight:normal}.kint-rich dt.kint-parent{margin-top:4px}.kint-rich dl dl{margin-top:4px;padding-left:25px;border-left:none}.kint-rich>dl>dt{background:var(--secondary-background)}.kint-rich ul{margin:0;padding-left:0}.kint-rich ul:not(.kint-tabs)>li{border-left:0}.kint-rich ul.kint-tabs{background:var(--secondary-background);border:var(--border);border-width:0 1px 1px 1px;padding:4px 0 0 12px;margin-left:-1px;margin-top:-1px}.kint-rich ul.kint-tabs li,.kint-rich ul.kint-tabs li+li{margin:0 0 0 4px}.kint-rich ul.kint-tabs li{border-bottom-width:0;height:calc(var(--spacing)*6 + 1px)}.kint-rich ul.kint-tabs li:first-child{margin-left:0}.kint-rich ul.kint-tabs li.kint-active-tab{border-top:var(--border);background:var(--alternative-background);font-weight:bold;padding-top:0;border-bottom:1px solid var(--alternative-background) !important;margin-bottom:-1px}.kint-rich ul.kint-tabs li.kint-active-tab:hover{border-bottom:1px solid var(--alternative-background)}.kint-rich ul>li>pre{border:var(--border)}.kint-rich dt:hover+dd>ul{border-color:var(--border-color-hover)}.kint-rich pre{background:var(--alternative-background);margin-top:4px;margin-left:25px}.kint-rich .kint-source{margin-left:-1px}.kint-rich .kint-source .kint-highlight{background:var(--highlight-color)}.kint-rich .kint-parent.kint-show>.kint-search{border-bottom-width:1px}.kint-rich table td{background:var(--alternative-background)}.kint-rich table td>dl{padding:0;margin:0}.kint-rich table td>dl>dt.kint-parent{margin:0}.kint-rich table td:first-child,.kint-rich table td,.kint-rich table th{padding:2px 4px}.kint-rich table dd,.kint-rich table dt{background:var(--alternative-background)}.kint-rich table tr:hover>td{box-shadow:none;background:var(--highlight-color)} diff --git a/system/ThirdParty/Kint/resources/compiled/main.js b/system/ThirdParty/Kint/resources/compiled/main.js index b1fab388..c88e6b90 100644 --- a/system/ThirdParty/Kint/resources/compiled/main.js +++ b/system/ThirdParty/Kint/resources/compiled/main.js @@ -1 +1 @@ -"use strict";(()=>{function m(n){if(!(n instanceof Element))throw new Error("Invalid argument to dedupeElement()");let t=n.ownerDocument,e=E(n);for(let s of t.querySelectorAll(e))n!==s&&s.parentNode.removeChild(s)}function d(n){return n instanceof Element?n.ownerDocument.contains(n):!1}function E(n){if(!(n instanceof Element))throw new Error("Invalid argument to buildClassSelector()");return[n.nodeName,...n.classList].join(".")}function f(n){if(!(n instanceof Element))throw new Error("Invalid argument to selectText()");let t=n.ownerDocument,e=t.getSelection(),s=t.createRange();s.selectNodeContents(n),e.removeAllRanges(),e.addRange(s)}function I(n,t){let e;return function(...s){clearTimeout(e),e=setTimeout(function(){n(...s)},t)}}function x(n){if(!(n instanceof Element))throw new Error("Invalid argument to offsetTop()");return n.offsetTop+(n.offsetParent?x(n.offsetParent):0)}var u=class n{static#e=new Set;static toggleSearchBox(t,e){let s=t.querySelector(".kint-search"),i=t.parentNode;if(s)if(s.classList.toggle("kint-show",e)){if(s.focus(),s.select(),!n.#e.has(s)){let r=i.querySelectorAll("dl").length,o=200;r>1e4&&(o=700),s.addEventListener("keyup",I(n.#t.bind(null,s),o)),n.#e.add(s)}n.#t(s)}else i.classList.remove("kint-search-root")}static#t(t){let e=t.closest(".kint-parent")?.parentNode;if(e)if(t.classList.contains("kint-show")&&t.value.length){let s=e.dataset.lastSearch;if(e.classList.add("kint-search-root"),s!==t.value){e.dataset.lastSearch=t.value,e.classList.remove("kint-search-match");for(let i of e.querySelectorAll(".kint-search-match"))i.classList.remove("kint-search-match");n.#s(e,t.value.toUpperCase())}}else e.classList.remove("kint-search-root")}static#s(t,e){let s=t.cloneNode(!0);for(let c of s.querySelectorAll(".access-path"))c.remove();if(!s.textContent.toUpperCase().includes(e))return;t.classList.add("kint-search-match");let i=t.firstElementChild;for(;i&&i.tagName!=="DT";)i=i.nextElementSibling;if(!i)return;let r=a.getChildContainer(i);if(!r)return;let o,l;for(let c of r.children)c.tagName==="DL"?n.#s(c,e):c.tagName==="UL"&&(c.classList.contains("kint-tabs")?o=c.children:c.classList.contains("kint-tab-contents")&&(l=c.children));if(!(!o||o.length!==l?.length))for(let c=o.length;c--;){let k=!1,F=!1;o[c].textContent.toUpperCase().includes(e)&&(k=!0);let O=l[c].cloneNode(!0);for(let v of O.querySelectorAll(".access-path"))v.remove();if(O.textContent.toUpperCase().includes(e)&&(k=!0,F=!0),k&&o[c].classList.add("kint-search-match"),F)for(let v of l[c].children)v.tagName==="DL"&&n.#s(v,e)}}};var g=class{static sort(t,e){let s=t.dataset.kintTableSort,i=parseInt(s)===e?-1:1,r=t.tBodies[0];[...r.rows].sort(function(o,l){o=o.cells[e].textContent.trim().toLocaleLowerCase(),l=l.cells[e].textContent.trim().toLocaleLowerCase();let c=0;return!isNaN(o)&&!isNaN(l)?(o=parseFloat(o),l=parseFloat(l),c=o-l):isNaN(o)&&!isNaN(l)?c=1:isNaN(l)&&!isNaN(o)?c=-1:c=(""+o).localeCompare(""+l),c*i}).forEach(o=>r.appendChild(o)),i<0?t.dataset.kintTableSort=null:t.dataset.kintTableSort=e}};var a=class n{#e;#t;#s;constructor(t){if(!(t instanceof h))throw new Error("Invalid argument to Rich.constructor()");this.#e=t,this.#e.runOnInit(this.#i.bind(this));let e=new q(this,t);new b(this,t.window,e)}#i(){let t=this.#e.window.document;if(d(this.#t)||(this.#t=t.querySelector("style.kint-rich-style")),this.#t&&m(this.#t),t.querySelector(".kint-rich.kint-file")){this.setupFolder(t);let e=this.#s.querySelector("dd.kint-foldout"),s=Array.from(t.querySelectorAll(".kint-rich.kint-file"));for(let i of s)i.parentNode!==e&&e.appendChild(i);this.#s.classList.add("kint-show")}}addToFolder(t){let e=t.closest(".kint-rich");if(!e)throw new Error("Bad addToFolder");let s=this.#e.window.document;if(this.setupFolder(s),this.folder.contains(t))throw new Error("Bad addToFolder");let i=this.#s.querySelector("dd.kint-foldout"),r=t.closest(".kint-parent, .kint-rich"),o=Array.from(e.querySelectorAll(".kint-folder-trigger"));if(e===r||e.querySelectorAll(".kint-rich > dl").length===1){for(let l of o)l.remove();e.classList.add("kint-file"),i.insertBefore(e,i.firstChild)}else{let l=s.createElement("div");l.classList.add("kint-rich"),l.classList.add("kint-file"),l.appendChild(r.closest(".kint-rich > dl"));let c=e.lastElementChild;c.matches(".kint-rich > footer")&&l.appendChild(c.cloneNode(!0));for(let k of o)k.remove();i.insertBefore(l,i.firstChild)}n.toggle(this.#s.querySelector(".kint-parent"),!0)}setupFolder(t){if(this.#s)d(this.#s)||(this.#s=t.querySelector(".kint-rich.kint-folder"));else{let e=t.createElement("template");e.innerHTML='
    Kint
    ',this.#s=e.content.firstChild,t.body.appendChild(this.#s)}}get folder(){return d(this.#s)||(this.#s=this.#e.window.document.querySelector(".kint-rich.kint-folder")),this.#s&&m(this.#s),this.#s}isFolderOpen(){let t=this.#s?.querySelector("dd.kint-foldout");if(t)return t.previousSibling.classList.contains("kint-show")}static getChildContainer(t){let e=t.nextElementSibling;for(;e&&!e.matches("dd");)e=e.nextElementSibling;return e}static toggle(t,e){let s=n.getChildContainer(t);s&&(e=t.classList.toggle("kint-show",e),n.#n(s,e))}static switchTab(t){t.parentNode.getElementsByClassName("kint-active-tab")[0].classList.remove("kint-active-tab"),t.classList.add("kint-active-tab");let e=t,s=0;for(;e=e.previousElementSibling;)s++;let i=t.parentNode.nextSibling.children;for(let r=i.length;r--;)r===s?(i[r].classList.add("kint-show"),n.#n(i[r],!0)):i[r].classList.remove("kint-show")}static toggleChildren(t,e){let s=n.getChildContainer(t);if(!s)return;e===void 0&&(e=t.classList.contains("kint-show"));let i=Array.from(s.getElementsByClassName("kint-parent"));for(let r of i)r.classList.toggle("kint-show",e)}static toggleAccessPath(t,e){let s=t.querySelector(".access-path");s?.classList.toggle("kint-show",e)&&f(s)}static#n(t,e){if(t.children.length===2&&t.lastElementChild.matches("ul.kint-tab-contents"))for(let s of t.lastElementChild.children)s.matches("li.kint-show")&&(t=s);if(t.children.length===1&&t.firstElementChild.matches("dl")){let s=t.firstElementChild.firstElementChild;s?.classList?.contains("kint-parent")&&n.toggle(s,e)}}},b=class{#e;#t;#s;#i=null;#n=null;#o=0;constructor(t,e,s){this.#e=t,this.#t=s,this.#s=e,this.#s.addEventListener("click",this.#a.bind(this),!0)}#r(){clearTimeout(this.#i),this.#i=setTimeout(this.#l.bind(this),250)}#l(){clearTimeout(this.#i),this.#i=null,this.#n=null,this.#o=0}#c(){let t=this.#n;if(!t.matches(".kint-parent > nav"))return;let e=t.parentNode;if(this.#o===1)a.toggleChildren(e),this.#t.onTreeChanged(),this.#r(),this.#o=2;else if(this.#o===2){this.#l();let s=e.classList.contains("kint-show"),i=this.#e.folder?.querySelector(".kint-parent"),r=Array.from(this.#s.document.getElementsByClassName("kint-parent"));for(let o of r)o!==i&&o.classList.toggle("kint-show",s);this.#t.onTreeChanged(),this.#t.scrollToFocus()}}#a(t){if(this.#o){this.#c();return}let e=t.target;if(!e.closest(".kint-rich"))return;if(e.tagName==="DFN"&&f(e),e.tagName==="TH"){t.ctrlKey||g.sort(e.closest("table"),e.cellIndex);return}if(e.tagName==="LI"&&e.parentNode.className==="kint-tabs"){if(e.className!=="kint-active-tab"){let i=e.closest("dl")?.querySelector(".kint-parent > nav")??e;a.switchTab(e),this.#t.onTreeChanged(),this.#t.setCursor(i)}return}let s=e.closest("dt");if(e.tagName==="NAV")e.parentNode.tagName==="FOOTER"?(this.#t.setCursor(e),e.parentNode.classList.toggle("kint-show")):s?.classList.contains("kint-parent")&&(a.toggle(s),this.#t.onTreeChanged(),this.#t.setCursor(e),this.#r(),this.#o=1,this.#n=e);else if(e.classList.contains("kint-access-path-trigger"))s&&a.toggleAccessPath(s);else if(e.classList.contains("kint-search-trigger"))s?.matches(".kint-rich > dl > dt.kint-parent")&&u.toggleSearchBox(s);else if(e.classList.contains("kint-folder-trigger")){if(s?.matches(".kint-rich > dl > dt.kint-parent"))this.#e.addToFolder(e),this.#t.onTreeChanged(),this.#t.setCursor(s.querySelector("nav")),this.#t.scrollToFocus();else if(e.parentNode.tagName==="FOOTER"){let i=e.closest(".kint-rich").querySelector(".kint-parent > nav, .kint-rich > footer > nav");this.#e.addToFolder(e),this.#t.onTreeChanged(),this.#t.setCursor(i),this.#t.scrollToFocus()}}else e.classList.contains("kint-search")||(e.tagName==="PRE"&&t.detail===3?f(e):e.closest(".kint-source")&&t.detail===3?f(e.closest(".kint-source")):e.classList.contains("access-path")?f(e):e.tagName!=="A"&&s?.classList.contains("kint-parent")&&(a.toggle(s),this.#t.onTreeChanged(),this.#t.setCursor(s.querySelector("nav"))))}},j=65,G=68,A=70,S=72,K=74,D=75,p=76,V=83,P=9,T=13,B=27,L=32,N=37,R=38,C=39,H=40,M=".kint-rich .kint-parent > nav, .kint-rich > footer > nav, .kint-rich .kint-tabs > li:not(.kint-active-tab)",q=class{#e=[];#t=0;#s=!1;#i;#n;constructor(t,e){this.#i=t,this.#n=e.window,this.#n.addEventListener("keydown",this.#c.bind(this),!0),e.runOnInit(this.onTreeChanged.bind(this))}scrollToFocus(){let t=this.#e[this.#t];if(!t)return;let e=this.#i.folder;if(t===e?.querySelector(".kint-parent > nav"))return;let s=x(t);if(this.#i.isFolderOpen()){let i=e.querySelector("dd.kint-foldout");i.scrollTo(0,s-i.clientHeight/2)}else this.#n.scrollTo(0,s-this.#n.innerHeight/2)}onTreeChanged(){let t=this.#e[this.#t];this.#e=[];let e=this.#i.folder,s=e?.querySelector(".kint-parent > nav"),i=this.#n.document;this.#i.isFolderOpen()&&(i=e,this.#e.push(s));let r=Array.from(i.querySelectorAll(M));for(let o of r)o.offsetParent!==null&&o!==s&&this.#e.push(o);if(s&&!this.#i.isFolderOpen()&&this.#e.push(s),this.#e.length===0){this.#s=!1,this.#r();return}t&&this.#e.indexOf(t)!==-1?this.#t=this.#e.indexOf(t):this.#r()}setCursor(t){if(this.#i.isFolderOpen()&&!this.#i.folder.contains(t)||!t.matches(M))return!1;let e=this.#e.indexOf(t);if(e===-1&&(this.onTreeChanged(),e=this.#e.indexOf(t)),e!==-1){if(e!==this.#t)return this.#t=e,this.#r(),!0;this.#e[e]?.classList.remove("kint-weak-focus")}else console.error("setCursor failed to find target in list",t),console.info("Please report this as a bug in Kint at https://github.com/kint-php/kint");return!1}#o(t){if(this.#e.length===0)return this.#t=0,null;for(this.#t+=t;this.#t<0;)this.#t+=this.#e.length;for(;this.#t>=this.#e.length;)this.#t-=this.#e.length;return this.#r(),this.#t}#r(){let t=this.#n.document.querySelector(".kint-focused");t&&(t.classList.remove("kint-focused"),t.classList.remove("kint-weak-focus")),this.#s&&this.#e[this.#t]?.classList.add("kint-focused")}#l(t){let e=t.closest(".kint-rich .kint-parent ~ dd")?.parentNode.querySelector(".kint-parent > nav");e&&(this.setCursor(e),this.scrollToFocus())}#c(t){if(this.#s&&t.keyCode===B&&t.target.matches(".kint-search")){t.target.blur(),this.#r();return}if(t.target!==this.#n.document.body||t.altKey||t.ctrlKey)return;if(t.keyCode===G){if(this.#s)this.#s=!1;else{if(this.#s=!0,this.onTreeChanged(),this.#e.length===0){this.#s=!1;return}this.scrollToFocus()}this.#r(),t.preventDefault();return}else if(t.keyCode===B){this.#s&&(this.#s=!1,this.#r(),t.preventDefault());return}else if(!this.#s)return;t.preventDefault(),d(this.#e[this.#t])||this.onTreeChanged();let e=this.#e[this.#t];if([P,R,D,H,K].includes(t.keyCode)){t.keyCode===P?this.#o(t.shiftKey?-1:1):t.keyCode===R||t.keyCode===D?this.#o(-1):(t.keyCode===H||t.keyCode===K)&&this.#o(1),this.scrollToFocus();return}if(e.tagName==="LI"&&[L,T,C,p,N,S].includes(t.keyCode)){t.keyCode===L||t.keyCode===T?(a.switchTab(e),this.onTreeChanged()):t.keyCode===C||t.keyCode===p?this.#o(1):(t.keyCode===N||t.keyCode===S)&&this.#o(-1),this.scrollToFocus();return}if(e.parentNode.tagName==="FOOTER"&&e.closest(".kint-rich")){if(t.keyCode===L||t.keyCode===T)e.parentNode.classList.toggle("kint-show");else if(t.keyCode===N||t.keyCode===S)if(e.parentNode.classList.contains("kint-show"))e.parentNode.classList.remove("kint-show");else{this.#l(e.closest(".kint-rich"));return}else if(t.keyCode===C||t.keyCode===p)e.parentNode.classList.add("kint-show");else if(t.keyCode===A&&!this.#i.isFolderOpen()&&e.matches(".kint-rich > footer > nav")){let i=e.closest(".kint-rich").querySelector(".kint-parent > nav, .kint-rich > footer > nav");this.#i.addToFolder(e),this.onTreeChanged(),this.setCursor(i),this.scrollToFocus()}return}let s=e.closest(".kint-parent");if(s){if(t.keyCode===j){a.toggleAccessPath(s);return}if(t.keyCode===A){!this.#i.isFolderOpen()&&s.matches(".kint-rich:not(.kint-folder) > dl > .kint-parent")&&(this.#i.addToFolder(e),this.onTreeChanged(),this.setCursor(e),this.scrollToFocus());return}if(t.keyCode===V){let i=s.closest(".kint-rich > dl")?.querySelector(".kint-search")?.closest(".kint-parent");if(i){e.classList.add("kint-weak-focus"),u.toggleSearchBox(i,!0);return}}if(t.keyCode===L||t.keyCode===T){a.toggle(s),this.onTreeChanged();return}if([C,p,N,S].includes(t.keyCode)){let i=s.classList.contains("kint-show");if(t.keyCode===C||t.keyCode===p){i&&a.toggleChildren(s,!0),a.toggle(s,!0),this.onTreeChanged();return}else if(i){a.toggleChildren(s,!1),a.toggle(s,!1),this.onTreeChanged();return}else{this.#l(s);return}}}}};var y=class{#e;#t;constructor(t){if(!(t instanceof h))throw new Error("Invalid argument to Plain.constructor()");this.#e=t.window,t.runOnInit(this.#s.bind(this))}#s(){d(this.#t)||(this.#t=this.#e.document.querySelector("style.kint-plain-style")),this.#t&&m(this.#t)}};var w=class{#e;constructor(t){if(!(t instanceof h))throw new Error("Invalid argument to Microtime.constructor()");this.#e=t.window,t.runOnInit(this.#t.bind(this))}#t(){let t={},e=this.#e.document.querySelectorAll("[data-kint-microtime-group]");for(let s of e){let i=s.querySelector(".kint-microtime-lap");if(!i)continue;let r=s.dataset.kintMicrotimeGroup,o=parseFloat(i.textContent),l=parseFloat(s.querySelector(".kint-microtime-avg").textContent);t[r]??={min:o,max:o,avg:l},t[r].min>o&&(t[r].min=o),t[r].maxo.avg){let l=(r-o.avg)/(o.max-o.avg);i.style.background="hsl("+(40-40*l)+", 100%, 65%)"}else{let l=0;o.avg!==o.min&&(l=(o.avg-r)/(o.avg-o.min)),i.style.background="hsl("+(40+80*l)+", 100%, 65%)"}}}};var U=Symbol(),h=class n{static#e=null;#t;#s=[];#i=new Set;static init(t){return n.#e??=new n(t,U),n.#e.#n(),n.#e.runOnLoad(n.#r),n.#e}get window(){return this.#t}constructor(t,e){if(U!==e)throw new Error("Kint constructor is private. Use Kint.init()");if(!(t instanceof Window))throw new Error("Invalid argument to Kint.init()");this.#t=t,this.runOnInit(this.#o.bind(this)),new y(this),new a(this),new w(this)}runOnLoad(t){if(this.#t.document.readyState==="complete")try{t()}catch{}else this.#t.addEventListener("load",t)}runOnInit(t){this.#s.push(t)}#n(){this.#t.document.currentScript&&(this.#i.add(E(window.document.currentScript)),window.document.currentScript.remove())}#o(){for(let t of this.#i.keys())for(let e of this.#t.document.querySelectorAll(t))e.remove()}static#r(){for(let t of n.#e.#s)t()}};window.Kint||(window.Kint=h);window.Kint.init(window);})(); +"use strict";(()=>{function m(n){if(!(n instanceof Element))throw new Error("Invalid argument to dedupeElement()");let t=n.ownerDocument,e=E(n);for(let s of t.querySelectorAll(e))n!==s&&s.parentNode.removeChild(s)}function d(n){return n instanceof Element?n.ownerDocument.contains(n):!1}function E(n){if(!(n instanceof Element))throw new Error("Invalid argument to buildClassSelector()");return[n.nodeName,...n.classList].join(".")}function f(n){if(!(n instanceof Element))throw new Error("Invalid argument to selectText()");let t=n.ownerDocument,e=t.getSelection(),s=t.createRange();s.selectNodeContents(n),e.removeAllRanges(),e.addRange(s)}function I(n,t){let e;return function(...s){clearTimeout(e),e=setTimeout(function(){n(...s)},t)}}function x(n){if(!(n instanceof Element))throw new Error("Invalid argument to offsetTop()");return n.offsetTop+(n.offsetParent?x(n.offsetParent):0)}var u=class n{static#e=new Set;static toggleSearchBox(t,e){let s=t.querySelector(".kint-search"),i=t.parentNode;if(s)if(s.classList.toggle("kint-show",e)){if(s.focus(),s.select(),!n.#e.has(s)){let r=i.querySelectorAll("dl").length,o=200;r>1e4&&(o=700),s.addEventListener("keyup",I(n.#t.bind(null,s),o)),n.#e.add(s)}n.#t(s)}else i.classList.remove("kint-search-root")}static#t(t){let e=t.closest(".kint-parent")?.parentNode;if(e)if(t.classList.contains("kint-show")&&t.value.length){let s=e.dataset.lastSearch;if(e.classList.add("kint-search-root"),s!==t.value){e.dataset.lastSearch=t.value,e.classList.remove("kint-search-match");for(let i of e.querySelectorAll(".kint-search-match"))i.classList.remove("kint-search-match");n.#s(e,t.value.toUpperCase())}}else e.classList.remove("kint-search-root")}static#s(t,e){let s=t.cloneNode(!0);for(let c of s.querySelectorAll(".access-path"))c.remove();if(!s.textContent.toUpperCase().includes(e))return;t.classList.add("kint-search-match");let i=t.firstElementChild;for(;i&&i.tagName!=="DT";)i=i.nextElementSibling;if(!i)return;let r=a.getChildContainer(i);if(!r)return;let o,l;for(let c of r.children)c.tagName==="DL"?n.#s(c,e):c.tagName==="UL"&&(c.classList.contains("kint-tabs")?o=c.children:c.classList.contains("kint-tab-contents")&&(l=c.children));if(!(!o||o.length!==l?.length))for(let c=o.length;c--;){let k=!1,F=!1;o[c].textContent.toUpperCase().includes(e)&&(k=!0);let O=l[c].cloneNode(!0);for(let v of O.querySelectorAll(".access-path"))v.remove();if(O.textContent.toUpperCase().includes(e)&&(k=!0,F=!0),k&&o[c].classList.add("kint-search-match"),F)for(let v of l[c].children)v.tagName==="DL"&&n.#s(v,e)}}};var g=class{static sort(t,e){let s=t.dataset.kintTableSort,i=parseInt(s)===e?-1:1,r=t.tBodies[0];[...r.rows].sort(function(o,l){o=o.cells[e].textContent.trim().toLocaleLowerCase(),l=l.cells[e].textContent.trim().toLocaleLowerCase();let c=0;return!isNaN(o)&&!isNaN(l)?(o=parseFloat(o),l=parseFloat(l),c=o-l):isNaN(o)&&!isNaN(l)?c=1:isNaN(l)&&!isNaN(o)?c=-1:c=(""+o).localeCompare(""+l),c*i}).forEach(o=>r.appendChild(o)),i<0?t.dataset.kintTableSort=null:t.dataset.kintTableSort=e}};var a=class n{#e;#t;#s;constructor(t){if(!(t instanceof h))throw new Error("Invalid argument to Rich.constructor()");this.#e=t,this.#e.runOnInit(this.#i.bind(this));let e=new q(this,t);new b(this,t.window,e)}#i(){let t=this.#e.window.document;if(d(this.#t)||(this.#t=t.querySelector("style.kint-rich-style")),this.#t&&m(this.#t),t.querySelector(".kint-rich.kint-file")){this.setupFolder(t);let e=this.#s.querySelector("dd.kint-foldout"),s=Array.from(t.querySelectorAll(".kint-rich.kint-file"));for(let i of s)i.parentNode!==e&&e.appendChild(i);this.#s.classList.add("kint-show")}}addToFolder(t){let e=t.closest(".kint-rich");if(!e)throw new Error("Bad addToFolder");let s=this.#e.window.document;if(this.setupFolder(s),this.folder.contains(t))throw new Error("Bad addToFolder");let i=this.#s.querySelector("dd.kint-foldout"),r=t.closest(".kint-parent, .kint-rich"),o=Array.from(e.querySelectorAll(".kint-folder-trigger"));if(e===r||e.querySelectorAll(".kint-rich > dl").length===1){for(let l of o)l.remove();e.classList.add("kint-file"),i.insertBefore(e,i.firstChild)}else{let l=s.createElement("div");l.classList.add("kint-rich"),l.classList.add("kint-file"),l.appendChild(r.closest(".kint-rich > dl"));let c=e.lastElementChild;c.matches(".kint-rich > footer")&&l.appendChild(c.cloneNode(!0));for(let k of o)k.remove();i.insertBefore(l,i.firstChild)}n.toggle(this.#s.querySelector(".kint-parent"),!0)}setupFolder(t){if(this.#s)d(this.#s)||(this.#s=t.querySelector(".kint-rich.kint-folder"));else{let e=t.createElement("template");e.innerHTML='
    Kint
    ',this.#s=e.content.firstChild,t.body.appendChild(this.#s)}}get folder(){return d(this.#s)||(this.#s=this.#e.window.document.querySelector(".kint-rich.kint-folder")),this.#s&&m(this.#s),this.#s}isFolderOpen(){let t=this.#s?.querySelector("dd.kint-foldout");if(t)return t.previousSibling.classList.contains("kint-show")}static getChildContainer(t){let e=t.nextElementSibling;for(;e&&!e.matches("dd");)e=e.nextElementSibling;return e}static toggle(t,e){let s=n.getChildContainer(t);s&&(e=t.classList.toggle("kint-show",e),n.#n(s,e))}static switchTab(t){t.parentNode.getElementsByClassName("kint-active-tab")[0].classList.remove("kint-active-tab"),t.classList.add("kint-active-tab");let e=t,s=0;for(;e=e.previousElementSibling;)s++;let i=t.parentNode.nextSibling.children;for(let r=i.length;r--;)r===s?(i[r].classList.add("kint-show"),n.#n(i[r],!0)):i[r].classList.remove("kint-show")}static toggleChildren(t,e){let s=n.getChildContainer(t);if(!s)return;e===void 0&&(e=t.classList.contains("kint-show"));let i=Array.from(s.getElementsByClassName("kint-parent"));for(let r of i)r.classList.toggle("kint-show",e)}static toggleAccessPath(t,e){let s=t.querySelector(".access-path");s?.classList.toggle("kint-show",e)&&f(s)}static#n(t,e){if(t.children.length===2&&t.lastElementChild.matches("ul.kint-tab-contents"))for(let s of t.lastElementChild.children)s.matches("li.kint-show")&&(t=s);if(t.children.length===1&&t.firstElementChild.matches("dl")){let s=t.firstElementChild.firstElementChild;s?.classList?.contains("kint-parent")&&n.toggle(s,e)}}},b=class{#e;#t;#s;#i=null;#n=null;#o=0;constructor(t,e,s){this.#e=t,this.#t=s,this.#s=e,this.#s.addEventListener("click",this.#a.bind(this),!0)}#r(){clearTimeout(this.#i),this.#i=setTimeout(this.#l.bind(this),250)}#l(){clearTimeout(this.#i),this.#i=null,this.#n=null,this.#o=0}#c(){let t=this.#n;if(!t.matches(".kint-parent > nav"))return;let e=t.parentNode;if(this.#o===1)a.toggleChildren(e),this.#t.onTreeChanged(),this.#r(),this.#o=2;else if(this.#o===2){this.#l();let s=e.classList.contains("kint-show"),i=this.#e.folder?.querySelector(".kint-parent"),r=Array.from(this.#s.document.getElementsByClassName("kint-parent"));for(let o of r)o!==i&&o.classList.toggle("kint-show",s);this.#t.onTreeChanged(),this.#t.scrollToFocus()}}#a(t){if(this.#o){this.#c();return}let e=t.target;if(!e.closest(".kint-rich"))return;if(e.tagName==="DFN"&&f(e),e.tagName==="TH"){t.ctrlKey||g.sort(e.closest("table"),e.cellIndex);return}if(e.tagName==="LI"&&e.parentNode.className==="kint-tabs"){if(e.className!=="kint-active-tab"){let i=e.closest("dl")?.querySelector(".kint-parent > nav")??e;a.switchTab(e),this.#t.onTreeChanged(),this.#t.setCursor(i)}return}let s=e.closest("dt");if(e.tagName==="NAV")e.parentNode.tagName==="FOOTER"?(this.#t.setCursor(e),e.parentNode.classList.toggle("kint-show")):s?.classList.contains("kint-parent")&&(a.toggle(s),this.#t.onTreeChanged(),this.#t.setCursor(e),this.#r(),this.#o=1,this.#n=e);else if(e.classList.contains("kint-access-path-trigger"))s&&a.toggleAccessPath(s);else if(e.classList.contains("kint-search-trigger"))s?.matches(".kint-rich > dl > dt.kint-parent")&&u.toggleSearchBox(s);else if(e.classList.contains("kint-folder-trigger")){if(s?.matches(".kint-rich > dl > dt.kint-parent"))this.#e.addToFolder(e),this.#t.onTreeChanged(),this.#t.setCursor(s.querySelector("nav")),this.#t.scrollToFocus();else if(e.parentNode.tagName==="FOOTER"){let i=e.closest(".kint-rich").querySelector(".kint-parent > nav, .kint-rich > footer > nav");this.#e.addToFolder(e),this.#t.onTreeChanged(),this.#t.setCursor(i),this.#t.scrollToFocus()}}else e.classList.contains("kint-search")||(e.tagName==="PRE"&&t.detail===3?f(e):e.closest(".kint-source")&&t.detail===3?f(e.closest(".kint-source")):e.classList.contains("access-path")?f(e):e.tagName!=="A"&&s?.classList.contains("kint-parent")&&(a.toggle(s),this.#t.onTreeChanged(),this.#t.setCursor(s.querySelector("nav"))))}},j=65,G=68,A=70,S=72,K=74,D=75,p=76,V=83,P=9,T=13,B=27,L=32,N=37,R=38,C=39,H=40,M=".kint-rich .kint-parent > nav, .kint-rich > footer > nav, .kint-rich .kint-tabs > li:not(.kint-active-tab)",q=class{#e=[];#t=0;#s=!1;#i;#n;constructor(t,e){this.#i=t,this.#n=e.window,this.#n.addEventListener("keydown",this.#c.bind(this),!0),e.runOnInit(this.onTreeChanged.bind(this))}scrollToFocus(){let t=this.#e[this.#t];if(!t)return;let e=this.#i.folder;if(t===e?.querySelector(".kint-parent > nav"))return;let s=x(t);if(this.#i.isFolderOpen()){let i=e.querySelector("dd.kint-foldout");i.scrollTo(0,s-i.clientHeight/2)}else this.#n.scrollTo(0,s-this.#n.innerHeight/2)}onTreeChanged(){let t=this.#e[this.#t];this.#e=[];let e=this.#i.folder,s=e?.querySelector(".kint-parent > nav"),i=this.#n.document;this.#i.isFolderOpen()&&(i=e,this.#e.push(s));let r=Array.from(i.querySelectorAll(M));for(let o of r)o.offsetParent!==null&&o!==s&&this.#e.push(o);if(s&&!this.#i.isFolderOpen()&&this.#e.push(s),this.#e.length===0){this.#s=!1,this.#r();return}t&&this.#e.indexOf(t)!==-1?this.#t=this.#e.indexOf(t):this.#r()}setCursor(t){if(this.#i.isFolderOpen()&&!this.#i.folder.contains(t)||!t.matches(M))return!1;let e=this.#e.indexOf(t);if(e===-1&&(this.onTreeChanged(),e=this.#e.indexOf(t)),e!==-1){if(e!==this.#t)return this.#t=e,this.#r(),!0;this.#e[e]?.classList.remove("kint-weak-focus")}else console.error("setCursor failed to find target in list",t),console.info("Please report this as a bug in Kint at https://github.com/kint-php/kint");return!1}#o(t){if(this.#e.length===0)return this.#t=0,null;for(this.#t+=t;this.#t<0;)this.#t+=this.#e.length;for(;this.#t>=this.#e.length;)this.#t-=this.#e.length;return this.#r(),this.#t}#r(){let t=this.#n.document.querySelector(".kint-focused");t&&(t.classList.remove("kint-focused"),t.classList.remove("kint-weak-focus")),this.#s&&this.#e[this.#t]?.classList.add("kint-focused")}#l(t){let e=t.closest(".kint-rich .kint-parent ~ dd")?.parentNode.querySelector(".kint-parent > nav");e&&(this.setCursor(e),this.scrollToFocus())}#c(t){if(t.keyCode===B&&t.target.matches(".kint-search")){t.target.blur(),this.#s&&this.#r();return}if(t.target!==this.#n.document.body||t.altKey||t.ctrlKey)return;if(t.keyCode===G){if(this.#s)this.#s=!1;else{if(this.#s=!0,this.onTreeChanged(),this.#e.length===0){this.#s=!1;return}this.scrollToFocus()}this.#r(),t.preventDefault();return}else if(t.keyCode===B){this.#s&&(this.#s=!1,this.#r(),t.preventDefault());return}else if(!this.#s)return;t.preventDefault(),d(this.#e[this.#t])||this.onTreeChanged();let e=this.#e[this.#t];if([P,R,D,H,K].includes(t.keyCode)){t.keyCode===P?this.#o(t.shiftKey?-1:1):t.keyCode===R||t.keyCode===D?this.#o(-1):(t.keyCode===H||t.keyCode===K)&&this.#o(1),this.scrollToFocus();return}if(e.tagName==="LI"&&[L,T,C,p,N,S].includes(t.keyCode)){t.keyCode===L||t.keyCode===T?(a.switchTab(e),this.onTreeChanged()):t.keyCode===C||t.keyCode===p?this.#o(1):(t.keyCode===N||t.keyCode===S)&&this.#o(-1),this.scrollToFocus();return}if(e.parentNode.tagName==="FOOTER"&&e.closest(".kint-rich")){if(t.keyCode===L||t.keyCode===T)e.parentNode.classList.toggle("kint-show");else if(t.keyCode===N||t.keyCode===S)if(e.parentNode.classList.contains("kint-show"))e.parentNode.classList.remove("kint-show");else{this.#l(e.closest(".kint-rich"));return}else if(t.keyCode===C||t.keyCode===p)e.parentNode.classList.add("kint-show");else if(t.keyCode===A&&!this.#i.isFolderOpen()&&e.matches(".kint-rich > footer > nav")){let i=e.closest(".kint-rich").querySelector(".kint-parent > nav, .kint-rich > footer > nav");this.#i.addToFolder(e),this.onTreeChanged(),this.setCursor(i),this.scrollToFocus()}return}let s=e.closest(".kint-parent");if(s){if(t.keyCode===j){a.toggleAccessPath(s);return}if(t.keyCode===A){!this.#i.isFolderOpen()&&s.matches(".kint-rich:not(.kint-folder) > dl > .kint-parent")&&(this.#i.addToFolder(e),this.onTreeChanged(),this.setCursor(e),this.scrollToFocus());return}if(t.keyCode===V){let i=s.closest(".kint-rich > dl")?.querySelector(".kint-search")?.closest(".kint-parent");if(i){e.classList.add("kint-weak-focus"),u.toggleSearchBox(i,!0);return}}if(t.keyCode===L||t.keyCode===T){a.toggle(s),this.onTreeChanged();return}if([C,p,N,S].includes(t.keyCode)){let i=s.classList.contains("kint-show");if(t.keyCode===C||t.keyCode===p){i&&a.toggleChildren(s,!0),a.toggle(s,!0),this.onTreeChanged();return}else if(i){a.toggleChildren(s,!1),a.toggle(s,!1),this.onTreeChanged();return}else{this.#l(s);return}}}}};var y=class{#e;#t;constructor(t){if(!(t instanceof h))throw new Error("Invalid argument to Plain.constructor()");this.#e=t.window,t.runOnInit(this.#s.bind(this))}#s(){d(this.#t)||(this.#t=this.#e.document.querySelector("style.kint-plain-style")),this.#t&&m(this.#t)}};var w=class{#e;constructor(t){if(!(t instanceof h))throw new Error("Invalid argument to Microtime.constructor()");this.#e=t.window,t.runOnInit(this.#t.bind(this))}#t(){let t={},e=this.#e.document.querySelectorAll("[data-kint-microtime-group]");for(let s of e){let i=s.querySelector(".kint-microtime-lap");if(!i)continue;let r=s.dataset.kintMicrotimeGroup,o=parseFloat(i.textContent),l=parseFloat(s.querySelector(".kint-microtime-avg").textContent);t[r]??={min:o,max:o,avg:l},t[r].min>o&&(t[r].min=o),t[r].maxo.avg){let l=(r-o.avg)/(o.max-o.avg);i.style.background="hsl("+(40-40*l)+", 100%, 65%)"}else{let l=0;o.avg!==o.min&&(l=(o.avg-r)/(o.avg-o.min)),i.style.background="hsl("+(40+80*l)+", 100%, 65%)"}}}};var U=Symbol(),h=class n{static#e=null;#t;#s=[];#i=new Set;static init(t){return n.#e??=new n(t,U),n.#e.#n(),n.#e.runOnLoad(n.#r),n.#e}get window(){return this.#t}constructor(t,e){if(U!==e)throw new Error("Kint constructor is private. Use Kint.init()");if(!(t instanceof Window))throw new Error("Invalid argument to Kint.init()");this.#t=t,this.runOnInit(this.#o.bind(this)),new y(this),new a(this),new w(this)}runOnLoad(t){if(this.#t.document.readyState==="complete")try{t()}catch{}else this.#t.addEventListener("load",t)}runOnInit(t){this.#s.push(t)}#n(){this.#t.document.currentScript&&(this.#i.add(E(window.document.currentScript)),window.document.currentScript.remove())}#o(){for(let t of this.#i.keys())for(let e of this.#t.document.querySelectorAll(t))e.remove()}static#r(){for(let t of n.#e.#s)t()}};window.Kint||(window.Kint=h);window.Kint.init(window);})(); diff --git a/system/ThirdParty/Kint/resources/compiled/original.css b/system/ThirdParty/Kint/resources/compiled/original.css index d4993ab2..765e5633 100644 --- a/system/ThirdParty/Kint/resources/compiled/original.css +++ b/system/ThirdParty/Kint/resources/compiled/original.css @@ -1 +1 @@ -.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}input.kint-note-input{width:100%}.kint-rich{--gradient-start: #e3ecf0;--gradient-end: #c0d4df;--tabs-gradient-start: #9dbed0;--tabs-gradient-end: #b2ccda}.kint-rich>dl>dt{background:linear-gradient(to bottom, var(--gradient-start) 0%, var(--gradient-end) 100%)}.kint-rich>dl>dd>ul.kint-tabs li{background:var(--main-background)}.kint-rich>dl>dd>ul.kint-tabs li.kint-active-tab{background:var(--secondary-background)}.kint-rich ul.kint-tabs{background:linear-gradient(to bottom, var(--tabs-gradient-start) 0%, var(--tabs-gradient-end) 100%)} +.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}.kint-rich{--gradient-start: #e3ecf0;--gradient-end: #c0d4df;--tabs-gradient-start: #9dbed0;--tabs-gradient-end: #b2ccda}.kint-rich>dl>dt{background:linear-gradient(to bottom, var(--gradient-start) 0%, var(--gradient-end) 100%)}.kint-rich>dl>dd>ul.kint-tabs li{background:var(--main-background)}.kint-rich>dl>dd>ul.kint-tabs li.kint-active-tab{background:var(--secondary-background)}.kint-rich ul.kint-tabs{background:linear-gradient(to bottom, var(--tabs-gradient-start) 0%, var(--tabs-gradient-end) 100%)} diff --git a/system/ThirdParty/Kint/resources/compiled/plain.css b/system/ThirdParty/Kint/resources/compiled/plain.css index 8d47b53a..0dd9b2d0 100644 --- a/system/ThirdParty/Kint/resources/compiled/plain.css +++ b/system/ThirdParty/Kint/resources/compiled/plain.css @@ -1 +1 @@ -.kint-plain{background:rgba(255,255,255,.9);white-space:pre;display:block;font-family:monospace;color:#222;line-height:normal}.kint-plain i{color:#d00;font-style:normal}.kint-plain u{color:#030;text-decoration:none;font-weight:bold}.kint-plain .kint-microtime-js .kint-microtime-lap{text-shadow:1px 0 #d00,0 1px #d00,-1px 0 #d00,0 -1px #d00;color:#fff;font-weight:bold} +.kint-plain{background:hsla(0,0%,100%,.9);white-space:pre;display:block;font-family:monospace;color:#222;line-height:normal}.kint-plain i{color:#d00;font-style:normal}.kint-plain u{color:#030;text-decoration:none;font-weight:bold}.kint-plain .kint-microtime-js .kint-microtime-lap{text-shadow:1px 0 #d00,0 1px #d00,-1px 0 #d00,0 -1px #d00;color:#fff;font-weight:bold} diff --git a/system/ThirdParty/Kint/resources/compiled/solarized-dark.css b/system/ThirdParty/Kint/resources/compiled/solarized-dark.css index 529a9ce4..87b7206e 100644 --- a/system/ThirdParty/Kint/resources/compiled/solarized-dark.css +++ b/system/ThirdParty/Kint/resources/compiled/solarized-dark.css @@ -1 +1 @@ -.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}input.kint-note-input{width:100%}.kint-rich{--spacing: 5px;--main-background: #002b36;--secondary-background: #073642;--backdrop-color: var(--secondary-background);--text-color: #839496;--variable-name-color: #93a1a1;--variable-type-color: #268bd2;--variable-type-color-hover: #2aa198;--border-color: #586e75;--border-color-hover: #268bd2;--caret-image: url("data:image/svg+xml;utf8,")}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px var(--variable-type-color) inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px}.kint-rich footer li{color:#ddd} +.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}.kint-rich{--spacing: 5px;--main-background: #002b36;--secondary-background: #073642;--backdrop-color: var(--secondary-background);--text-color: #839496;--variable-name-color: #93a1a1;--variable-type-color: #268bd2;--variable-type-color-hover: #2aa198;--border-color: #586e75;--border-color-hover: #268bd2;--caret-image: url("data:image/svg+xml;utf8,")}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px var(--variable-type-color) inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px}.kint-rich footer li{color:#ddd} diff --git a/system/ThirdParty/Kint/resources/compiled/solarized.css b/system/ThirdParty/Kint/resources/compiled/solarized.css index 0db55c01..1c469b50 100644 --- a/system/ThirdParty/Kint/resources/compiled/solarized.css +++ b/system/ThirdParty/Kint/resources/compiled/solarized.css @@ -1 +1 @@ -.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}input.kint-note-input{width:100%}.kint-rich{--spacing: 5px;--main-background: #fdf6e3;--secondary-background: #eee8d5;--text-color: #657b83;--variable-name-color: #586e75;--variable-type-color: #268bd2;--variable-type-color-hover: #2aa198;--border-color: #93a1a1;--border-color-hover: #268bd2;--caret-image: url("data:image/svg+xml;utf8,")}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px var(--variable-type-color) inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px} +.kint-rich{--spacing: 4px;--nav-size: 15px;--backdrop-color: rgba(255, 255, 255, 0.9);--main-background: #e0eaef;--secondary-background: #c1d4df;--text-color: #1d1e1e;--variable-name-color: #1d1e1e;--variable-type-color: #0092db;--variable-type-color-hover: #5cb730;--border-color: #b6cedb;--border-color-hover: #0092db;--border: 1px solid var(--border-color);--foldout-max-size: calc(100vh - 100px);--foldout-zindex: 999999;--caret-image: url("data:image/svg+xml;utf8,");--ap-image: url("data:image/svg+xml;utf8,");--folder-image: url("data:image/svg+xml;utf8,");--search-image: url("data:image/svg+xml;utf8,");font-size:13px;overflow-x:auto;white-space:nowrap;background:var(--backdrop-color);direction:ltr;contain:content}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:var(--foldout-zindex);width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:var(--foldout-max-size);padding-right:calc(var(--spacing)*2);overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection{background:var(--border-color-hover);color:var(--text-color)}.kint-rich .kint-focused{box-shadow:0 0 3px 3px var(--variable-type-color-hover)}.kint-rich .kint-focused.kint-weak-focus{box-shadow:0 0 3px 1px color-mix(in srgb, var(--variable-type-color-hover) 50%, transparent)}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:var(--text-color);float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:calc(var(--spacing)*2) 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:var(--main-background);border:var(--border);color:var(--text-color);display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:var(--spacing)}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:var(--border-color-hover)}.kint-rich>dl dl{padding:0 0 0 calc(var(--spacing)*3)}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:var(--caret-image) no-repeat scroll 0 0/var(--nav-size) 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:var(--nav-size);width:var(--nav-size);margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed var(--border-color);contain:strict}.kint-rich dt.kint-parent.kint-show+dd{display:block;contain:content}.kint-rich var,.kint-rich var a{color:var(--variable-type-color);font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:var(--variable-type-color-hover)}.kint-rich dfn{font-style:normal;font-family:monospace;color:var(--variable-name-color)}.kint-rich pre{color:var(--text-color);margin:0 0 0 calc(var(--spacing)*3);padding:5px;overflow-y:hidden;border-top:0;border:var(--border);background:var(--main-background);display:block;word-break:normal}.kint-rich .kint-access-path-trigger,.kint-rich .kint-folder-trigger,.kint-rich .kint-search-trigger{background:color-mix(in srgb, var(--text-color) 80%, transparent);border-radius:3px;padding:2px;height:var(--nav-size);width:var(--nav-size);font-size:var(--nav-size);margin-left:5px;font-weight:bold;text-align:center;line-height:1;float:right !important;cursor:pointer;position:relative;overflow:hidden}.kint-rich .kint-access-path-trigger::before,.kint-rich .kint-folder-trigger::before,.kint-rich .kint-search-trigger::before{display:block;content:"";width:100%;height:100%;background:var(--main-background);mask:center/contain no-repeat alpha}.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-folder-trigger:hover,.kint-rich .kint-search-trigger:hover{background:var(--main-background)}.kint-rich .kint-access-path-trigger:hover::before,.kint-rich .kint-folder-trigger:hover::before,.kint-rich .kint-search-trigger:hover::before{background:var(--text-color)}.kint-rich .kint-access-path-trigger::before{mask-image:var(--ap-image)}.kint-rich .kint-folder-trigger::before{mask-image:var(--folder-image)}.kint-rich .kint-search-trigger::before{mask-image:var(--search-image)}.kint-rich input.kint-search{display:none;border:var(--border);border-top-width:0;border-bottom-width:0;padding:var(--spacing);float:right !important;margin:calc(var(--spacing)*-1) 0;color:var(--variable-name-color);background:var(--secondary-background);height:calc(var(--nav-size) + var(--spacing)*2);width:calc(var(--nav-size)*10);position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:var(--secondary-background);filter:saturate(0);opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:var(--main-background);filter:saturate(0)}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:var(--secondary-background);display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger{background:rgba(0,0,0,0)}.kint-rich footer>.kint-folder-trigger::before{background:var(--text-color)}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:var(--text-color);text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:var(--variable-name-color);border-bottom:1px dotted var(--variable-name-color)}.kint-rich ul{list-style:none;padding-left:calc(var(--spacing)*3)}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed var(--border-color)}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 calc(var(--spacing)*3);padding-left:0;background:var(--main-background);border:var(--border);border-top:0}.kint-rich ul.kint-tabs>li{background:var(--secondary-background);border:var(--border);cursor:pointer;display:inline-block;height:calc(var(--spacing)*6);margin:calc(var(--spacing)/2);padding:0 calc(2px + var(--spacing)*2.5);vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich ul.kint-tabs>li.kint-active-tab{background:var(--main-background);border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:calc(var(--spacing)*5)}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none;contain:strict}.kint-rich ul.kint-tab-contents>li.kint-show{display:block;contain:content}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:var(--border-color-hover);color:var(--variable-type-color-hover)}.kint-rich dt>.kint-color-preview{width:var(--nav-size);height:var(--nav-size);display:inline-block;vertical-align:middle;margin-left:10px;border:var(--border);background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:min(20px,100%)}.kint-rich dt>.kint-color-preview:hover{border-color:var(--border-color-hover)}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:calc(var(--spacing)/2)}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:var(--border);padding:calc(var(--spacing)/2);vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:var(--secondary-background);color:var(--variable-name-color)}.kint-rich table td{background:var(--main-background);white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 var(--border-color-hover) inset}.kint-rich table tr:hover var{color:var(--variable-type-color-hover)}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:1px solid var(--secondary-background)}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid var(--border-color-hover);padding-right:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.kint-rich pre.kint-source>div.kint-highlight{background:var(--secondary-background)}.kint-rich .kint-microtime-js .kint-microtime-lap{text-shadow:-1px 0 var(--border-color-hover),0 1px var(--border-color-hover),1px 0 var(--border-color-hover),0 -1px var(--border-color-hover);color:var(--main-background);font-weight:bold}.kint-rich{--spacing: 5px;--main-background: #fdf6e3;--secondary-background: #eee8d5;--text-color: #657b83;--variable-name-color: #586e75;--variable-type-color: #268bd2;--variable-type-color-hover: #2aa198;--border-color: #93a1a1;--border-color-hover: #268bd2;--caret-image: url("data:image/svg+xml;utf8,")}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px var(--variable-type-color) inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px} diff --git a/system/Throttle/Throttler.php b/system/Throttle/Throttler.php index c023b5e4..fba7665d 100644 --- a/system/Throttle/Throttler.php +++ b/system/Throttle/Throttler.php @@ -90,8 +90,6 @@ public function getTokenTime(): int * @param int $capacity The number of requests the "bucket" can hold * @param int $seconds The time it takes the "bucket" to completely refill * @param int $cost The number of tokens this action uses. - * - * @internal param int $maxRequests */ public function check(string $key, int $capacity, int $seconds, int $cost = 1): bool { diff --git a/system/Validation/FormatRules.php b/system/Validation/FormatRules.php index 502f3ae0..e28ef977 100644 --- a/system/Validation/FormatRules.php +++ b/system/Validation/FormatRules.php @@ -284,7 +284,13 @@ public function valid_base64($str = null): bool $str = (string) $str; } - return base64_encode(base64_decode($str, true)) === $str; + $decoded = base64_decode($str, true); + + if ($decoded === false) { + return false; + } + + return base64_encode($decoded) === $str; } /** @@ -416,7 +422,7 @@ public function valid_url($str = null): bool */ public function valid_url_strict($str = null, ?string $validSchemes = null): bool { - if ($str === null || $str === '' || $str === '0') { + if (in_array($str, [null, '', '0'], true)) { return false; } diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index d3f2a3dc..37cf5524 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -918,7 +918,7 @@ protected function getErrorMessage( $args = [ 'field' => ($label === null || $label === '') ? $field : lang($label), - 'param' => (! isset($this->rules[$param]['label'])) ? $param : lang($this->rules[$param]['label']), + 'param' => isset($this->rules[$param]['label']) ? lang($this->rules[$param]['label']) : $param, 'value' => $value ?? '', ]; diff --git a/system/View/Cell.php b/system/View/Cell.php index 8325accc..3a3e9595 100644 --- a/system/View/Cell.php +++ b/system/View/Cell.php @@ -125,7 +125,7 @@ public function render(string $library, $params = null, int $ttl = 0, ?string $c public function prepareParams($params) { if ( - ($params === null || $params === '' || $params === []) + in_array($params, [null, '', []], true) || (! is_string($params) && ! is_array($params)) ) { return []; @@ -154,10 +154,6 @@ public function prepareParams($params) unset($newParams); } - if ($params === []) { - return []; - } - return $params; } diff --git a/system/View/Cells/Cell.php b/system/View/Cells/Cell.php index ffe652ba..58149902 100644 --- a/system/View/Cells/Cell.php +++ b/system/View/Cells/Cell.php @@ -85,7 +85,7 @@ final protected function view(?string $view, array $data = []): string $viewName = decamelize(class_basename(static::class)); $directory = dirname((new ReflectionClass($this))->getFileName()) . DIRECTORY_SEPARATOR; - $possibleView1 = $directory . substr($viewName, 0, strrpos($viewName, '_cell')) . '.php'; + $possibleView1 = $directory . substr($viewName, 0, (int) strrpos($viewName, '_cell')) . '.php'; $possibleView2 = $directory . $viewName . '.php'; }