From 7c663e1b51d36a49fcdfc151cf2991821d6aa4d6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 30 Jul 2023 08:45:46 +0000 Subject: [PATCH] Release v4.3.7 --- app/Config/App.php | 2 +- app/Config/Autoload.php | 5 +- app/Config/Cache.php | 17 +-- app/Config/ContentSecurityPolicy.php | 2 +- app/Config/Filters.php | 2 +- app/Config/Kint.php | 18 +++ app/Config/Logger.php | 4 +- app/Config/Migrations.php | 2 +- app/Config/Modules.php | 6 + app/Config/Paths.php | 5 + app/Controllers/BaseController.php | 2 +- app/Controllers/Home.php | 2 +- system/Autoloader/Autoloader.php | 4 + system/BaseModel.php | 21 +++- system/CLI/BaseCommand.php | 3 +- system/CLI/CLI.php | 2 +- system/CLI/GeneratorTrait.php | 3 +- system/Cache/Handlers/BaseHandler.php | 3 +- system/Cache/Handlers/FileHandler.php | 58 +++++---- system/Cache/Handlers/MemcachedHandler.php | 3 + system/Cache/Handlers/PredisHandler.php | 3 + system/Cache/Handlers/RedisHandler.php | 5 +- system/Cache/Handlers/WincacheHandler.php | 3 + system/CodeIgniter.php | 12 +- system/Commands/Cache/ClearCache.php | 3 +- system/Commands/Cache/InfoCache.php | 3 +- system/Commands/Database/CreateDatabase.php | 5 +- .../Generators/MigrationGenerator.php | 11 +- .../Generators/SessionMigrationGenerator.php | 6 +- system/Commands/Utilities/Routes.php | 3 +- .../Utilities/Routes/FilterCollector.php | 3 +- .../Utilities/Routes/FilterFinder.php | 3 +- system/Common.php | 16 +-- system/Config/BaseConfig.php | 2 +- system/Config/BaseService.php | 4 +- system/Config/Factories.php | 2 +- system/Config/Services.php | 58 ++++----- system/Controller.php | 3 +- system/Database/Config.php | 5 +- system/Database/Migration.php | 2 +- system/Database/MigrationRunner.php | 4 +- system/Database/OCI8/Connection.php | 2 +- system/Database/OCI8/Forge.php | 2 +- system/Debug/Exceptions.php | 47 ++++++-- system/Debug/Toolbar.php | 2 +- system/Debug/Toolbar/Collectors/Config.php | 3 +- system/Debug/Toolbar/Collectors/Database.php | 3 +- system/Encryption/Handlers/BaseHandler.php | 10 +- system/Events/Events.php | 3 +- system/Filters/FilterInterface.php | 4 +- system/Filters/Filters.php | 2 +- system/Format/FormatterInterface.php | 2 +- system/HTTP/CLIRequest.php | 8 +- system/HTTP/CURLRequest.php | 10 +- system/HTTP/ContentSecurityPolicy.php | 5 +- system/HTTP/DownloadResponse.php | 3 +- system/HTTP/Files/FileCollection.php | 4 +- system/HTTP/MessageInterface.php | 4 +- system/HTTP/MessageTrait.php | 2 +- system/HTTP/RequestTrait.php | 114 +++++++++--------- system/HTTP/Response.php | 3 +- system/HTTP/ResponseTrait.php | 10 +- system/HTTP/URI.php | 7 +- system/HTTP/UserAgent.php | 2 +- system/Helpers/array_helper.php | 6 +- system/Helpers/cookie_helper.php | 6 +- system/Helpers/filesystem_helper.php | 4 +- system/Helpers/form_helper.php | 8 +- system/Helpers/number_helper.php | 25 ++-- system/Helpers/text_helper.php | 12 +- system/Helpers/url_helper.php | 10 +- system/Model.php | 6 +- system/Publisher/Publisher.php | 3 +- system/Router/AutoRouter.php | 52 +++++--- system/Router/AutoRouterImproved.php | 3 + system/Router/RouteCollection.php | 16 ++- system/Router/Router.php | 11 +- system/Security/Security.php | 4 +- system/Session/Handlers/BaseHandler.php | 4 +- system/Session/Handlers/DatabaseHandler.php | 2 +- system/Session/Handlers/MemcachedHandler.php | 2 +- system/Session/Handlers/RedisHandler.php | 2 +- system/Session/Session.php | 4 +- system/Test/CIUnitTestCase.php | 5 +- system/Test/ControllerTestTrait.php | 2 +- system/Test/ControllerTester.php | 2 +- system/Test/Fabricator.php | 3 +- system/Test/FeatureTestCase.php | 3 +- system/Test/FeatureTestTrait.php | 46 ++++--- system/Test/FilterTestTrait.php | 2 +- system/Test/TestResponse.php | 4 +- system/ThirdParty/Kint/Parser/Parser.php | 6 +- system/ThirdParty/Kint/Utils.php | 4 + .../Kint/resources/compiled/rich.js | 2 +- system/Typography/Typography.php | 4 +- system/Validation/Validation.php | 20 +-- system/View/Cell.php | 12 +- system/View/ViewDecoratorTrait.php | 3 +- 98 files changed, 513 insertions(+), 357 deletions(-) diff --git a/app/Config/App.php b/app/Config/App.php index 67eaf7da..f04703ba 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -126,7 +126,7 @@ class App extends BaseConfig /** * -------------------------------------------------------------------------- - * URI PROTOCOL + * Force Global Secure Requests * -------------------------------------------------------------------------- * * If true, this will force every request made to this application to be diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index abd9df9d..e9ee6613 100644 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -13,7 +13,10 @@ * can find the files as needed. * * NOTE: If you use an identical key in $psr4 or $classmap, then - * the values in this file will overwrite the framework's values. + * the values in this file will overwrite the framework's values. + * + * NOTE: This class is required prior to Autoloader instantiation, + * and does not extend BaseConfig. */ class Autoload extends AutoloadConfig { diff --git a/app/Config/Cache.php b/app/Config/Cache.php index 659d9ed6..61cdd50d 100644 --- a/app/Config/Cache.php +++ b/app/Config/Cache.php @@ -2,6 +2,7 @@ namespace Config; +use CodeIgniter\Cache\CacheInterface; use CodeIgniter\Cache\Handlers\DummyHandler; use CodeIgniter\Cache\Handlers\FileHandler; use CodeIgniter\Cache\Handlers\MemcachedHandler; @@ -53,12 +54,12 @@ class Cache extends BaseConfig * Whether to take the URL query string into consideration when generating * output cache files. Valid options are: * - * false = Disabled - * true = Enabled, take all query parameters into account. - * Please be aware that this may result in numerous cache - * files generated for the same page over and over again. - * array('q') = Enabled, but only take into account the specified list - * of query parameters. + * false = Disabled + * true = Enabled, take all query parameters into account. + * Please be aware that this may result in numerous cache + * files generated for the same page over and over again. + * ['q'] = Enabled, but only take into account the specified list + * of query parameters. * * @var bool|string[] */ @@ -95,7 +96,8 @@ class Cache extends BaseConfig * A string of reserved characters that will not be allowed in keys or tags. * Strings that violate this restriction will cause handlers to throw. * Default: {}()/\@: - * Note: The default set is required for PSR-6 compliance. + * + * NOTE: The default set is required for PSR-6 compliance. */ public string $reservedCharacters = '{}()/\@:'; @@ -157,6 +159,7 @@ class Cache extends BaseConfig * that are listed here are allowed to be used. * * @var array + * @phpstan-var array> */ public array $validHandlers = [ 'dummy' => DummyHandler::class, diff --git a/app/Config/ContentSecurityPolicy.php b/app/Config/ContentSecurityPolicy.php index 18612e15..7799c476 100644 --- a/app/Config/ContentSecurityPolicy.php +++ b/app/Config/ContentSecurityPolicy.php @@ -39,7 +39,7 @@ class ContentSecurityPolicy extends BaseConfig // ------------------------------------------------------------------------- // Sources allowed - // Note: once you set a policy to 'none', it cannot be further restricted + // NOTE: once you set a policy to 'none', it cannot be further restricted // ------------------------------------------------------------------------- /** diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 7b70c4fb..f751b8c2 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -49,7 +49,7 @@ class Filters extends BaseConfig * * If you use this, you should disable auto-routing because auto-routing * permits any HTTP method to access a controller. Accessing the controller - * with a method you don’t expect could bypass the filter. + * with a method you don't expect could bypass the filter. */ public array $methods = []; diff --git a/app/Config/Kint.php b/app/Config/Kint.php index 7e0a0157..cc8b5459 100644 --- a/app/Config/Kint.php +++ b/app/Config/Kint.php @@ -3,7 +3,10 @@ namespace Config; use CodeIgniter\Config\BaseConfig; +use Kint\Parser\ConstructablePluginInterface; use Kint\Renderer\AbstractRenderer; +use Kint\Renderer\Rich\TabPluginInterface; +use Kint\Renderer\Rich\ValuePluginInterface; /** * -------------------------------------------------------------------------- @@ -23,7 +26,12 @@ class Kint extends BaseConfig |-------------------------------------------------------------------------- */ + /** + * @var array + * @phpstan-var list|ConstructablePluginInterface> + */ public $plugins; + public int $maxDepth = 6; public bool $displayCalledFrom = true; public bool $expanded = false; @@ -36,7 +44,17 @@ class Kint extends BaseConfig public string $richTheme = 'aante-light.css'; public bool $richFolder = false; public int $richSort = AbstractRenderer::SORT_FULL; + + /** + * @var array + * @phpstan-var array> + */ public $richObjectPlugins; + + /** + * @var array + * @phpstan-var array> + */ public $richTabPlugins; /* diff --git a/app/Config/Logger.php b/app/Config/Logger.php index 7c7414d4..568c5da6 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -74,14 +74,12 @@ class Logger extends BaseConfig * the handler on top and continuing down. */ public array $handlers = [ - /* * -------------------------------------------------------------------- * File Handler * -------------------------------------------------------------------- */ FileHandler::class => [ - // The log levels that this handler will handle. 'handles' => [ 'critical', @@ -99,7 +97,7 @@ class Logger extends BaseConfig * An extension of 'php' allows for protecting the log files via basic * scripting, when they are to be stored under a publicly accessible directory. * - * Note: Leaving it blank will default to 'log'. + * NOTE: Leaving it blank will default to 'log'. */ 'fileExtension' => '', diff --git a/app/Config/Migrations.php b/app/Config/Migrations.php index af28e8ef..3c825667 100644 --- a/app/Config/Migrations.php +++ b/app/Config/Migrations.php @@ -40,7 +40,7 @@ class Migrations extends BaseConfig * using the CLI command: * > php spark make:migration * - * Note: if you set an unsupported format, migration runner will not find + * NOTE: if you set an unsupported format, migration runner will not find * your migration files. * * Supported formats: diff --git a/app/Config/Modules.php b/app/Config/Modules.php index 5b6a639b..54079e77 100644 --- a/app/Config/Modules.php +++ b/app/Config/Modules.php @@ -4,6 +4,12 @@ use CodeIgniter\Modules\Modules as BaseModules; +/** + * Modules Configuration. + * + * NOTE: This class is required prior to Autoloader instantiation, + * and does not extend BaseConfig. + */ class Modules extends BaseModules { /** diff --git a/app/Config/Paths.php b/app/Config/Paths.php index e419477a..262c745f 100644 --- a/app/Config/Paths.php +++ b/app/Config/Paths.php @@ -12,6 +12,11 @@ * share a system folder between multiple applications, and more. * * All paths are relative to the project's root folder. + * + * NOTE: This class is required prior to Autoloader instantiation, + * and does not extend BaseConfig. + * + * @immutable */ class Paths { diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index ecea1620..fb44007e 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -44,7 +44,7 @@ abstract class BaseController extends Controller // protected $session; /** - * Constructor. + * @return void */ public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) { diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php index 7f867e95..59343333 100644 --- a/app/Controllers/Home.php +++ b/app/Controllers/Home.php @@ -4,7 +4,7 @@ class Home extends BaseController { - public function index() + public function index(): string { return view('welcome_message'); } diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index f838451c..65be3d7d 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -148,6 +148,8 @@ private function loadComposerInfo(Modules $modules): void /** * Register the loader with the SPL autoloader stack. + * + * @return void */ public function register() { @@ -445,6 +447,8 @@ private function loadComposerClassmap(ClassLoader $composer): void * Locates autoload information from Composer, if available. * * @deprecated No longer used. + * + * @return void */ protected function discoverComposerNamespaces() { diff --git a/system/BaseModel.php b/system/BaseModel.php index 26b69522..0f316e6f 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -1345,7 +1345,7 @@ public function skipValidation(bool $skip = true) } /** - * Allows to set validation messages. + * Allows to set (and reset) validation messages. * It could be used when you have to change default or override current validate messages. * * @param array $validationMessages Value @@ -1376,7 +1376,7 @@ public function setValidationMessage(string $field, array $fieldMessages) } /** - * Allows to set validation rules. + * Allows to set (and reset) validation rules. * It could be used when you have to change default or override current validate rules. * * @param array $validationRules Value @@ -1401,6 +1401,17 @@ public function setValidationRules(array $validationRules) */ public function setValidationRule(string $field, $fieldRules) { + $rules = $this->validationRules; + + // ValidationRules can be either a string, which is the group name, + // or an array of rules. + if (is_string($rules)) { + [$rules, $customErrors] = $this->validation->loadRuleGroup($rules); + + $this->validationRules = $rules; + $this->validationMessages = $this->validationMessages + $customErrors; + } + $this->validationRules[$field] = $fieldRules; return $this; @@ -1466,7 +1477,9 @@ public function getValidationRules(array $options = []): array // ValidationRules can be either a string, which is the group name, // or an array of rules. if (is_string($rules)) { - $rules = $this->validation->loadRuleGroup($rules); + [$rules, $customErrors] = $this->validation->loadRuleGroup($rules); + + $this->validationMessages = $this->validationMessages + $customErrors; } if (isset($options['except'])) { @@ -1711,7 +1724,7 @@ protected function transformDataToArray($data, string $type): array * * @param string $name Name * - * @return mixed + * @return array|bool|float|int|object|string|null */ public function __get(string $name) { diff --git a/system/CLI/BaseCommand.php b/system/CLI/BaseCommand.php index 0618ceb2..0c4abd2c 100644 --- a/system/CLI/BaseCommand.php +++ b/system/CLI/BaseCommand.php @@ -11,6 +11,7 @@ namespace CodeIgniter\CLI; +use Config\Exceptions; use Psr\Log\LoggerInterface; use ReflectionException; use Throwable; @@ -121,7 +122,7 @@ protected function showError(Throwable $e) { $exception = $e; $message = $e->getMessage(); - $config = config('Exceptions'); + $config = config(Exceptions::class); require $config->errorViewPath . '/cli/error_exception.php'; } diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 80d001a3..2798319b 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -520,7 +520,7 @@ public static function wait(int $seconds, bool $countdown = false) /** * if operating system === windows * - * @deprecated v4.3 Use `is_windows()` instead + * @deprecated 4.3.0 Use `is_windows()` instead */ public static function isWindows(): bool { diff --git a/system/CLI/GeneratorTrait.php b/system/CLI/GeneratorTrait.php index 21ae0c16..7b606638 100644 --- a/system/CLI/GeneratorTrait.php +++ b/system/CLI/GeneratorTrait.php @@ -11,6 +11,7 @@ namespace CodeIgniter\CLI; +use Config\Generators; use Config\Services; use Throwable; @@ -267,7 +268,7 @@ protected function qualifyClassName(): string protected function renderTemplate(array $data = []): string { try { - return view(config('Generators')->views[$this->name], $data, ['debug' => false]); + return view(config(Generators::class)->views[$this->name], $data, ['debug' => false]); } catch (Throwable $e) { log_message('error', (string) $e); diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index 0c5ef170..9f700370 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -13,6 +13,7 @@ use Closure; use CodeIgniter\Cache\CacheInterface; +use Config\Cache; use Exception; use InvalidArgumentException; @@ -61,7 +62,7 @@ public static function validateKey($key, $prefix = ''): string throw new InvalidArgumentException('Cache key cannot be empty.'); } - $reserved = config('Cache')->reservedCharacters ?? self::RESERVED_CHARACTERS; + $reserved = config(Cache::class)->reservedCharacters ?? self::RESERVED_CHARACTERS; if ($reserved && strpbrk($key, $reserved) !== false) { throw new InvalidArgumentException('Cache key contains reserved characters ' . $reserved); } diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index c217c7ed..524ee8e4 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -44,6 +44,8 @@ class FileHandler extends BaseHandler protected $mode; /** + * Note: Use `CacheFactory::getHandler()` to instantiate. + * * @throws CacheException */ public function __construct(Cache $config) @@ -144,21 +146,22 @@ public function deleteMatching(string $pattern) */ public function increment(string $key, int $offset = 1) { - $key = static::validateKey($key, $this->prefix); - $data = $this->getItem($key); + $key = static::validateKey($key, $this->prefix); + $tmp = $this->getItem($key); - if ($data === false) { - $data = [ - 'data' => 0, - 'ttl' => 60, - ]; - } elseif (! is_int($data['data'])) { + if ($tmp === false) { + $tmp = ['data' => 0, 'ttl' => 60]; + } + + ['data' => $value, 'ttl' => $ttl] = $tmp; + + if (! is_int($value)) { return false; } - $newValue = $data['data'] + $offset; + $value += $offset; - return $this->save($key, $newValue, $data['ttl']) ? $newValue : false; + return $this->save($key, $value, $ttl) ? $value : false; } /** @@ -166,21 +169,7 @@ public function increment(string $key, int $offset = 1) */ public function decrement(string $key, int $offset = 1) { - $key = static::validateKey($key, $this->prefix); - $data = $this->getItem($key); - - if ($data === false) { - $data = [ - 'data' => 0, - 'ttl' => 60, - ]; - } elseif (! is_int($data['data'])) { - return false; - } - - $newValue = $data['data'] - $offset; - - return $this->save($key, $newValue, $data['ttl']) ? $newValue : false; + return $this->increment($key, -$offset); } /** @@ -229,7 +218,8 @@ public function isSupported(): bool * Does the heavy lifting of actually retrieving the file and * verifying it's age. * - * @return array|bool|float|int|object|string|null + * @return array|false + * @phpstan-return array{data: mixed, ttl: int, time: int}|false */ protected function getItem(string $filename) { @@ -238,15 +228,21 @@ protected function getItem(string $filename) } $data = @unserialize(file_get_contents($this->path . $filename)); - if (! is_array($data) || ! isset($data['ttl'])) { + + if (! is_array($data)) { + return false; + } + + if (! isset($data['ttl']) || ! is_int($data['ttl'])) { + return false; + } + + if (! isset($data['time']) || ! is_int($data['time'])) { return false; } if ($data['ttl'] > 0 && Time::now()->getTimestamp() > $data['time'] + $data['ttl']) { - // If the file is still there then try to remove it - if (is_file($this->path . $filename)) { - @unlink($this->path . $filename); - } + @unlink($this->path . $filename); return false; } diff --git a/system/Cache/Handlers/MemcachedHandler.php b/system/Cache/Handlers/MemcachedHandler.php index 5053b1a6..f38a5411 100644 --- a/system/Cache/Handlers/MemcachedHandler.php +++ b/system/Cache/Handlers/MemcachedHandler.php @@ -42,6 +42,9 @@ class MemcachedHandler extends BaseHandler 'raw' => false, ]; + /** + * Note: Use `CacheFactory::getHandler()` to instantiate. + */ public function __construct(Cache $config) { $this->prefix = $config->prefix; diff --git a/system/Cache/Handlers/PredisHandler.php b/system/Cache/Handlers/PredisHandler.php index cf44f634..ab4fb1ff 100644 --- a/system/Cache/Handlers/PredisHandler.php +++ b/system/Cache/Handlers/PredisHandler.php @@ -43,6 +43,9 @@ class PredisHandler extends BaseHandler */ protected $redis; + /** + * Note: Use `CacheFactory::getHandler()` to instantiate. + */ public function __construct(Cache $config) { $this->prefix = $config->prefix; diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index 6874a86c..adb9a581 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -38,10 +38,13 @@ class RedisHandler extends BaseHandler /** * Redis connection * - * @var Redis + * @var Redis|null */ protected $redis; + /** + * Note: Use `CacheFactory::getHandler()` to instantiate. + */ public function __construct(Cache $config) { $this->prefix = $config->prefix; diff --git a/system/Cache/Handlers/WincacheHandler.php b/system/Cache/Handlers/WincacheHandler.php index e422dc1f..c5f0f084 100644 --- a/system/Cache/Handlers/WincacheHandler.php +++ b/system/Cache/Handlers/WincacheHandler.php @@ -22,6 +22,9 @@ */ class WincacheHandler extends BaseHandler { + /** + * Note: Use `CacheFactory::getHandler()` to instantiate. + */ public function __construct(Cache $config) { $this->prefix = $config->prefix; diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 829b65ea..af3a06f1 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -28,6 +28,7 @@ use CodeIgniter\Router\Router; use Config\App; use Config\Cache; +use Config\Feature; use Config\Kint as KintConfig; use Config\Services; use Exception; @@ -47,7 +48,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.3.6'; + public const CI_VERSION = '4.3.7'; /** * App startup time. @@ -268,7 +269,6 @@ private function autoloadKint(): void private function configureKint(): void { - /** @var \Config\Kint $config */ $config = config(KintConfig::class); Kint::$depth_limit = $config->maxDepth; @@ -455,7 +455,7 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache // If any filters were specified within the routes file, // we need to ensure it's active for the current request if ($routeFilter !== null) { - $multipleFiltersEnabled = config('Feature')->multipleFilters ?? false; + $multipleFiltersEnabled = config(Feature::class)->multipleFilters ?? false; if ($multipleFiltersEnabled) { $filters->enableFilters($routeFilter, 'before'); $filters->enableFilters($routeFilter, 'after'); @@ -732,7 +732,7 @@ public static function cache(int $time) * Caches the full response from the current request. Used for * full-page caching for very high performance. * - * @return mixed + * @return bool */ public function cachePage(Cache $config) { @@ -822,7 +822,7 @@ protected function tryToRouteIt(?RouteCollectionInterface $routes = null) $this->benchmark->stop('routing'); // for backward compatibility - $multipleFiltersEnabled = config('Feature')->multipleFilters ?? false; + $multipleFiltersEnabled = config(Feature::class)->multipleFilters ?? false; if (! $multipleFiltersEnabled) { return $this->router->getFilter(); } @@ -918,7 +918,7 @@ protected function createController() * 2. PHP CLI: accessed by CLI via php public/index.php, arguments become URI segments, * sent to Controllers via Routes, output varies * - * @param mixed $class + * @param Controller $class * * @return false|ResponseInterface|string|void */ diff --git a/system/Commands/Cache/ClearCache.php b/system/Commands/Cache/ClearCache.php index 648d99e4..b903425b 100644 --- a/system/Commands/Cache/ClearCache.php +++ b/system/Commands/Cache/ClearCache.php @@ -14,6 +14,7 @@ use CodeIgniter\Cache\CacheFactory; use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +use Config\Cache; /** * Clears current cache. @@ -62,7 +63,7 @@ class ClearCache extends BaseCommand */ public function run(array $params) { - $config = config('Cache'); + $config = config(Cache::class); $handler = $params[0] ?? $config->handler; if (! array_key_exists($handler, $config->validHandlers)) { diff --git a/system/Commands/Cache/InfoCache.php b/system/Commands/Cache/InfoCache.php index db5a51d7..1dc2504b 100644 --- a/system/Commands/Cache/InfoCache.php +++ b/system/Commands/Cache/InfoCache.php @@ -15,6 +15,7 @@ use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; use CodeIgniter\I18n\Time; +use Config\Cache; /** * Shows information on the cache. @@ -54,7 +55,7 @@ class InfoCache extends BaseCommand */ public function run(array $params) { - $config = config('Cache'); + $config = config(Cache::class); helper('number'); if ($config->handler !== 'file') { diff --git a/system/Commands/Database/CreateDatabase.php b/system/Commands/Database/CreateDatabase.php index a3922bd6..d103d97f 100644 --- a/system/Commands/Database/CreateDatabase.php +++ b/system/Commands/Database/CreateDatabase.php @@ -82,10 +82,7 @@ public function run(array $params) } try { - /** - * @var Database $config - */ - $config = config('Database'); + $config = config(Database::class); // Set to an empty database to prevent connection errors. $group = ENVIRONMENT === 'testing' ? 'tests' : $config->defaultGroup; diff --git a/system/Commands/Generators/MigrationGenerator.php b/system/Commands/Generators/MigrationGenerator.php index 817c1688..5ffd259e 100644 --- a/system/Commands/Generators/MigrationGenerator.php +++ b/system/Commands/Generators/MigrationGenerator.php @@ -15,6 +15,8 @@ use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\GeneratorTrait; use Config\App as AppConfig; +use Config\Database; +use Config\Migrations; use Config\Session as SessionConfig; /** @@ -107,12 +109,11 @@ protected function prepare(string $class): string $data['session'] = true; $data['table'] = is_string($table) ? $table : 'ci_sessions'; $data['DBGroup'] = is_string($DBGroup) ? $DBGroup : 'default'; - $data['DBDriver'] = config('Database')->{$data['DBGroup']}['DBDriver']; + $data['DBDriver'] = config(Database::class)->{$data['DBGroup']}['DBDriver']; - /** @var AppConfig $config */ - $config = config('App'); + $config = config(AppConfig::class); /** @var SessionConfig|null $session */ - $session = config('Session'); + $session = config(SessionConfig::class); $data['matchIP'] = ($session instanceof SessionConfig) ? $session->matchIP : $config->sessionMatchIP; @@ -126,6 +127,6 @@ protected function prepare(string $class): string */ protected function basename(string $filename): string { - return gmdate(config('Migrations')->timestampFormat) . basename($filename); + return gmdate(config(Migrations::class)->timestampFormat) . basename($filename); } } diff --git a/system/Commands/Generators/SessionMigrationGenerator.php b/system/Commands/Generators/SessionMigrationGenerator.php index fbfb2d3d..a04527e8 100644 --- a/system/Commands/Generators/SessionMigrationGenerator.php +++ b/system/Commands/Generators/SessionMigrationGenerator.php @@ -14,6 +14,8 @@ use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\GeneratorTrait; +use Config\App; +use Config\Migrations; /** * Generates a migration file for database sessions. @@ -93,7 +95,7 @@ protected function prepare(string $class): string $data['session'] = true; $data['table'] = $this->getOption('t'); $data['DBGroup'] = $this->getOption('g'); - $data['matchIP'] = config('App')->sessionMatchIP ?? false; + $data['matchIP'] = config(App::class)->sessionMatchIP ?? false; $data['table'] = is_string($data['table']) ? $data['table'] : 'ci_sessions'; $data['DBGroup'] = is_string($data['DBGroup']) ? $data['DBGroup'] : 'default'; @@ -106,6 +108,6 @@ protected function prepare(string $class): string */ protected function basename(string $filename): string { - return gmdate(config('Migrations')->timestampFormat) . basename($filename); + return gmdate(config(Migrations::class)->timestampFormat) . basename($filename); } } diff --git a/system/Commands/Utilities/Routes.php b/system/Commands/Utilities/Routes.php index bcd5825c..4ee18331 100644 --- a/system/Commands/Utilities/Routes.php +++ b/system/Commands/Utilities/Routes.php @@ -18,6 +18,7 @@ use CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\AutoRouteCollector as AutoRouteCollectorImproved; use CodeIgniter\Commands\Utilities\Routes\FilterCollector; use CodeIgniter\Commands\Utilities\Routes\SampleURIGenerator; +use Config\Feature; use Config\Services; /** @@ -124,7 +125,7 @@ public function run(array $params) } if ($collection->shouldAutoRoute()) { - $autoRoutesImproved = config('Feature')->autoRoutesImproved ?? false; + $autoRoutesImproved = config(Feature::class)->autoRoutesImproved ?? false; if ($autoRoutesImproved) { $autoRouteCollector = new AutoRouteCollectorImproved( diff --git a/system/Commands/Utilities/Routes/FilterCollector.php b/system/Commands/Utilities/Routes/FilterCollector.php index 63041674..71bbd1b0 100644 --- a/system/Commands/Utilities/Routes/FilterCollector.php +++ b/system/Commands/Utilities/Routes/FilterCollector.php @@ -15,6 +15,7 @@ use CodeIgniter\Filters\Filters; use CodeIgniter\HTTP\Request; use CodeIgniter\Router\Router; +use Config\Filters as FiltersConfig; /** * Collects filters for a route. @@ -72,7 +73,7 @@ private function createRouter(Request $request): Router private function createFilters(Request $request): Filters { - $config = config('Filters'); + $config = config(FiltersConfig::class); return new Filters($config, $request, Services::response()); } diff --git a/system/Commands/Utilities/Routes/FilterFinder.php b/system/Commands/Utilities/Routes/FilterFinder.php index 0f25eebf..042b3a26 100644 --- a/system/Commands/Utilities/Routes/FilterFinder.php +++ b/system/Commands/Utilities/Routes/FilterFinder.php @@ -15,6 +15,7 @@ use CodeIgniter\Filters\Filters; use CodeIgniter\Router\Exceptions\RedirectException; use CodeIgniter\Router\Router; +use Config\Feature; use Config\Services; /** @@ -35,7 +36,7 @@ private function getRouteFilters(string $uri): array { $this->router->handle($uri); - $multipleFiltersEnabled = config('Feature')->multipleFilters ?? false; + $multipleFiltersEnabled = config(Feature::class)->multipleFilters ?? false; if (! $multipleFiltersEnabled) { $filter = $this->router->getFilter(); diff --git a/system/Common.php b/system/Common.php index 8d366973..9be45b3a 100644 --- a/system/Common.php +++ b/system/Common.php @@ -65,8 +65,8 @@ function app_timezone(): string * cache()->save('foo', 'bar'); * $foo = cache('bar'); * - * @return CacheInterface|mixed - * @phpstan-return ($key is null ? CacheInterface : mixed) + * @return array|bool|CacheInterface|float|int|object|string|null + * @phpstan-return ($key is null ? CacheInterface : array|bool|float|int|object|string|null) */ function cache(?string $key = null) { @@ -1003,11 +1003,9 @@ function session(?string $val = null) * - $timer = service('timer') * - $timer = \CodeIgniter\Config\Services::timer(); * - * @param mixed ...$params - * - * @return object + * @param array|bool|float|int|object|string|null ...$params */ - function service(string $name, ...$params) + function service(string $name, ...$params): ?object { return Services::$name(...$params); } @@ -1017,11 +1015,9 @@ function service(string $name, ...$params) /** * Always returns a new instance of the class. * - * @param mixed ...$params - * - * @return object|null + * @param array|bool|float|int|object|string|null ...$params */ - function single_service(string $name, ...$params) + function single_service(string $name, ...$params): ?object { $service = Services::serviceExists($name); diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index 300e9ffa..80ae2686 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -59,7 +59,7 @@ class BaseConfig */ public function __construct() { - static::$moduleConfig = config('Modules'); + static::$moduleConfig = config(Modules::class); $this->registerProperties(); diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index 6b72b6ff..d517691b 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -318,7 +318,7 @@ public static function injectMock(string $name, $mock) protected static function discoverServices(string $name, array $arguments) { if (! static::$discovered) { - $config = config('Modules'); + $config = config(Modules::class); if ($config->shouldDiscover('services')) { $locator = static::locator(); @@ -360,7 +360,7 @@ protected static function discoverServices(string $name, array $arguments) protected static function buildServicesCache(): void { if (! static::$discovered) { - $config = config('Modules'); + $config = config(Modules::class); if ($config->shouldDiscover('services')) { $locator = static::locator(); diff --git a/system/Config/Factories.php b/system/Config/Factories.php index d752f55c..222df063 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -230,7 +230,7 @@ public static function getOptions(string $component): array // Handle Config as a special case to prevent logic loops ? self::$configOptions // Load values from the best Factory configuration (will include Registrars) - : config('Factory')->{$component} ?? []; + : config(Factory::class)->{$component} ?? []; return self::setOptions($component, $values); } diff --git a/system/Config/Services.php b/system/Config/Services.php index a2d13c40..66d0532e 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -61,6 +61,7 @@ use CodeIgniter\View\View; use Config\App; use Config\Cache; +use Config\ContentSecurityPolicy as ContentSecurityPolicyConfig; use Config\ContentSecurityPolicy as CSPConfig; use Config\Database; use Config\Email as EmailConfig; @@ -70,8 +71,11 @@ use Config\Format as FormatConfig; use Config\Honeypot as HoneypotConfig; use Config\Images; +use Config\Logger as LoggerConfig; use Config\Migrations; +use Config\Modules; use Config\Pager as PagerConfig; +use Config\Paths; use Config\Services as AppServices; use Config\Session as SessionConfig; use Config\Toolbar as ToolbarConfig; @@ -129,7 +133,7 @@ public static function clirequest(?App $config = null, bool $getShared = true) return static::getSharedInstance('clirequest', $config); } - $config ??= config('App'); + $config ??= config(App::class); return new CLIRequest($config); } @@ -145,7 +149,7 @@ public static function codeigniter(?App $config = null, bool $getShared = true) return static::getSharedInstance('codeigniter', $config); } - $config ??= config('App'); + $config ??= config(App::class); return new CodeIgniter($config); } @@ -175,7 +179,7 @@ public static function csp(?CSPConfig $config = null, bool $getShared = true) return static::getSharedInstance('csp', $config); } - $config ??= config('ContentSecurityPolicy'); + $config ??= config(ContentSecurityPolicyConfig::class); return new ContentSecurityPolicy($config); } @@ -192,7 +196,7 @@ public static function curlrequest(array $options = [], ?ResponseInterface $resp return static::getSharedInstance('curlrequest', $options, $response, $config); } - $config ??= config('App'); + $config ??= config(App::class); $response ??= new Response($config); return new CURLRequest( @@ -217,7 +221,7 @@ public static function email($config = null, bool $getShared = true) } if (empty($config) || ! (is_array($config) || $config instanceof EmailConfig)) { - $config = config('Email'); + $config = config(EmailConfig::class); } return new Email($config); @@ -236,7 +240,7 @@ public static function encrypter(?EncryptionConfig $config = null, $getShared = return static::getSharedInstance('encrypter', $config); } - $config ??= config('Encryption'); + $config ??= config(EncryptionConfig::class); $encryption = new Encryption($config); return $encryption->initialize($config); @@ -261,7 +265,7 @@ public static function exceptions( return static::getSharedInstance('exceptions', $config, $request, $response); } - $config ??= config('Exceptions'); + $config ??= config(ExceptionsConfig::class); $request ??= AppServices::request(); $response ??= AppServices::response(); @@ -282,7 +286,7 @@ public static function filters(?FiltersConfig $config = null, bool $getShared = return static::getSharedInstance('filters', $config); } - $config ??= config('Filters'); + $config ??= config(FiltersConfig::class); return new Filters($config, AppServices::request(), AppServices::response()); } @@ -298,7 +302,7 @@ public static function format(?FormatConfig $config = null, bool $getShared = tr return static::getSharedInstance('format', $config); } - $config ??= config('Format'); + $config ??= config(FormatConfig::class); return new Format($config); } @@ -315,7 +319,7 @@ public static function honeypot(?HoneypotConfig $config = null, bool $getShared return static::getSharedInstance('honeypot', $config); } - $config ??= config('Honeypot'); + $config ??= config(HoneypotConfig::class); return new Honeypot($config); } @@ -332,7 +336,7 @@ public static function image(?string $handler = null, ?Images $config = null, bo return static::getSharedInstance('image', $handler, $config); } - $config ??= config('Images'); + $config ??= config(Images::class); assert($config instanceof Images); $handler = $handler ?: $config->defaultHandler; @@ -392,7 +396,7 @@ public static function logger(bool $getShared = true) return static::getSharedInstance('logger'); } - return new Logger(config('Logger')); + return new Logger(config(LoggerConfig::class)); } /** @@ -406,7 +410,7 @@ public static function migrations(?Migrations $config = null, ?ConnectionInterfa return static::getSharedInstance('migrations', $config, $db); } - $config ??= config('Migrations'); + $config ??= config(Migrations::class); return new MigrationRunner($config, $db); } @@ -440,7 +444,7 @@ public static function pager(?PagerConfig $config = null, ?RendererInterface $vi return static::getSharedInstance('pager', $config, $view); } - $config ??= config('Pager'); + $config ??= config(PagerConfig::class); $view ??= AppServices::renderer(); return new Pager($config, $view); @@ -457,8 +461,8 @@ public static function parser(?string $viewPath = null, ?ViewConfig $config = nu return static::getSharedInstance('parser', $viewPath, $config); } - $viewPath = $viewPath ?: config('Paths')->viewDirectory; - $config ??= config('View'); + $viewPath = $viewPath ?: (new Paths())->viewDirectory; + $config ??= config(ViewConfig::class); return new Parser($config, $viewPath, AppServices::locator(), CI_DEBUG, AppServices::logger()); } @@ -476,8 +480,8 @@ public static function renderer(?string $viewPath = null, ?ViewConfig $config = return static::getSharedInstance('renderer', $viewPath, $config); } - $viewPath = $viewPath ?: config('Paths')->viewDirectory; - $config ??= config('View'); + $viewPath = $viewPath ?: (new Paths())->viewDirectory; + $config ??= config(ViewConfig::class); return new View($config, $viewPath, AppServices::locator(), CI_DEBUG, AppServices::logger()); } @@ -536,7 +540,7 @@ public static function incomingrequest(?App $config = null, bool $getShared = tr return static::getSharedInstance('request', $config); } - $config ??= config('App'); + $config ??= config(App::class); return new IncomingRequest( $config, @@ -557,7 +561,7 @@ public static function response(?App $config = null, bool $getShared = true) return static::getSharedInstance('response', $config); } - $config ??= config('App'); + $config ??= config(App::class); return new Response($config); } @@ -573,7 +577,7 @@ public static function redirectresponse(?App $config = null, bool $getShared = t return static::getSharedInstance('redirectresponse', $config); } - $config ??= config('App'); + $config ??= config(App::class); $response = new RedirectResponse($config); $response->setProtocolVersion(AppServices::request()->getProtocolVersion()); @@ -592,7 +596,7 @@ public static function routes(bool $getShared = true) return static::getSharedInstance('routes'); } - return new RouteCollection(AppServices::locator(), config('Modules')); + return new RouteCollection(AppServices::locator(), config(Modules::class)); } /** @@ -625,7 +629,7 @@ public static function security(?App $config = null, bool $getShared = true) return static::getSharedInstance('security', $config); } - $config ??= config('App'); + $config ??= config(App::class); return new Security($config); } @@ -641,13 +645,13 @@ public static function session(?App $config = null, bool $getShared = true) return static::getSharedInstance('session', $config); } - $config ??= config('App'); + $config ??= config(App::class); assert($config instanceof App); $logger = AppServices::logger(); /** @var SessionConfig|null $sessionConfig */ - $sessionConfig = config('Session'); + $sessionConfig = config(SessionConfig::class); $driverName = $sessionConfig->driver ?? $config->sessionDriver; @@ -718,7 +722,7 @@ public static function toolbar(?ToolbarConfig $config = null, bool $getShared = return static::getSharedInstance('toolbar', $config); } - $config ??= config('Toolbar'); + $config ??= config(ToolbarConfig::class); return new Toolbar($config); } @@ -750,7 +754,7 @@ public static function validation(?ValidationConfig $config = null, bool $getSha return static::getSharedInstance('validation', $config); } - $config ??= config('Validation'); + $config ??= config(ValidationConfig::class); return new Validation($config, AppServices::renderer()); } diff --git a/system/Controller.php b/system/Controller.php index 870f15b4..64de91ab 100644 --- a/system/Controller.php +++ b/system/Controller.php @@ -17,6 +17,7 @@ use CodeIgniter\Validation\Exceptions\ValidationException; use CodeIgniter\Validation\ValidationInterface; use Config\Services; +use Config\Validation; use Psr\Log\LoggerInterface; /** @@ -164,7 +165,7 @@ private function setValidator($rules, array $messages): void // If you replace the $rules array with the name of the group if (is_string($rules)) { - $validation = config('Validation'); + $validation = config(Validation::class); // If the rule wasn't found in the \Config\Validation, we // should throw an exception so the developer can find it. diff --git a/system/Database/Config.php b/system/Database/Config.php index 802a4da2..f34d0395 100644 --- a/system/Database/Config.php +++ b/system/Database/Config.php @@ -57,8 +57,7 @@ public static function connect($group = null, bool $getShared = true) $config = $group; $group = 'custom-' . md5(json_encode($config)); } else { - /** @var DbConfig $dbConfig */ - $dbConfig = config('Database'); + $dbConfig = config(DbConfig::class); if ($group === null) { $group = (ENVIRONMENT === 'testing') ? 'tests' : $dbConfig->defaultGroup; @@ -132,7 +131,7 @@ public static function utils($group = null) */ public static function seeder(?string $group = null) { - $config = config('Database'); + $config = config(DbConfig::class); return new Seeder($config, static::connect($group)); } diff --git a/system/Database/Migration.php b/system/Database/Migration.php index 6b1cf82d..0f1ab2c7 100644 --- a/system/Database/Migration.php +++ b/system/Database/Migration.php @@ -46,7 +46,7 @@ abstract class Migration */ public function __construct(?Forge $forge = null) { - $this->forge = $forge ?? Database::forge($this->DBGroup ?? config('Database')->defaultGroup); + $this->forge = $forge ?? Database::forge($this->DBGroup ?? config(Database::class)->defaultGroup); $this->db = $this->forge->getConnection(); } diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index 569be228..eda6bb58 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -142,7 +142,7 @@ public function __construct(MigrationsConfig $config, $db = null) $this->namespace = APP_NAMESPACE; // get default database group - $config = config('Database'); + $config = config(Database::class); $this->group = $config->defaultGroup; unset($config); @@ -844,7 +844,7 @@ protected function migrate($direction, $migration): bool } $instance = new $class(); - $group = $instance->getDBGroup() ?? config('Database')->defaultGroup; + $group = $instance->getDBGroup() ?? config(Database::class)->defaultGroup; if (ENVIRONMENT !== 'testing' && $group === 'tests' && $this->groupFilter !== 'tests') { // @codeCoverageIgnoreStart diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index ce6554dd..dd54bf9d 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -98,7 +98,7 @@ class Connection extends BaseConnection public $lastInsertedTableName; /** - * confirm DNS format. + * confirm DSN format. */ private function isValidDSN(): bool { diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 31b20584..1eddd4a7 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -187,7 +187,7 @@ protected function _attributeAutoIncrement(array &$attributes, array &$field) protected function _processColumn(array $field): string { $constraint = ''; - // @todo: can’t cover multi pattern when set type. + // @todo: can't cover multi pattern when set type. if ($field['type'] === 'VARCHAR2' && strpos($field['length'], "('") === 0) { $constraint = ' CHECK(' . $this->db->escapeIdentifiers($field['name']) . ' IN ' . $field['length'] . ')'; diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index 531e95fd..6df81c16 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -293,7 +293,7 @@ protected function collectVars(Throwable $exception, int $statusCode): array $trace = $exception->getTrace(); if ($this->config->sensitiveDataInTrace !== []) { - $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace); + $trace = $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace); } return [ @@ -311,31 +311,52 @@ protected function collectVars(Throwable $exception, int $statusCode): array * Mask sensitive data in the trace. * * @param array|object $trace + * + * @return array|object */ - protected function maskSensitiveData(&$trace, array $keysToMask, string $path = '') + protected function maskSensitiveData($trace, array $keysToMask, string $path = '') + { + foreach ($trace as $i => $line) { + $trace[$i]['args'] = $this->maskData($line['args'], $keysToMask); + } + + return $trace; + } + + /** + * @param array|object $args + * + * @return array|object + */ + private function maskData($args, array $keysToMask, string $path = '') { foreach ($keysToMask as $keyToMask) { $explode = explode('/', $keyToMask); $index = end($explode); if (strpos(strrev($path . '/' . $index), strrev($keyToMask)) === 0) { - if (is_array($trace) && array_key_exists($index, $trace)) { - $trace[$index] = '******************'; - } elseif (is_object($trace) && property_exists($trace, $index) && isset($trace->{$index})) { - $trace->{$index} = '******************'; + if (is_array($args) && array_key_exists($index, $args)) { + $args[$index] = '******************'; + } elseif ( + is_object($args) && property_exists($args, $index) + && isset($args->{$index}) && is_scalar($args->{$index}) + ) { + $args->{$index} = '******************'; } } } - if (is_object($trace)) { - $trace = get_object_vars($trace); - } - - if (is_array($trace)) { - foreach ($trace as $pathKey => $subarray) { - $this->maskSensitiveData($subarray, $keysToMask, $path . '/' . $pathKey); + if (is_array($args)) { + foreach ($args as $pathKey => $subarray) { + $args[$pathKey] = $this->maskData($subarray, $keysToMask, $path . '/' . $pathKey); + } + } elseif (is_object($args)) { + foreach ($args as $pathKey => $subarray) { + $args->{$pathKey} = $this->maskData($subarray, $keysToMask, $path . '/' . $pathKey); } } + + return $args; } /** diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 17e67aa8..f00ad09b 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -365,7 +365,7 @@ public function prepare(?RequestInterface $request = null, ?ResponseInterface $r return; } - $toolbar = Services::toolbar(config(self::class)); + $toolbar = Services::toolbar(config(ToolbarConfig::class)); $stats = $app->getPerformanceStats(); $data = $toolbar->run( $stats['startTime'], diff --git a/system/Debug/Toolbar/Collectors/Config.php b/system/Debug/Toolbar/Collectors/Config.php index b4600555..faf7a19b 100644 --- a/system/Debug/Toolbar/Collectors/Config.php +++ b/system/Debug/Toolbar/Collectors/Config.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; use CodeIgniter\CodeIgniter; +use Config\App; use Config\Services; /** @@ -24,7 +25,7 @@ class Config */ public static function display(): array { - $config = config('App'); + $config = config(App::class); return [ 'ciVersion' => CodeIgniter::CI_VERSION, diff --git a/system/Debug/Toolbar/Collectors/Database.php b/system/Debug/Toolbar/Collectors/Database.php index c7ed6703..9d4099f4 100644 --- a/system/Debug/Toolbar/Collectors/Database.php +++ b/system/Debug/Toolbar/Collectors/Database.php @@ -13,6 +13,7 @@ use CodeIgniter\Database\Query; use CodeIgniter\I18n\Time; +use Config\Toolbar; /** * Collector for the Database tab of the Debug Toolbar. @@ -78,7 +79,7 @@ public function __construct() */ public static function collect(Query $query) { - $config = config('Toolbar'); + $config = config(Toolbar::class); // Provide default in case it's not set $max = $config->maxQueries ?: 100; diff --git a/system/Encryption/Handlers/BaseHandler.php b/system/Encryption/Handlers/BaseHandler.php index 64195672..08c0234a 100644 --- a/system/Encryption/Handlers/BaseHandler.php +++ b/system/Encryption/Handlers/BaseHandler.php @@ -13,26 +13,18 @@ use CodeIgniter\Encryption\EncrypterInterface; use Config\Encryption; -use Psr\Log\LoggerInterface; /** * Base class for encryption handling */ abstract class BaseHandler implements EncrypterInterface { - /** - * Logger instance to record error messages and warnings. - * - * @var LoggerInterface - */ - protected $logger; - /** * Constructor */ public function __construct(?Encryption $config = null) { - $config ??= config('Encryption'); + $config ??= config(Encryption::class); // make the parameters conveniently accessible foreach (get_object_vars($config) as $key => $value) { diff --git a/system/Events/Events.php b/system/Events/Events.php index 064f01ab..f90af36c 100644 --- a/system/Events/Events.php +++ b/system/Events/Events.php @@ -71,8 +71,7 @@ public static function initialize() return; } - /** @var Modules $config */ - $config = config('Modules'); + $config = config(Modules::class); $events = APPPATH . 'Config' . DIRECTORY_SEPARATOR . 'Events.php'; $files = []; diff --git a/system/Filters/FilterInterface.php b/system/Filters/FilterInterface.php index 8055d11e..1a3363c9 100644 --- a/system/Filters/FilterInterface.php +++ b/system/Filters/FilterInterface.php @@ -31,7 +31,7 @@ interface FilterInterface * * @param array|null $arguments * - * @return mixed + * @return RequestInterface|ResponseInterface|string|void */ public function before(RequestInterface $request, $arguments = null); @@ -43,7 +43,7 @@ public function before(RequestInterface $request, $arguments = null); * * @param array|null $arguments * - * @return mixed + * @return ResponseInterface|void */ public function after(RequestInterface $request, ResponseInterface $response, $arguments = null); } diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index c022d7d7..784c45c4 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -106,7 +106,7 @@ public function __construct($config, RequestInterface $request, ResponseInterfac $this->request = &$request; $this->setResponse($response); - $this->modules = $modules ?? config('Modules'); + $this->modules = $modules ?? config(Modules::class); if ($this->modules->shouldDiscover('filters')) { $this->discoverFilters(); diff --git a/system/Format/FormatterInterface.php b/system/Format/FormatterInterface.php index 277299ee..6e8e9bd5 100644 --- a/system/Format/FormatterInterface.php +++ b/system/Format/FormatterInterface.php @@ -19,7 +19,7 @@ interface FormatterInterface /** * Takes the given data and formats it. * - * @param array|string $data + * @param array|object|string $data * * @return false|string */ diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index dd4733e9..31b0f36f 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -221,7 +221,7 @@ public function isCLI(): bool * * @param array|string|null $index Index for item to fetch from $_GET. * @param int|null $filter A filter name to apply. - * @param mixed|null $flags + * @param array|int|null $flags * * @return array|null */ @@ -235,7 +235,7 @@ public function getGet($index = null, $filter = null, $flags = null) * * @param array|string|null $index Index for item to fetch from $_POST. * @param int|null $filter A filter name to apply - * @param mixed $flags + * @param array|int|null $flags * * @return array|null */ @@ -249,7 +249,7 @@ public function getPost($index = null, $filter = null, $flags = null) * * @param array|string|null $index Index for item to fetch from $_POST or $_GET * @param int|null $filter A filter name to apply - * @param mixed $flags + * @param array|int|null $flags * * @return array|null */ @@ -263,7 +263,7 @@ public function getPostGet($index = null, $filter = null, $flags = null) * * @param array|string|null $index Index for item to be fetched from $_GET or $_POST * @param int|null $filter A filter name to apply - * @param mixed $flags + * @param array|int|null $flags * * @return array|null */ diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index 1f467a57..019b12e2 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -112,12 +112,12 @@ public function __construct(App $config, URI $uri, ?ResponseInterface $response parent::__construct('GET', $uri); - $this->responseOrig = $response ?? new Response(config('App')); + $this->responseOrig = $response ?? new Response(config(App::class)); $this->baseURI = $uri->useRawQueryString(); $this->defaultOptions = $options; /** @var ConfigCURLRequest|null $configCURLRequest */ - $configCURLRequest = config('CURLRequest'); + $configCURLRequest = config(ConfigCURLRequest::class); $this->shareOptions = $configCURLRequest->shareOptions ?? true; $this->config = $this->defaultConfig; @@ -263,7 +263,7 @@ public function setForm(array $params, bool $multipart = false) /** * Set JSON data to be sent. * - * @param mixed $data + * @param array|bool|float|int|object|string|null $data * * @return $this */ @@ -387,6 +387,10 @@ public function send(string $method, string $url) $output = substr($output, strpos($output, $breakString) + 4); } + if (strpos($output, 'HTTP/1.1 200 Connection established') === 0) { + $output = substr($output, strpos($output, $breakString) + 4); + } + // If request and response have Digest if (isset($this->config['auth'][2]) && $this->config['auth'][2] === 'digest' && strpos($output, 'WWW-Authenticate: Digest') !== false) { $output = substr($output, strpos($output, $breakString) + 4); diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 3cc9b03a..deb5093e 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -11,6 +11,7 @@ namespace CodeIgniter\HTTP; +use Config\App; use Config\ContentSecurityPolicy as ContentSecurityPolicyConfig; /** @@ -241,7 +242,7 @@ class ContentSecurityPolicy */ public function __construct(ContentSecurityPolicyConfig $config) { - $appConfig = config('App'); + $appConfig = config(App::class); $this->CSPEnabled = $appConfig->CSPEnabled; foreach (get_object_vars($config) as $setting => $value) { @@ -326,7 +327,7 @@ public function reportOnly(bool $value = true) /** * Adds a new base_uri value. Can be either a URI class or a simple string. * - * base_uri restricts the URLs that can appear in a page’s element. + * base_uri restricts the URLs that can appear in a page's element. * * @see http://www.w3.org/TR/CSP/#directive-base-uri * diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index d5824da1..d8fb8c03 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -13,6 +13,7 @@ use CodeIgniter\Exceptions\DownloadException; use CodeIgniter\Files\File; +use Config\App; use Config\Mimes; /** @@ -64,7 +65,7 @@ class DownloadResponse extends Response */ public function __construct(string $filename, bool $setMime) { - parent::__construct(config('App')); + parent::__construct(config(App::class)); $this->filename = $filename; $this->setMime = $setMime; diff --git a/system/HTTP/Files/FileCollection.php b/system/HTTP/Files/FileCollection.php index 87f31ee5..37c44a88 100644 --- a/system/HTTP/Files/FileCollection.php +++ b/system/HTTP/Files/FileCollection.php @@ -159,7 +159,7 @@ protected function populateFiles() * Given a file array, will create UploadedFile instances. Will * loop over an array and create objects for each. * - * @return array|UploadedFile + * @return UploadedFile|UploadedFile[] */ protected function createFileObject(array $array) { @@ -240,7 +240,7 @@ protected function fixFilesArray(array $data): array * @param array $index The index sequence we are navigating down * @param array $value The portion of the array to process * - * @return mixed + * @return UploadedFile|null */ protected function getValueDotNotationSyntax(array $index, array $value) { diff --git a/system/HTTP/MessageInterface.php b/system/HTTP/MessageInterface.php index ac4ccf41..99867bde 100644 --- a/system/HTTP/MessageInterface.php +++ b/system/HTTP/MessageInterface.php @@ -30,7 +30,7 @@ public function getProtocolVersion(): string; /** * Sets the body of the current message. * - * @param mixed $data + * @param string $data * * @return $this */ @@ -48,7 +48,7 @@ public function getBody(); /** * Appends data to the body of the current message. * - * @param mixed $data + * @param string $data * * @return $this */ diff --git a/system/HTTP/MessageTrait.php b/system/HTTP/MessageTrait.php index 7e2665d0..e565c8d9 100644 --- a/system/HTTP/MessageTrait.php +++ b/system/HTTP/MessageTrait.php @@ -59,7 +59,7 @@ public function setBody($data): self /** * Appends data to the body of the current message. * - * @param mixed $data + * @param string $data * * @return $this */ diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index 418da598..57f97982 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -13,6 +13,7 @@ use CodeIgniter\Exceptions\ConfigException; use CodeIgniter\Validation\FormatRules; +use Config\App; /** * Request Trait @@ -63,8 +64,13 @@ public function getIPAddress(): string * @deprecated $this->proxyIPs property will be removed in the future */ // @phpstan-ignore-next-line - $proxyIPs = $this->proxyIPs ?? config('App')->proxyIPs; + $proxyIPs = $this->proxyIPs ?? config(App::class)->proxyIPs; // @phpstan-ignore-next-line + + // Workaround for old Config\App file. App::$proxyIPs may be empty string. + if ($proxyIPs === '') { + $proxyIPs = []; + } if (! empty($proxyIPs) && (! is_array($proxyIPs) || is_int(array_key_first($proxyIPs)))) { throw new ConfigException( 'You must set an array with Proxy IP address key and HTTP header name value in Config\App::$proxyIPs.' @@ -78,76 +84,74 @@ public function getIPAddress(): string return $this->ipAddress = '0.0.0.0'; } - if ($proxyIPs) { - // @TODO Extract all this IP address logic to another class. - foreach ($proxyIPs as $proxyIP => $header) { - // Check if we have an IP address or a subnet - if (strpos($proxyIP, '/') === false) { - // An IP address (and not a subnet) is specified. - // We can compare right away. - if ($proxyIP === $this->ipAddress) { - $spoof = $this->getClientIP($header); - - if ($spoof !== null) { - $this->ipAddress = $spoof; - break; - } - } + // @TODO Extract all this IP address logic to another class. + foreach ($proxyIPs as $proxyIP => $header) { + // Check if we have an IP address or a subnet + if (strpos($proxyIP, '/') === false) { + // An IP address (and not a subnet) is specified. + // We can compare right away. + if ($proxyIP === $this->ipAddress) { + $spoof = $this->getClientIP($header); - continue; + if ($spoof !== null) { + $this->ipAddress = $spoof; + break; + } } - // We have a subnet ... now the heavy lifting begins - if (! isset($separator)) { - $separator = $ipValidator($this->ipAddress, 'ipv6') ? ':' : '.'; - } + continue; + } - // If the proxy entry doesn't match the IP protocol - skip it - if (strpos($proxyIP, $separator) === false) { - continue; - } + // We have a subnet ... now the heavy lifting begins + if (! isset($separator)) { + $separator = $ipValidator($this->ipAddress, 'ipv6') ? ':' : '.'; + } - // Convert the REMOTE_ADDR IP address to binary, if needed - if (! isset($ip, $sprintf)) { - if ($separator === ':') { - // Make sure we're having the "full" IPv6 format - $ip = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($this->ipAddress, ':')), $this->ipAddress)); + // If the proxy entry doesn't match the IP protocol - skip it + if (strpos($proxyIP, $separator) === false) { + continue; + } - for ($j = 0; $j < 8; $j++) { - $ip[$j] = intval($ip[$j], 16); - } + // Convert the REMOTE_ADDR IP address to binary, if needed + if (! isset($ip, $sprintf)) { + if ($separator === ':') { + // Make sure we're having the "full" IPv6 format + $ip = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($this->ipAddress, ':')), $this->ipAddress)); - $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; - } else { - $ip = explode('.', $this->ipAddress); - $sprintf = '%08b%08b%08b%08b'; + for ($j = 0; $j < 8; $j++) { + $ip[$j] = intval($ip[$j], 16); } - $ip = vsprintf($sprintf, $ip); + $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; + } else { + $ip = explode('.', $this->ipAddress); + $sprintf = '%08b%08b%08b%08b'; } - // Split the netmask length off the network address - sscanf($proxyIP, '%[^/]/%d', $netaddr, $masklen); + $ip = vsprintf($sprintf, $ip); + } - // Again, an IPv6 address is most likely in a compressed form - if ($separator === ':') { - $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); + // Split the netmask length off the network address + sscanf($proxyIP, '%[^/]/%d', $netaddr, $masklen); - for ($i = 0; $i < 8; $i++) { - $netaddr[$i] = intval($netaddr[$i], 16); - } - } else { - $netaddr = explode('.', $netaddr); + // Again, an IPv6 address is most likely in a compressed form + if ($separator === ':') { + $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); + + for ($i = 0; $i < 8; $i++) { + $netaddr[$i] = intval($netaddr[$i], 16); } + } else { + $netaddr = explode('.', $netaddr); + } - // Convert to binary and finally compare - if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) { - $spoof = $this->getClientIP($header); + // Convert to binary and finally compare + if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) { + $spoof = $this->getClientIP($header); - if ($spoof !== null) { - $this->ipAddress = $spoof; - break; - } + if ($spoof !== null) { + $this->ipAddress = $spoof; + break; } } } diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index 21db9acd..f573905d 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -16,6 +16,7 @@ use CodeIgniter\Cookie\Exceptions\CookieException; use CodeIgniter\HTTP\Exceptions\HTTPException; use Config\App; +use Config\Cookie as CookieConfig; use Config\Services; /** @@ -172,7 +173,7 @@ public function __construct($config) } $this->cookieStore = new CookieStore([]); - Cookie::setDefaults(config('Cookie') ?? [ + Cookie::setDefaults(config(CookieConfig::class) ?? [ // @todo Remove this fallback when deprecated `App` members are removed 'prefix' => $config->cookiePrefix, 'path' => $config->cookiePath, diff --git a/system/HTTP/ResponseTrait.php b/system/HTTP/ResponseTrait.php index 133d1b19..2ab026c9 100644 --- a/system/HTTP/ResponseTrait.php +++ b/system/HTTP/ResponseTrait.php @@ -239,7 +239,7 @@ public function setContentType(string $mime, string $charset = 'UTF-8') /** * Converts the $body into JSON and sets the Content Type header. * - * @param array|string $body + * @param array|object|string $body * * @return $this */ @@ -304,10 +304,10 @@ public function getXML() * Handles conversion of the data into the appropriate format, * and sets the correct Content-Type header for our response. * - * @param array|string $body - * @param string $format Valid: json, xml + * @param array|object|string $body + * @param string $format Valid: json, xml * - * @return mixed + * @return false|string * * @throws InvalidArgumentException If the body property is not string or array. */ @@ -582,7 +582,7 @@ public function setCookie( } /** @var CookieConfig|null $cookieConfig */ - $cookieConfig = config('Cookie'); + $cookieConfig = config(CookieConfig::class); if ($cookieConfig instanceof CookieConfig) { $secure ??= $cookieConfig->secure; diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 587e441f..b88712e1 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -13,6 +13,7 @@ use BadMethodCallException; use CodeIgniter\HTTP\Exceptions\HTTPException; +use Config\App; /** * Abstraction for a uniform resource identifier (URI). @@ -556,7 +557,7 @@ public function getSegment(int $number, string $default = ''): string * Set the value of a specific segment of the URI path. * Allows to set only existing segments or add new one. * - * @param mixed $value (string or int) + * @param int|string $value (string or int) * * @return $this */ @@ -622,7 +623,7 @@ public function __toString(): string private function changeSchemeAndPath(string $scheme, string $path): array { // Check if this is an internal URI - $config = config('App'); + $config = config(App::class); $baseUri = new self($config->baseURL); if ( @@ -843,7 +844,7 @@ public function setQueryArray(array $query) /** * Adds a single new element to the query vars. * - * @param mixed $value + * @param int|string $value * * @return $this */ diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 10088111..ff1c68c4 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -91,7 +91,7 @@ class UserAgent /** * HTTP Referer * - * @var mixed + * @var bool|string|null */ protected $referrer; diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index 9b88cc2a..c5c9d956 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -41,7 +41,7 @@ function dot_array_search(string $index, array $array) * * @internal This should not be used on its own. * - * @return mixed + * @return array|bool|float|int|object|string|null */ function _array_search_dot(array $indexes, array $array) { @@ -103,9 +103,9 @@ function _array_search_dot(array $indexes, array $array) /** * Returns the value of an element at a key in an array of uncertain depth. * - * @param mixed $key + * @param int|string $key * - * @return mixed|null + * @return array|bool|float|int|object|string|null */ function array_deep_search($key, array $array) { diff --git a/system/Helpers/cookie_helper.php b/system/Helpers/cookie_helper.php index 0a11ce84..9fc004c2 100755 --- a/system/Helpers/cookie_helper.php +++ b/system/Helpers/cookie_helper.php @@ -69,10 +69,10 @@ function get_cookie($index, bool $xssClean = false, ?string $prefix = '') { if ($prefix === '') { /** @var Cookie|null $cookie */ - $cookie = config('Cookie'); + $cookie = config(Cookie::class); // @TODO Remove Config\App fallback when deprecated `App` members are removed. - $prefix = $cookie instanceof Cookie ? $cookie->prefix : config('App')->cookiePrefix; + $prefix = $cookie instanceof Cookie ? $cookie->prefix : config(App::class)->cookiePrefix; } $request = Services::request(); @@ -86,7 +86,7 @@ function get_cookie($index, bool $xssClean = false, ?string $prefix = '') /** * Delete a cookie * - * @param mixed $name + * @param string $name * @param string $domain the cookie domain. Usually: .yourdomain.com * @param string $path the cookie path * @param string $prefix the cookie prefix diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 4918e87e..47c829bb 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -292,8 +292,8 @@ 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 mixed $returnedValues Array or comma separated string of information returned + * @param string $file Path to file + * @param array|string $returnedValues Array or comma separated string of information returned * * @return array|null */ diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index dfd4a64f..c553bdde 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -10,7 +10,9 @@ */ use CodeIgniter\Validation\Exceptions\ValidationException; +use Config\App; use Config\Services; +use Config\Validation; // CodeIgniter Form Helpers @@ -50,7 +52,7 @@ function form_open(string $action = '', $attributes = [], array $hidden = []): s $attributes .= ' method="post"'; } if (stripos($attributes, 'accept-charset=') === false) { - $config = config('App'); + $config = config(App::class); $attributes .= ' accept-charset="' . strtolower($config->charset) . '"'; } @@ -718,7 +720,7 @@ function validation_errors() */ function validation_list_errors(string $template = 'list'): string { - $config = config('Validation'); + $config = config(Validation::class); $view = Services::renderer(); if (! array_key_exists($template, $config->templates)) { @@ -738,7 +740,7 @@ function validation_list_errors(string $template = 'list'): string */ function validation_show_error(string $field, string $template = 'single'): string { - $config = config('Validation'); + $config = config(Validation::class); $view = Services::renderer(); $errors = array_filter(validation_errors(), static fn ($key) => preg_match( diff --git a/system/Helpers/number_helper.php b/system/Helpers/number_helper.php index c61229da..07d3be94 100644 --- a/system/Helpers/number_helper.php +++ b/system/Helpers/number_helper.php @@ -15,8 +15,8 @@ /** * Formats a numbers as bytes, based on size, and adds the appropriate suffix * - * @param mixed $num Will be cast as int - * @param string $locale + * @param int|string $num Will be cast as int + * @param string $locale * * @return bool|string */ @@ -24,8 +24,10 @@ function number_to_size($num, int $precision = 1, ?string $locale = null) { // Strip any formatting & ensure numeric input try { + // @phpstan-ignore-next-line $num = 0 + str_replace(',', '', $num); } catch (ErrorException $ee) { + // Catch "Warning: A non-numeric value encountered" return false; } @@ -67,7 +69,9 @@ function number_to_size($num, int $precision = 1, ?string $locale = null) * * @see https://simple.wikipedia.org/wiki/Names_for_large_numbers * - * @param string $num + * @param int|string $num Will be cast as int + * @param int $precision [optional] The optional number of decimal digits to round to. + * @param string|null $locale [optional] * * @return bool|string */ @@ -75,6 +79,7 @@ function number_to_amount($num, int $precision = 0, ?string $locale = null) { // Strip any formatting & ensure numeric input try { + // @phpstan-ignore-next-line $num = 0 + str_replace(',', '', $num); } catch (ErrorException $ee) { return false; @@ -88,19 +93,19 @@ function number_to_amount($num, int $precision = 0, ?string $locale = null) $generalLocale = substr($locale, 0, $underscorePos); } - if ($num > 1_000_000_000_000_000) { + if ($num >= 1_000_000_000_000_000) { $suffix = lang('Number.quadrillion', [], $generalLocale); $num = round(($num / 1_000_000_000_000_000), $precision); - } elseif ($num > 1_000_000_000_000) { + } elseif ($num >= 1_000_000_000_000) { $suffix = lang('Number.trillion', [], $generalLocale); $num = round(($num / 1_000_000_000_000), $precision); - } elseif ($num > 1_000_000_000) { + } elseif ($num >= 1_000_000_000) { $suffix = lang('Number.billion', [], $generalLocale); $num = round(($num / 1_000_000_000), $precision); - } elseif ($num > 1_000_000) { + } elseif ($num >= 1_000_000) { $suffix = lang('Number.million', [], $generalLocale); $num = round(($num / 1_000_000), $precision); - } elseif ($num > 1000) { + } elseif ($num >= 1000) { $suffix = lang('Number.thousand', [], $generalLocale); $num = round(($num / 1000), $precision); } @@ -173,9 +178,9 @@ function format_number(float $num, int $precision = 1, ?string $locale = null, a /** * Convert a number to a roman numeral. * - * @param string $num it will convert to int + * @param int|string $num it will convert to int */ - function number_to_roman(string $num): ?string + function number_to_roman($num): ?string { static $map = [ 'M' => 1000, diff --git a/system/Helpers/text_helper.php b/system/Helpers/text_helper.php index 2343a89b..14551545 100755 --- a/system/Helpers/text_helper.php +++ b/system/Helpers/text_helper.php @@ -410,10 +410,10 @@ function word_wrap(string $str, int $charlim = 76): string * * This function will strip tags from a string, split it at its max_length and ellipsize * - * @param string $str String to ellipsize - * @param int $maxLength Max length of string - * @param mixed $position int (1|0) or float, .5, .2, etc for position to split - * @param string $ellipsis ellipsis ; Default '...' + * @param string $str String to ellipsize + * @param int $maxLength Max length of string + * @param float|int $position int (1|0) or float, .5, .2, etc for position to split + * @param string $ellipsis ellipsis ; Default '...' * * @return string Ellipsized string */ @@ -446,9 +446,9 @@ function ellipsize(string $str, int $maxLength, $position = 1, string $ellipsis * * Removes slashes contained in a string or in an array * - * @param mixed $str string or array + * @param array|string $str string or array * - * @return mixed string or array + * @return array|string string or array */ function strip_slashes($str) { diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index 8443c7a0..c6c810b6 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -38,7 +38,7 @@ function _get_uri($relativePath = '', ?App $config = null): URI $appConfig = null; if ($config === null) { /** @var App $appConfig */ - $appConfig = config('App'); + $appConfig = config(App::class); if ($appConfig->baseURL === '') { throw new InvalidArgumentException( @@ -145,7 +145,7 @@ function site_url($relativePath = '', ?string $scheme = null, ?App $config = nul function base_url($relativePath = '', ?string $scheme = null): string { /** @var App $config */ - $config = clone config('App'); + $config = clone config(App::class); // Use the current baseURL for multiple domain support $request = Services::request(); @@ -238,7 +238,7 @@ function uri_string(): string function index_page(?App $altConfig = null): string { // use alternate config if provided, else default one - $config = $altConfig ?? config('App'); + $config = $altConfig ?? config(App::class); return $config->indexPage; } @@ -258,7 +258,7 @@ function index_page(?App $altConfig = null): string function anchor($uri = '', string $title = '', $attributes = '', ?App $altConfig = null): string { // use alternate config if provided, else default one - $config = $altConfig ?? config('App'); + $config = $altConfig ?? config(App::class); $siteUrl = is_array($uri) ? site_url($uri, null, $config) : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null, $config)); // eliminate trailing slash @@ -291,7 +291,7 @@ function anchor($uri = '', string $title = '', $attributes = '', ?App $altConfig function anchor_popup($uri = '', string $title = '', $attributes = false, ?App $altConfig = null): string { // use alternate config if provided, else default one - $config = $altConfig ?? config('App'); + $config = $altConfig ?? config(App::class); $siteUrl = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null, $config); $siteUrl = rtrim($siteUrl, '/'); diff --git a/system/Model.php b/system/Model.php index 80e4bb48..a9c8e4f2 100644 --- a/system/Model.php +++ b/system/Model.php @@ -792,7 +792,7 @@ protected function objectToRawArray($data, bool $onlyChanged = true, bool $recur * * @param string $name Name * - * @return mixed + * @return array|BaseBuilder|bool|float|int|object|string|null */ public function __get(string $name) { @@ -821,7 +821,7 @@ public function __isset(string $name): bool * Provides direct access to method in the builder (if available) * and the database connection. * - * @return mixed + * @return $this|array|BaseBuilder|bool|float|int|object|string|null */ public function __call(string $name, array $params) { @@ -866,7 +866,7 @@ private function checkBuilderMethod(string $name): void * * @codeCoverageIgnore * - * @deprecated since 4.1 + * @deprecated 4.1.0 */ public static function classToArray($data, $primaryKey = null, string $dateFormat = 'datetime', bool $onlyChanged = true): array { diff --git a/system/Publisher/Publisher.php b/system/Publisher/Publisher.php index 88459153..82948b83 100644 --- a/system/Publisher/Publisher.php +++ b/system/Publisher/Publisher.php @@ -15,6 +15,7 @@ use CodeIgniter\Files\FileCollection; use CodeIgniter\HTTP\URI; use CodeIgniter\Publisher\Exceptions\PublisherException; +use Config\Publisher as PublisherConfig; use RuntimeException; use Throwable; @@ -162,7 +163,7 @@ public function __construct(?string $source = null, ?string $destination = null) $this->replacer = new ContentReplacer(); // Restrictions are intentionally not injected to prevent overriding - $this->restrictions = config('Publisher')->restrictions; + $this->restrictions = config(PublisherConfig::class)->restrictions; // Make sure the destination is allowed foreach (array_keys($this->restrictions) as $directory) { diff --git a/system/Router/AutoRouter.php b/system/Router/AutoRouter.php index 3096b3bb..1475def4 100644 --- a/system/Router/AutoRouter.php +++ b/system/Router/AutoRouter.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Router; +use Closure; use CodeIgniter\Exceptions\PageNotFoundException; /** @@ -19,11 +20,11 @@ final class AutoRouter implements AutoRouterInterface { /** - * List of controllers registered for the CLI verb that should not be accessed in the web. + * List of CLI routes that do not contain '*' routes. * - * @var class-string[] + * @var array [routeKey => handler] */ - private array $protectedControllers; + private array $cliRoutes; /** * Sub-directory that contains the requested controller class. @@ -58,17 +59,17 @@ final class AutoRouter implements AutoRouterInterface private string $defaultNamespace; public function __construct( - array $protectedControllers, + array $cliRoutes, string $defaultNamespace, string $defaultController, string $defaultMethod, bool $translateURIDashes, string $httpVerb ) { - $this->protectedControllers = $protectedControllers; - $this->defaultNamespace = $defaultNamespace; - $this->translateURIDashes = $translateURIDashes; - $this->httpVerb = $httpVerb; + $this->cliRoutes = $cliRoutes; + $this->defaultNamespace = $defaultNamespace; + $this->translateURIDashes = $translateURIDashes; + $this->httpVerb = $httpVerb; $this->controller = $defaultController; $this->method = $defaultMethod; @@ -126,18 +127,31 @@ public function getRoute(string $uri, string $httpVerb): array $controller .= $controllerName; $controller = strtolower($controller); - - foreach ($this->protectedControllers as $controllerInRoute) { - if (! is_string($controllerInRoute)) { - continue; - } - if (strtolower($controllerInRoute) !== $controller) { - continue; + $methodName = strtolower($this->methodName()); + + foreach ($this->cliRoutes as $handler) { + if (is_string($handler)) { + $handler = strtolower($handler); + + // Like $routes->cli('hello/(:segment)', 'Home::$1') + if (strpos($handler, '::$') !== false) { + throw new PageNotFoundException( + 'Cannot access CLI Route: ' . $uri + ); + } + + if (strpos($handler, $controller . '::' . $methodName) === 0) { + throw new PageNotFoundException( + 'Cannot access CLI Route: ' . $uri + ); + } + + if ($handler === $controller) { + throw new PageNotFoundException( + 'Cannot access CLI Route: ' . $uri + ); + } } - - throw new PageNotFoundException( - 'Cannot access the controller in a CLI Route. Controller: ' . $controllerInRoute - ); } } diff --git a/system/Router/AutoRouterImproved.php b/system/Router/AutoRouterImproved.php index 6f2aa1af..42fe18e9 100644 --- a/system/Router/AutoRouterImproved.php +++ b/system/Router/AutoRouterImproved.php @@ -106,6 +106,9 @@ public function getRoute(string $uri, string $httpVerb): array { $httpVerb = strtolower($httpVerb); + // Reset Controller method params. + $this->params = []; + $defaultMethod = $httpVerb . ucfirst($this->defaultMethod); $this->method = $defaultMethod; diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 66724935..48831519 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -409,7 +409,7 @@ public function setAutoRoute(bool $value): RouteCollectionInterface * * This setting is passed to the Router class and handled there. * - * @param callable|null $callable + * @param callable|string|null $callable */ public function set404Override($callable = null): RouteCollectionInterface { @@ -484,8 +484,10 @@ public function shouldAutoRoute(): bool /** * Returns the raw array of available routes. + * + * @param bool $includeWildcard Whether to include '*' routes. */ - public function getRoutes(?string $verb = null): array + public function getRoutes(?string $verb = null, bool $includeWildcard = true): array { if (empty($verb)) { $verb = $this->getHTTPVerb(); @@ -501,7 +503,7 @@ public function getRoutes(?string $verb = null): array if (isset($this->routes[$verb])) { // Keep current verb's routes at the beginning, so they're matched // before any of the generic, "add" routes. - $collection = $this->routes[$verb] + ($this->routes['*'] ?? []); + $collection = $includeWildcard ? $this->routes[$verb] + ($this->routes['*'] ?? []) : $this->routes[$verb]; foreach ($collection as $r) { $key = key($r['route']); @@ -1239,6 +1241,11 @@ protected function buildReverseRoute(string $from, array $params): string // Build our resulting string, inserting the $params in // the appropriate places. foreach ($matches[0] as $index => $pattern) { + if (! isset($params[$index])) { + throw new InvalidArgumentException( + 'Missing argument for "' . $pattern . '" in route "' . $from . '".' + ); + } if (! preg_match('#^' . $pattern . '$#u', $params[$index])) { throw RouterException::forInvalidParameterType(); } @@ -1265,8 +1272,7 @@ private function replaceLocale(string $route, ?string $locale = null): string // Check invalid locale if ($locale !== null) { - /** @var App $config */ - $config = config('App'); + $config = config(App::class); if (! in_array($locale, $config->supportedLocales, true)) { $locale = null; } diff --git a/system/Router/Router.php b/system/Router/Router.php index f9299fa1..f0b9d2c9 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -16,6 +16,8 @@ use CodeIgniter\HTTP\Request; use CodeIgniter\Router\Exceptions\RedirectException; use CodeIgniter\Router\Exceptions\RouterException; +use Config\App; +use Config\Feature; /** * Request router. @@ -131,7 +133,7 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request $this->translateURIDashes = $this->collection->shouldTranslateURIDashes(); if ($this->collection->shouldAutoRoute()) { - $autoRoutesImproved = config('Feature')->autoRoutesImproved ?? false; + $autoRoutesImproved = config(Feature::class)->autoRoutesImproved ?? false; if ($autoRoutesImproved) { $this->autoRouter = new AutoRouterImproved( $this->collection->getRegisteredControllers('*'), @@ -143,7 +145,7 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request ); } else { $this->autoRouter = new AutoRouter( - $this->collection->getRegisteredControllers('cli'), + $this->collection->getRoutes('cli', false), // @phpstan-ignore-line $this->collection->getDefaultNamespace(), $this->collection->getDefaultController(), $this->collection->getDefaultMethod(), @@ -181,7 +183,7 @@ public function handle(?string $uri = null) // Checks defined routes if ($this->checkRoutes($uri)) { if ($this->collection->isFiltered($this->matchedRoute[0])) { - $multipleFiltersEnabled = config('Feature')->multipleFilters ?? false; + $multipleFiltersEnabled = config(Feature::class)->multipleFilters ?? false; if ($multipleFiltersEnabled) { $this->filtersInfo = $this->collection->getFiltersForRoute($this->matchedRoute[0]); } else { @@ -391,6 +393,7 @@ public function getLocale() */ protected function checkRoutes(string $uri): bool { + // @phpstan-ignore-next-line $routes = $this->collection->getRoutes($this->collection->getHTTPVerb()); // Don't waste any time @@ -441,7 +444,7 @@ protected function checkRoutes(string $uri): bool ); if ($this->collection->shouldUseSupportedLocalesOnly() - && ! in_array($matched['locale'], config('App')->supportedLocales, true)) { + && ! in_array($matched['locale'], config(App::class)->supportedLocales, true)) { // Throw exception to prevent the autorouter, if enabled, // from trying to find a route throw PageNotFoundException::forLocaleNotSupported($matched['locale']); diff --git a/system/Security/Security.php b/system/Security/Security.php index 23915671..c31cea42 100644 --- a/system/Security/Security.php +++ b/system/Security/Security.php @@ -172,7 +172,7 @@ class Security implements SecurityInterface public function __construct(App $config) { /** @var SecurityConfig|null $security */ - $security = config('Security'); + $security = config(SecurityConfig::class); // Store CSRF-related configurations if ($security instanceof SecurityConfig) { @@ -223,7 +223,7 @@ private function configureSession(): void private function configureCookie(App $config): void { /** @var CookieConfig|null $cookie */ - $cookie = config('Cookie'); + $cookie = config(CookieConfig::class); if ($cookie instanceof CookieConfig) { $cookiePrefix = $cookie->prefix; diff --git a/system/Session/Handlers/BaseHandler.php b/system/Session/Handlers/BaseHandler.php index f392c492..9ea9df97 100644 --- a/system/Session/Handlers/BaseHandler.php +++ b/system/Session/Handlers/BaseHandler.php @@ -108,7 +108,7 @@ abstract class BaseHandler implements SessionHandlerInterface public function __construct(AppConfig $config, string $ipAddress) { /** @var SessionConfig|null $session */ - $session = config('Session'); + $session = config(SessionConfig::class); // Store Session configurations if ($session instanceof SessionConfig) { @@ -123,7 +123,7 @@ public function __construct(AppConfig $config, string $ipAddress) } /** @var CookieConfig|null $cookie */ - $cookie = config('Cookie'); + $cookie = config(CookieConfig::class); if ($cookie instanceof CookieConfig) { // Session cookies have no prefix. diff --git a/system/Session/Handlers/DatabaseHandler.php b/system/Session/Handlers/DatabaseHandler.php index b566a3b0..fa474faa 100644 --- a/system/Session/Handlers/DatabaseHandler.php +++ b/system/Session/Handlers/DatabaseHandler.php @@ -74,7 +74,7 @@ public function __construct(AppConfig $config, string $ipAddress) parent::__construct($config, $ipAddress); /** @var SessionConfig|null $session */ - $session = config('Session'); + $session = config(SessionConfig::class); // Store Session configurations if ($session instanceof SessionConfig) { diff --git a/system/Session/Handlers/MemcachedHandler.php b/system/Session/Handlers/MemcachedHandler.php index e4c8c6e8..e6f575e9 100644 --- a/system/Session/Handlers/MemcachedHandler.php +++ b/system/Session/Handlers/MemcachedHandler.php @@ -59,7 +59,7 @@ public function __construct(AppConfig $config, string $ipAddress) parent::__construct($config, $ipAddress); /** @var SessionConfig|null $session */ - $session = config('Session'); + $session = config(SessionConfig::class); $this->sessionExpiration = ($session instanceof SessionConfig) ? $session->expiration : $config->sessionExpiration; diff --git a/system/Session/Handlers/RedisHandler.php b/system/Session/Handlers/RedisHandler.php index 99106376..56b72532 100644 --- a/system/Session/Handlers/RedisHandler.php +++ b/system/Session/Handlers/RedisHandler.php @@ -72,7 +72,7 @@ public function __construct(AppConfig $config, string $ipAddress) parent::__construct($config, $ipAddress); /** @var SessionConfig|null $session */ - $session = config('Session'); + $session = config(SessionConfig::class); // Store Session configurations if ($session instanceof SessionConfig) { diff --git a/system/Session/Session.php b/system/Session/Session.php index 4f536ca0..efbd67d5 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -166,7 +166,7 @@ public function __construct(SessionHandlerInterface $driver, App $config) $this->driver = $driver; /** @var SessionConfig|null $session */ - $session = config('Session'); + $session = config(SessionConfig::class); // Store Session configurations if ($session instanceof SessionConfig) { @@ -195,7 +195,7 @@ public function __construct(SessionHandlerInterface $driver, App $config) $this->cookieSameSite = $config->cookieSameSite ?? $this->cookieSameSite; /** @var CookieConfig $cookie */ - $cookie = config('Cookie'); + $cookie = config(CookieConfig::class); $this->cookie = (new Cookie($this->sessionCookieName, '', [ 'expires' => $this->sessionExpiration === 0 ? 0 : Time::now()->getTimestamp() + $this->sessionExpiration, diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index bfde56fa..0ed50a50 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -25,6 +25,7 @@ use CodeIgniter\Test\Mock\MockSession; use Config\App; use Config\Autoload; +use Config\Email; use Config\Modules; use Config\Services; use Exception; @@ -323,7 +324,7 @@ protected function mockCache() */ protected function mockEmail() { - Services::injectMock('email', new MockEmail(config('Email'))); + Services::injectMock('email', new MockEmail(config(Email::class))); } /** @@ -333,7 +334,7 @@ protected function mockSession() { $_SESSION = []; - $config = config('App'); + $config = config(App::class); $session = new MockSession(new ArrayHandler($config, '0.0.0.0'), $config); Services::injectMock('session', $session); diff --git a/system/Test/ControllerTestTrait.php b/system/Test/ControllerTestTrait.php index 64cbcbe1..828dabb9 100644 --- a/system/Test/ControllerTestTrait.php +++ b/system/Test/ControllerTestTrait.php @@ -96,7 +96,7 @@ protected function setUpControllerTestTrait(): void helper('url'); if (empty($this->appConfig)) { - $this->appConfig = config('App'); + $this->appConfig = config(App::class); } if (! $this->uri instanceof URI) { diff --git a/system/Test/ControllerTester.php b/system/Test/ControllerTester.php index a4b41a94..2fff82ee 100644 --- a/system/Test/ControllerTester.php +++ b/system/Test/ControllerTester.php @@ -96,7 +96,7 @@ trait ControllerTester protected function setUpControllerTester(): void { if (empty($this->appConfig)) { - $this->appConfig = config('App'); + $this->appConfig = config(App::class); } if (! $this->uri instanceof URI) { diff --git a/system/Test/Fabricator.php b/system/Test/Fabricator.php index df1906b6..6c9e82ff 100644 --- a/system/Test/Fabricator.php +++ b/system/Test/Fabricator.php @@ -14,6 +14,7 @@ use CodeIgniter\Exceptions\FrameworkException; use CodeIgniter\I18n\Time; use CodeIgniter\Model; +use Config\App; use Faker\Factory; use Faker\Generator; use InvalidArgumentException; @@ -114,7 +115,7 @@ public function __construct($model, ?array $formatters = null, ?string $locale = // If no locale was specified then use the App default if ($locale === null) { - $locale = config('App')->defaultLocale; + $locale = config(App::class)->defaultLocale; } // There is no easy way to retrieve the locale from Faker so we will store it diff --git a/system/Test/FeatureTestCase.php b/system/Test/FeatureTestCase.php index bf157aea..0bef8732 100644 --- a/system/Test/FeatureTestCase.php +++ b/system/Test/FeatureTestCase.php @@ -18,6 +18,7 @@ use CodeIgniter\HTTP\UserAgent; use CodeIgniter\Router\Exceptions\RedirectException; use CodeIgniter\Router\RouteCollection; +use Config\App; use Config\Services; use Exception; use ReflectionException; @@ -291,7 +292,7 @@ public function options(string $path, ?array $params = null) */ protected function setupRequest(string $method, ?string $path = null): IncomingRequest { - $config = config('App'); + $config = config(App::class); $uri = new URI(rtrim($config->baseURL, '/') . '/' . trim($path, '/ ')); $request = new IncomingRequest($config, clone $uri, null, new UserAgent()); diff --git a/system/Test/FeatureTestTrait.php b/system/Test/FeatureTestTrait.php index f7a6d060..74529e77 100644 --- a/system/Test/FeatureTestTrait.php +++ b/system/Test/FeatureTestTrait.php @@ -110,7 +110,7 @@ public function withBodyFormat(string $format) /** * Set the raw body for the request * - * @param mixed $body + * @param string $body * * @return $this */ @@ -137,6 +137,8 @@ public function skipEvents() * Calls a single URI, executes it, and returns a TestResponse * instance that can be used to run many assertions against. * + * @param string $method HTTP verb + * * @return TestResponse */ public function call(string $method, string $path, ?array $params = null) @@ -156,7 +158,7 @@ public function call(string $method, string $path, ?array $params = null) $request = $this->setupRequest($method, $path); $request = $this->setupHeaders($request); $request = $this->populateGlobals($method, $request, $params); - $request = $this->setRequestBody($request); + $request = $this->setRequestBody($request, $params); // Initialize the RouteCollection if (! $routes = $this->routes) { @@ -281,6 +283,8 @@ public function options(string $path, ?array $params = null) /** * Setup a Request object to use so that CodeIgniter * won't try to auto-populate some of the items. + * + * @param string $method HTTP verb */ protected function setupRequest(string $method, ?string $path = null): IncomingRequest { @@ -325,6 +329,8 @@ protected function setupHeaders(IncomingRequest $request) * * Always populate the GET vars based on the URI. * + * @param string $method HTTP verb + * * @return Request * * @throws ReflectionException @@ -333,16 +339,23 @@ protected function populateGlobals(string $method, Request $request, ?array $par { // $params should set the query vars if present, // otherwise set it from the URL. - $get = ! empty($params) && $method === 'get' + $get = (! empty($params) && $method === 'get') ? $params : $this->getPrivateProperty($request->getUri(), 'query'); $request->setGlobal('get', $get); - if ($method !== 'get') { - $request->setGlobal($method, $params); + + if ($method === 'get') { + $request->setGlobal('request', $request->fetchGlobal('get')); } - $request->setGlobal('request', $params); + if ($method === 'post') { + $request->setGlobal($method, $params); + $request->setGlobal( + 'request', + $request->fetchGlobal('post') + $request->fetchGlobal('get') + ); + } $_SESSION = $this->session ?? []; @@ -354,31 +367,30 @@ protected function populateGlobals(string $method, Request $request, ?array $par * This allows the body to be formatted in a way that the controller is going to * expect as in the case of testing a JSON or XML API. * - * @param array|null $params The parameters to be formatted and put in the body. If this is empty, it will get the - * what has been loaded into the request global of the request class. + * @param array|null $params The parameters to be formatted and put in the body. */ protected function setRequestBody(Request $request, ?array $params = null): Request { - if (isset($this->requestBody) && $this->requestBody !== '') { + if ($this->requestBody !== '') { $request->setBody($this->requestBody); - - return $request; } - if (isset($this->bodyFormat) && $this->bodyFormat !== '') { - if (empty($params)) { - $params = $request->fetchGlobal('request'); - } + if ($this->bodyFormat !== '') { $formatMime = ''; if ($this->bodyFormat === 'json') { $formatMime = 'application/json'; } elseif ($this->bodyFormat === 'xml') { $formatMime = 'application/xml'; } - if (! empty($formatMime) && ! empty($params)) { + + if ($formatMime !== '') { + $request->setHeader('Content-Type', $formatMime); + } + + if ($params !== null && $formatMime !== '') { $formatted = Services::format()->getFormatter($formatMime)->format($params); + // "withBodyFormat() and $params of call()" has higher priority than withBody(). $request->setBody($formatted); - $request->setHeader('Content-Type', $formatMime); } } diff --git a/system/Test/FilterTestTrait.php b/system/Test/FilterTestTrait.php index 3bcd3796..4901a585 100644 --- a/system/Test/FilterTestTrait.php +++ b/system/Test/FilterTestTrait.php @@ -98,7 +98,7 @@ protected function setUpFilterTestTrait(): void $this->response ??= clone Services::response(); // Create our config and Filters instance to reuse for performance - $this->filtersConfig ??= config('Filters'); + $this->filtersConfig ??= config(FiltersConfig::class); $this->filters ??= new Filters($this->filtersConfig, $this->request, $this->response); if ($this->collection === null) { diff --git a/system/Test/TestResponse.php b/system/Test/TestResponse.php index 700b27ef..62706158 100644 --- a/system/Test/TestResponse.php +++ b/system/Test/TestResponse.php @@ -350,7 +350,7 @@ public function assertCookieExpired(string $key, string $prefix = '') /** * Returns the response's body as JSON * - * @return false|string|null + * @return false|string */ public function getJSON() { @@ -385,7 +385,7 @@ public function assertJSONFragment(array $fragment, bool $strict = false) * Asserts that the JSON exactly matches the passed in data. * If the value being passed in is a string, it must be a json_encoded string. * - * @param array|string $test + * @param array|object|string $test * * @throws Exception */ diff --git a/system/ThirdParty/Kint/Parser/Parser.php b/system/ThirdParty/Kint/Parser/Parser.php index 5c7a40a3..50642b90 100644 --- a/system/ThirdParty/Kint/Parser/Parser.php +++ b/system/ThirdParty/Kint/Parser/Parser.php @@ -374,7 +374,11 @@ private function parseArray(array &$var, Value $o): Value } $stash = $val; - $copy[$i] = $refmarker; + try { + $copy[$i] = $refmarker; + } catch (TypeError $e) { + $child->reference = true; + } if ($val === $refmarker) { $child->reference = true; $val = $stash; diff --git a/system/ThirdParty/Kint/Utils.php b/system/ThirdParty/Kint/Utils.php index b51b3355..78b5a1e8 100644 --- a/system/ThirdParty/Kint/Utils.php +++ b/system/ThirdParty/Kint/Utils.php @@ -150,6 +150,10 @@ public static function composerGetExtras(string $key = 'kint'): array */ public static function composerSkipFlags(): void { + if (\defined('KINT_SKIP_FACADE') && \defined('KINT_SKIP_HELPERS')) { + return; + } + $extras = self::composerGetExtras(); if (!empty($extras['disable-facade']) && !\defined('KINT_SKIP_FACADE')) { diff --git a/system/ThirdParty/Kint/resources/compiled/rich.js b/system/ThirdParty/Kint/resources/compiled/rich.js index c02e3fe2..39db8fb9 100644 --- a/system/ThirdParty/Kint/resources/compiled/rich.js +++ b/system/ThirdParty/Kint/resources/compiled/rich.js @@ -1 +1 @@ -void 0===window.kintRich&&(window.kintRich=function(){"use strict";var l={selectText:function(e){var t=window.getSelection(),a=document.createRange();a.selectNodeContents(e),t.removeAllRanges(),t.addRange(a)},toggle:function(e,t){var a=l.getChildren(e);a&&(e.classList.toggle("kint-show",t),1===a.childNodes.length)&&(a=a.childNodes[0].childNodes[0])&&a.classList&&a.classList.contains("kint-parent")&&l.toggle(a,t)},toggleChildren:function(e,t){var a=l.getChildren(e);if(a){var o=a.getElementsByClassName("kint-parent"),s=o.length;for(void 0===t&&(t=e.classList.contains("kint-show"));s--;)l.toggle(o[s],t)}},switchTab:function(e){var t=e.previousSibling,a=0;for(e.parentNode.getElementsByClassName("kint-active-tab")[0].classList.remove("kint-active-tab"),e.classList.add("kint-active-tab");t;)1===t.nodeType&&a++,t=t.previousSibling;for(var o=e.parentNode.nextSibling.childNodes,s=0;s"},openInNewWindow:function(e){var t=window.open();t&&(t.document.open(),t.document.write(l.mktag("html")+l.mktag("head")+l.mktag("title")+"Kint ("+(new Date).toISOString()+")"+l.mktag("/title")+l.mktag('meta charset="utf-8"')+l.mktag('script class="kint-rich-script" nonce="'+l.script.nonce+'"')+l.script.innerHTML+l.mktag("/script")+l.mktag('style class="kint-rich-style" nonce="'+l.style.nonce+'"')+l.style.innerHTML+l.mktag("/style")+l.mktag("/head")+l.mktag("body")+'
'+e.parentNode.outerHTML+"
"+l.mktag("/body")),t.document.close())},sortTable:function(e,a){var t=e.tBodies[0];[].slice.call(e.tBodies[0].rows).sort(function(e,t){if(e=e.cells[a].textContent.trim().toLocaleLowerCase(),t=t.cells[a].textContent.trim().toLocaleLowerCase(),isNaN(e)||isNaN(t)){if(isNaN(e)&&!isNaN(t))return 1;if(isNaN(t)&&!isNaN(e))return-1}else e=parseFloat(e),t=parseFloat(t);return eli:not(.kint-active-tab)").forEach(function(e){l.isFolderOpen()&&!l.folder.contains(e)||0===e.offsetWidth&&0===e.offsetHeight||l.keyboardNav.targets.push(e)}),e&&-1!==l.keyboardNav.targets.indexOf(e)&&(l.keyboardNav.target=l.keyboardNav.targets.indexOf(e))},sync:function(e){var t=document.querySelector(".kint-focused");t&&t.classList.remove("kint-focused"),l.keyboardNav.active&&((t=l.keyboardNav.targets[l.keyboardNav.target]).classList.add("kint-focused"),e||l.keyboardNav.scroll(t))},scroll:function(e){var t,a;e!==l.folder.querySelector("dt > nav")&&(e=(t=function(e){return e.offsetTop+(e.offsetParent?t(e.offsetParent):0)})(e),l.isFolderOpen()?(a=l.folder.querySelector("dd.kint-foldout")).scrollTo(0,e-a.clientHeight/2):window.scrollTo(0,e-window.innerHeight/2))},moveCursor:function(e){for(l.keyboardNav.target+=e;l.keyboardNav.target<0;)l.keyboardNav.target+=l.keyboardNav.targets.length;for(;l.keyboardNav.target>=l.keyboardNav.targets.length;)l.keyboardNav.target-=l.keyboardNav.targets.length;l.keyboardNav.sync()},setCursor:function(e){if(!l.isFolderOpen()||l.folder.contains(e)){l.keyboardNav.fetchTargets();for(var t=0;t"},openInNewWindow:function(e){var t=window.open();t&&(t.document.open(),t.document.write(l.mktag("html")+l.mktag("head")+l.mktag("title")+"Kint ("+(new Date).toISOString()+")"+l.mktag("/title")+l.mktag('meta charset="utf-8"')+l.mktag('script class="kint-rich-script" nonce="'+l.script.nonce+'"')+l.script.innerHTML+l.mktag("/script")+l.mktag('style class="kint-rich-style" nonce="'+l.style.nonce+'"')+l.style.innerHTML+l.mktag("/style")+l.mktag("/head")+l.mktag("body")+'
'+e.parentNode.outerHTML+"
"+l.mktag("/body")),t.document.close())},sortTable:function(e,a){var t=e.tBodies[0];[].slice.call(e.tBodies[0].rows).sort(function(e,t){if(e=e.cells[a].textContent.trim().toLocaleLowerCase(),t=t.cells[a].textContent.trim().toLocaleLowerCase(),isNaN(e)||isNaN(t)){if(isNaN(e)&&!isNaN(t))return 1;if(isNaN(t)&&!isNaN(e))return-1}else e=parseFloat(e),t=parseFloat(t);return eli:not(.kint-active-tab)").forEach(function(e){l.isFolderOpen()&&!l.folder.contains(e)||0===e.offsetWidth&&0===e.offsetHeight||l.keyboardNav.targets.push(e)}),e&&-1!==l.keyboardNav.targets.indexOf(e)&&(l.keyboardNav.target=l.keyboardNav.targets.indexOf(e))},sync:function(e){var t=document.querySelector(".kint-focused");t&&t.classList.remove("kint-focused"),l.keyboardNav.active&&((t=l.keyboardNav.targets[l.keyboardNav.target]).classList.add("kint-focused"),e||l.keyboardNav.scroll(t))},scroll:function(e){var t,a;l.folder&&e===l.folder.querySelector("dt > nav")||(e=(t=function(e){return e.offsetTop+(e.offsetParent?t(e.offsetParent):0)})(e),l.isFolderOpen()?(a=l.folder.querySelector("dd.kint-foldout")).scrollTo(0,e-a.clientHeight/2):window.scrollTo(0,e-window.innerHeight/2))},moveCursor:function(e){for(l.keyboardNav.target+=e;l.keyboardNav.target<0;)l.keyboardNav.target+=l.keyboardNav.targets.length;for(;l.keyboardNav.target>=l.keyboardNav.targets.length;)l.keyboardNav.target-=l.keyboardNav.targets.length;l.keyboardNav.sync()},setCursor:function(e){if(!l.isFolderOpen()||l.folder.contains(e)){l.keyboardNav.fetchTargets();for(var t=0;t', $str), $ct = count($ex), $i = 0; $i < $ct; $i++) { - $xhtml = ! (config('DocTypes')->html5 ?? false); + $xhtml = ! (config(DocTypes::class)->html5 ?? false); $newstr .= (($i % 2) === 0) ? nl2br($ex[$i], $xhtml) : $ex[$i]; if ($ct - 1 !== $i) { diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index 5e539561..91e44135 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -462,14 +462,14 @@ public function withRequest(RequestInterface $request): ValidationInterface } /** - * Sets an individual rule and custom error messages for a single field. + * Sets (or adds) an individual rule and custom error messages for a single + * field. * * The custom error message should be just the messages that apply to * this field, like so: - * * [ - * 'rule' => 'message', - * 'rule' => 'message', + * 'rule1' => 'message1', + * 'rule2' => 'message2', * ] * * @param array|string $rules @@ -502,8 +502,8 @@ public function setRule(string $field, ?string $label, $rules, array $errors = [ /** * Stores the rules that should be used to validate the items. - * Rules should be an array formatted like: * + * Rules should be an array formatted like: * [ * 'field' => 'rule1|rule2' * ] @@ -511,12 +511,12 @@ public function setRule(string $field, ?string $label, $rules, array $errors = [ * The $errors array should be formatted like: * [ * 'field' => [ - * 'rule' => 'message', - * 'rule' => 'message + * 'rule1' => 'message1', + * 'rule2' => 'message2', * ], * ] * - * @param array $errors // An array of custom error messages + * @param array $errors An array of custom error messages */ public function setRules(array $rules, array $errors = []): ValidationInterface { @@ -665,7 +665,7 @@ protected function loadRuleSets() * same format used with setRules(). Additionally, check * for {group}_errors for an array of custom error messages. * - * @return array + * @return array [rules, customErrors] * * @throws ValidationException */ @@ -693,7 +693,7 @@ public function loadRuleGroup(?string $group = null) $this->customErrors = $this->config->{$errorName}; } - return $this->rules; + return [$this->rules, $this->customErrors]; } /** diff --git a/system/View/Cell.php b/system/View/Cell.php index b898adc0..5c4d0ce2 100644 --- a/system/View/Cell.php +++ b/system/View/Cell.php @@ -64,7 +64,10 @@ public function __construct(CacheInterface $cache) /** * Render a cell, returning its body as a string. * - * @param array|string|null $params + * @param string $library Cell class and method name. + * @param array|string|null $params Parameters to pass to the method. + * @param int $ttl Number of seconds to cache the cell. + * @param string|null $cacheName Cache item name. * * @throws ReflectionException */ @@ -76,6 +79,8 @@ public function render(string $library, $params = null, int $ttl = 0, ?string $c ? get_class($instance) : null; + $params = $this->prepareParams($params); + // Is the output cached? $cacheName = ! empty($cacheName) ? $cacheName @@ -93,8 +98,6 @@ public function render(string $library, $params = null, int $ttl = 0, ?string $c throw ViewException::forInvalidCellMethod($class, $method); } - $params = $this->prepareParams($params); - $output = $instance instanceof BaseCell ? $this->renderCell($instance, $method, $params) : $this->renderSimpleClass($instance, $method, $params, $class); @@ -175,7 +178,8 @@ protected function determineClass(string $library): array } // locate and return an instance of the cell - $object = Factories::cells($class); + // @TODO extend Factories to be able to load classes with the same short name. + $object = class_exists($class) ? new $class() : Factories::cells($class); if (! is_object($object)) { throw ViewException::forInvalidCellClass($class); diff --git a/system/View/ViewDecoratorTrait.php b/system/View/ViewDecoratorTrait.php index 243c229b..97cdbe0a 100644 --- a/system/View/ViewDecoratorTrait.php +++ b/system/View/ViewDecoratorTrait.php @@ -12,6 +12,7 @@ namespace CodeIgniter\View; use CodeIgniter\View\Exceptions\ViewException; +use Config\View as ViewConfig; trait ViewDecoratorTrait { @@ -21,7 +22,7 @@ trait ViewDecoratorTrait */ protected function decorateOutput(string $html): string { - $decorators = \config('View')->decorators; + $decorators = \config(ViewConfig::class)->decorators; foreach ($decorators as $decorator) { if (! is_subclass_of($decorator, ViewDecoratorInterface::class)) {