diff --git a/README.md b/README.md index 974decb2..00cdf704 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,59 @@ -# nHance +# CodeIgniter 4 Framework -## Tech Stack -**Please** read the dev guide \ No newline at end of file +## What is CodeIgniter? + +CodeIgniter is a PHP full-stack web framework that is light, fast, flexible and secure. +More information can be found at the [official site](https://codeigniter.com). + +This repository holds the distributable version of the framework. +It has been built from the +[development repository](https://github.com/codeigniter4/CodeIgniter4). + +More information about the plans for version 4 can be found in [CodeIgniter 4](https://forum.codeigniter.com/forumdisplay.php?fid=28) on the forums. + +The user guide corresponding to the latest version of the framework can be found +[here](https://codeigniter4.github.io/userguide/). + +## Important Change with index.php + +`index.php` is no longer in the root of the project! It has been moved inside the *public* folder, +for better security and separation of components. + +This means that you should configure your web server to "point" to your project's *public* folder, and +not to the project root. A better practice would be to configure a virtual host to point there. A poor practice would be to point your web server to the project root and expect to enter *public/...*, as the rest of your logic and the +framework are exposed. + +**Please** read the user guide for a better explanation of how CI4 works! + +## Repository Management + +We use GitHub issues, in our main repository, to track **BUGS** and to track approved **DEVELOPMENT** work packages. +We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss +FEATURE REQUESTS. + +This repository is a "distribution" one, built by our release preparation script. +Problems with it can be raised on our forum, or as issues in the main repository. + +## Contributing + +We welcome contributions from the community. + +Please read the [*Contributing to CodeIgniter*](https://github.com/codeigniter4/CodeIgniter4/blob/develop/CONTRIBUTING.md) section in the development repository. + +## Server Requirements + +PHP version 7.4 or higher is required, with the following extensions installed: + +- [intl](http://php.net/manual/en/intl.requirements.php) +- [mbstring](http://php.net/manual/en/mbstring.installation.php) + +> **Warning** +> The end of life date for PHP 7.4 was November 28, 2022. If you are +> still using PHP 7.4, you should upgrade immediately. The end of life date +> for PHP 8.0 will be November 26, 2023. + +Additionally, make sure that the following extensions are enabled in your PHP: + +- json (enabled by default - don't turn it off) +- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) if you plan to use MySQL +- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library diff --git a/app/Config/App.php b/app/Config/App.php index 775b5ca3..ec06b887 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -16,7 +16,7 @@ class App extends BaseConfig * * http://example.com/ */ - public string $baseURL = 'http://localhost:8080/'; + public string $baseURL = ''; /** * Allowed Hostnames in the Site URL other than the hostname in the baseURL. @@ -27,8 +27,7 @@ class App extends BaseConfig * 'http://accounts.example.com/': * ['media.example.com', 'accounts.example.com'] * - * @var string[] - * @phpstan-var list + * @var list */ public array $allowedHostnames = []; @@ -41,7 +40,7 @@ class App extends BaseConfig * something else. If you are using mod_rewrite to remove the page set this * variable so that it is blank. */ - public string $indexPage = ''; + public string $indexPage = 'index.php'; /** * -------------------------------------------------------------------------- diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php old mode 100644 new mode 100755 diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php index aa8099a4..a868447a 100644 --- a/app/Config/Boot/development.php +++ b/app/Config/Boot/development.php @@ -10,7 +10,7 @@ | | If you set 'display_errors' to '1', CI4's detailed error report will show. */ -error_reporting(-1); +error_reporting(E_ALL); ini_set('display_errors', '1'); /* diff --git a/app/Config/Boot/testing.php b/app/Config/Boot/testing.php index e84670e9..40b6ca83 100644 --- a/app/Config/Boot/testing.php +++ b/app/Config/Boot/testing.php @@ -14,7 +14,7 @@ | make sure they don't make it to production. And save us hours of | painful debugging. */ -error_reporting(-1); +error_reporting(E_ALL); ini_set('display_errors', '1'); /* diff --git a/app/Config/Cache.php b/app/Config/Cache.php index 61cdd50d..b5b2cdc1 100644 --- a/app/Config/Cache.php +++ b/app/Config/Cache.php @@ -158,8 +158,7 @@ class Cache extends BaseConfig * This is an array of cache engine alias' and class names. Only engines * that are listed here are allowed to be used. * - * @var array - * @phpstan-var array> + * @var array> */ public array $validHandlers = [ 'dummy' => DummyHandler::class, diff --git a/app/Config/DocTypes.php b/app/Config/DocTypes.php old mode 100644 new mode 100755 diff --git a/app/Config/Email.php b/app/Config/Email.php index 01b805a5..4dce650b 100644 --- a/app/Config/Email.php +++ b/app/Config/Email.php @@ -26,7 +26,7 @@ class Email extends BaseConfig public string $mailPath = '/usr/sbin/sendmail'; /** - * SMTP Server Address + * SMTP Server Hostname */ public string $SMTPHost = ''; diff --git a/app/Config/Kint.php b/app/Config/Kint.php index cc8b5459..117e66d8 100644 --- a/app/Config/Kint.php +++ b/app/Config/Kint.php @@ -27,8 +27,7 @@ class Kint extends BaseConfig */ /** - * @var array - * @phpstan-var list|ConstructablePluginInterface> + * @var list|ConstructablePluginInterface>|null */ public $plugins; @@ -46,14 +45,12 @@ class Kint extends BaseConfig public int $richSort = AbstractRenderer::SORT_FULL; /** - * @var array - * @phpstan-var array> + * @var array>|null */ public $richObjectPlugins; /** - * @var array - * @phpstan-var array> + * @var array>|null */ public $richTabPlugins; diff --git a/app/Config/Modules.php b/app/Config/Modules.php index f84580c8..8d4bf565 100644 --- a/app/Config/Modules.php +++ b/app/Config/Modules.php @@ -58,7 +58,7 @@ class Modules extends BaseModules * ], * ] * - * @var array + * @var array{only?: list, exclude?: list} */ public $composerPackages = []; @@ -72,7 +72,7 @@ class Modules extends BaseModules * * If it is not listed, only the base application elements will be used. * - * @var string[] + * @var list */ public $aliases = [ 'events', diff --git a/app/Config/Publisher.php b/app/Config/Publisher.php index 47475112..bf03be1d 100644 --- a/app/Config/Publisher.php +++ b/app/Config/Publisher.php @@ -19,7 +19,7 @@ class Publisher extends BasePublisher * result in a PublisherException. Files that do no fit the * pattern will cause copy/merge to fail. * - * @var array + * @var array */ public $restrictions = [ ROOTPATH => '*', diff --git a/app/Config/Session.php b/app/Config/Session.php index ea834805..e077df64 100644 --- a/app/Config/Session.php +++ b/app/Config/Session.php @@ -19,7 +19,7 @@ class Session extends BaseConfig * - `CodeIgniter\Session\Handlers\MemcachedHandler` * - `CodeIgniter\Session\Handlers\RedisHandler` * - * @phpstan-var class-string + * @var class-string */ public string $driver = FileHandler::class; diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php index 489a8b91..056d4170 100644 --- a/app/Controllers/Home.php +++ b/app/Controllers/Home.php @@ -13,6 +13,7 @@ class Home extends PublicController { echo base_url(); //echo ($_SERVER['SERVER_PORT'] == 443 ? 'https' : 'http') . "://{$_SERVER['SERVER_NAME']}".str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']); + return view('welcome_message'); return view('test'); } diff --git a/app/Controllers/LoginController.php b/app/Controllers/LoginController.php index 49e73583..6a4761a9 100644 --- a/app/Controllers/LoginController.php +++ b/app/Controllers/LoginController.php @@ -18,8 +18,8 @@ class LoginController extends BaseController public function login() { - $this->session->set('isLoggedIn',true); - $this->session->set('userid','10'); + // $this->session->set('isLoggedIn',true); + // $this->session->set('userid','10'); return view('login'); } diff --git a/app/Controllers/UserController.php b/app/Controllers/UserController.php index 5c9fba87..4c56077a 100644 --- a/app/Controllers/UserController.php +++ b/app/Controllers/UserController.php @@ -46,7 +46,7 @@ class UserController extends AdminController // Get the ID of the inserted record $insertedId = $userModel->insertID(); - return view('userlist'); + redirect('user/list'); } @@ -87,9 +87,9 @@ class UserController extends AdminController // Fetch all users from the 'users' table $users = $userModel - ->join('users as updater', 'updater.id = users.updated_by', 'left') - ->join('users as creator', 'creator.id = users.created_by', 'left') - ->select('users.*, updater.username as updated_by_name,creator.username as created_by_name'); // Assuming + ->join('users3 as updater', 'updater.id = users3.updated_by', 'left') + ->join('users3 as creator', 'creator.id = users3.created_by', 'left') + ->select('users3.*, updater.username as updated_by_name,creator.username as created_by_name'); // Assuming if(!empty($userid) && is_numeric($userid)) { $result = $users->find($userid); diff --git a/app/Filters/AuthMVC.php b/app/Filters/AuthMVC.php index 13f0f5fe..16618cde 100644 --- a/app/Filters/AuthMVC.php +++ b/app/Filters/AuthMVC.php @@ -11,7 +11,7 @@ class AuthMVC implements FilterInterface { if (!check_session()) { - return redirect()->to(site_url('login')); + return redirect()->to(base_url('login')); } } diff --git a/app/Models/User.php b/app/Models/User.php index 1ab18fa1..9a884bb5 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,7 +6,7 @@ use CodeIgniter\Model; class User extends Model { protected $DBGroup = 'default'; - protected $table = 'users'; + protected $table = 'users3'; protected $primaryKey = 'id'; protected $useAutoIncrement = true; protected $insertID = 0; diff --git a/app/Views/user.php b/app/Views/user.php index cb799615..536e6d92 100644 --- a/app/Views/user.php +++ b/app/Views/user.php @@ -106,28 +106,28 @@ span.psw {

New/Edit User

-
method="post"> + method="post"> - > - required>
+ +
- required>
+
- required>
+
- required>
+
- >
+

- >
+
diff --git a/composer.json b/composer.json index f93daa27..891bff55 100644 --- a/composer.json +++ b/composer.json @@ -18,9 +18,9 @@ "psr/log": "^1.1" }, "require-dev": { - "codeigniter/coding-standard": "^1.5", + "codeigniter/coding-standard": "^1.7", "fakerphp/faker": "^1.9", - "friendsofphp/php-cs-fixer": "3.13.0", + "friendsofphp/php-cs-fixer": "^3.47.1", "kint-php/kint": "^5.0.4", "mikey179/vfsstream": "^1.6", "nexusphp/cs-config": "^3.6", @@ -43,6 +43,7 @@ "ext-readline": "Improves CLI::input() usability", "ext-redis": "If you use Cache class RedisHandler", "ext-simplexml": "If you format XML", + "ext-sodium": "If you use Encryption SodiumHandler", "ext-sqlite3": "If you use SQLite3", "ext-sqlsrv": "If you use SQL Server", "ext-xdebug": "If you use CIUnitTestCase::assertHeaderEmitted()" diff --git a/public/index.php b/public/index.php index 75a69d2b..3596f8a8 100644 --- a/public/index.php +++ b/public/index.php @@ -14,6 +14,9 @@ if (version_compare(PHP_VERSION, $minPhpVersion, '<')) { // Path to the front controller (this file) define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR); +// echo FCPATH; +// echo getcwd(); +// die(); // Ensure the current directory is pointing to the front controller's directory if (getcwd() . DIRECTORY_SEPARATOR !== FCPATH) { @@ -35,6 +38,7 @@ require FCPATH . '../app/Config/Paths.php'; // ^^^ Change this line if you move your application folder $paths = new Config\Paths(); +// print_r($paths); // Location of the framework bootstrap file. require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php'; @@ -43,12 +47,11 @@ require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstra require_once SYSTEMPATH . 'Config/DotEnv.php'; (new CodeIgniter\Config\DotEnv(ROOTPATH))->load(); - // Define ENVIRONMENT if (! defined('ENVIRONMENT')) { - define('ENVIRONMENT', env('CI_ENVIRONMENT', 'production')); + define('ENVIRONMENT', env('CI_ENVIRONMENT', 'development')); } -// init_set('display_errors', 1); +error_reporting(E_ALL ^ E_NOTICE); // Load Config Cache // $factoriesCache = new \CodeIgniter\Cache\FactoriesCache(); // $factoriesCache->load('config'); @@ -63,7 +66,7 @@ if (! defined('ENVIRONMENT')) { * the application run, and does all the dirty work to get * the pieces all working together. */ - +// echo ENVIRONMENT; $app = Config\Services::codeigniter(); $app->initialize(); $context = is_cli() ? 'php-cli' : 'web'; @@ -76,7 +79,7 @@ $app->setContext($context); * Now that everything is set up, it's time to actually fire * up the engines and make this app do its thang. */ - +//echo 'run';die('sample'); $app->run(); // Save Config Cache diff --git a/spark b/spark old mode 100644 new mode 100755 index 2ea79d5c..9daa4403 --- a/spark +++ b/spark @@ -39,7 +39,7 @@ if (version_compare(PHP_VERSION, $minPhpVersion, '<')) { } // We want errors to be shown when using it from the CLI. -error_reporting(-1); +error_reporting(E_ALL); ini_set('display_errors', '1'); /** diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 5a350007..3cf05359 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -94,7 +94,7 @@ trait ResponseTrait $output = null; $this->format($data); } else { - $status = empty($status) ? 200 : $status; + $status ??= 200; $output = $this->format($data); } @@ -318,7 +318,7 @@ trait ResponseTrait // Determine correct response type through content negotiation if not explicitly declared if ( - (empty($this->format) || ! in_array($this->format, ['json', 'xml'], true)) + ! in_array($this->format, ['json', 'xml'], true) && $this->request instanceof IncomingRequest ) { $mime = $this->request->negotiate( diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index ecfd92b5..ca886fb5 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -75,8 +75,7 @@ class Autoloader /** * Stores files as a list. * - * @var string[] - * @phpstan-var list + * @var list */ protected $files = []; @@ -84,8 +83,7 @@ class Autoloader * Stores helper list. * Always load the URL helper, it should be used in most apps. * - * @var string[] - * @phpstan-var list + * @var list */ protected $helpers = ['url']; @@ -119,7 +117,7 @@ class Autoloader $this->files = $config->files; } - if (isset($config->helpers)) { // @phpstan-ignore-line + if (isset($config->helpers)) { $this->helpers = [...$this->helpers, ...$config->helpers]; } @@ -185,8 +183,7 @@ class Autoloader /** * Registers namespaces with the autoloader. * - * @param array|string>|string $namespace - * @phpstan-param array|string>|string $namespace + * @param array|string>|string $namespace * * @return $this */ diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 744567a3..c5cf8e18 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -35,14 +35,14 @@ class FileLocator * Attempts to locate a file by examining the name for a namespace * and looking through the PSR-4 namespaced files that we know about. * - * @param string $file The relative file path or namespaced file to - * locate. If not namespaced, search in the app - * folder. - * @param string|null $folder The folder within the namespace that we should - * look for the file. If $file does not contain - * this value, it will be appended to the namespace - * folder. - * @param string $ext The file extension the file should have. + * @param string $file The relative file path or namespaced file to + * locate. If not namespaced, search in the app + * folder. + * @param non-empty-string|null $folder The folder within the namespace that we should + * look for the file. If $file does not contain + * this value, it will be appended to the namespace + * folder. + * @param string $ext The file extension the file should have. * * @return false|string The path to the file, or false if not found. */ @@ -51,7 +51,7 @@ class FileLocator $file = $this->ensureExt($file, $ext); // Clears the folder name if it is at the beginning of the filename - if (! empty($folder) && strpos($file, $folder) === 0) { + if ($folder !== null && strpos($file, $folder) === 0) { $file = substr($file, strlen($folder . '/')); } @@ -67,7 +67,7 @@ class FileLocator $segments = explode('\\', $file); // The first segment will be empty if a slash started the filename. - if (empty($segments[0])) { + if ($segments[0] === '') { unset($segments[0]); } @@ -89,7 +89,7 @@ class FileLocator } // if no namespaces matched then quit - if (empty($paths)) { + if ($paths === []) { return false; } @@ -101,7 +101,7 @@ class FileLocator // If we have a folder name, then the calling function // expects this file to be within that folder, like 'Views', // or 'libraries'. - if (! empty($folder) && strpos($path . $filename, '/' . $folder . '/') === false) { + if ($folder !== null && strpos($path . $filename, '/' . $folder . '/') === false) { $path .= trim($folder, '/') . '/'; } @@ -119,6 +119,10 @@ class FileLocator */ public function getClassname(string $file): string { + if (is_dir($file)) { + return ''; + } + $php = file_get_contents($file); $tokens = token_get_all($php); $dlm = false; @@ -150,7 +154,7 @@ class FileLocator } } - if (empty($className)) { + if ($className === '') { return ''; } @@ -192,7 +196,7 @@ class FileLocator } } - if (! $prioritizeApp && ! empty($appPaths)) { + if (! $prioritizeApp && $appPaths !== []) { $foundPaths = [...$foundPaths, ...$appPaths]; } @@ -268,7 +272,7 @@ class FileLocator foreach ($this->getNamespaces() as $namespace) { $namespace['path'] = realpath($namespace['path']) ?: $namespace['path']; - if (empty($namespace['path'])) { + if ($namespace['path'] === '') { continue; } @@ -301,7 +305,7 @@ class FileLocator */ public function listFiles(string $path): array { - if (empty($path)) { + if ($path === '') { return []; } @@ -318,7 +322,7 @@ class FileLocator $tempFiles = get_filenames($fullPath, true, false, false); - if (! empty($tempFiles)) { + if ($tempFiles !== []) { $files = array_merge($files, $tempFiles); } } @@ -334,7 +338,7 @@ class FileLocator */ public function listNamespaceFiles(string $prefix, string $path): array { - if (empty($path) || empty($prefix)) { + if ($path === '' || ($prefix === '')) { return []; } @@ -352,7 +356,7 @@ class FileLocator $tempFiles = get_filenames($fullPath, true, false, false); - if (! empty($tempFiles)) { + if ($tempFiles !== []) { $files = array_merge($files, $tempFiles); } } @@ -364,11 +368,13 @@ class FileLocator * Checks the app folder to see if the file can be found. * Only for use with filenames that DO NOT include namespacing. * + * @param non-empty-string|null $folder + * * @return false|string The path to the file, or false if not found. */ protected function legacyLocate(string $file, ?string $folder = null) { - $path = APPPATH . (empty($folder) ? $file : $folder . '/' . $file); + $path = APPPATH . ($folder === null ? $file : $folder . '/' . $file); $path = realpath($path) ?: $path; if (is_file($path)) { diff --git a/system/BaseModel.php b/system/BaseModel.php index c27afd02..6a6f0dff 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -41,6 +41,16 @@ use stdClass; * - ensure validation is run against objects when saving items * - process various callbacks * - allow intermingling calls to the db connection + * + * @phpstan-type row_array array + * @phpstan-type event_data_beforeinsert array{data: row_array} + * @phpstan-type event_data_afterinsert array{id: int|string, data: row_array, result: bool} + * @phpstan-type event_data_beforefind array{id?: int|string, method: string, singleton: bool, limit?: int, offset?: int} + * @phpstan-type event_data_afterfind array{id: int|string|null|list, data: row_array|list|object|null, method: string, singleton: bool} + * @phpstan-type event_data_beforeupdate array{id: null|list, data: row_array} + * @phpstan-type event_data_afterupdate array{id: null|list, data: row_array|object, result: bool} + * @phpstan-type event_data_beforedelete array{id: null|list, purge: bool} + * @phpstan-type event_data_afterdelete array{id: null|list, data: null, purge: bool, result: bool} */ abstract class BaseModel { @@ -63,8 +73,7 @@ abstract class BaseModel * The Database connection group that * should be instantiated. * - * @var string|null - * @phpstan-var non-empty-string|null + * @var non-empty-string|null */ protected $DBGroup; @@ -391,11 +400,12 @@ abstract class BaseModel * Inserts data into the current database. * This method works only with dbCalls. * - * @param array $data Data + * @param array $row Row data + * @phpstan-param row_array $row * * @return bool */ - abstract protected function doInsert(array $data); + abstract protected function doInsert(array $row); /** * Compiles batch insert and runs the queries, validating each row prior. @@ -414,10 +424,11 @@ abstract class BaseModel * Updates a single record in the database. * This method works only with dbCalls. * - * @param array|int|string|null $id ID - * @param array|null $data Data + * @param array|int|string|null $id ID + * @param array|null $row Row data + * @phpstan-param row_array|null $row */ - abstract protected function doUpdate($id = null, $data = null): bool; + abstract protected function doUpdate($id = null, $row = null): bool; /** * Compiles an update and runs the query. @@ -469,12 +480,13 @@ abstract class BaseModel * Compiles a replace and runs the query. * This method works only with dbCalls. * - * @param array|null $data Data - * @param bool $returnSQL Set to true to return Query String + * @param array|null $row Row data + * @phpstan-param row_array|null $row + * @param bool $returnSQL Set to true to return Query String * * @return BaseResult|false|Query|string */ - abstract protected function doReplace(?array $data = null, bool $returnSQL = false); + abstract protected function doReplace(?array $row = null, bool $returnSQL = false); /** * Grabs the last error(s) that occurred from the Database connection. @@ -499,15 +511,16 @@ abstract class BaseModel * Public getter to return the id value using the idValue() method. * For example with SQL this will return $data->$this->primaryKey. * - * @param array|object $data + * @param array|object $row Row data + * @phpstan-param row_array|object $row * * @return array|int|string|null * * @todo: Make abstract in version 5.0 */ - public function getIdValue($data) + public function getIdValue($row) { - return $this->idValue($data); + return $this->idValue($row); } /** @@ -540,6 +553,7 @@ abstract class BaseModel * @param array|int|string|null $id One primary key or an array of primary keys * * @return array|object|null The resulting row of data, or null. + * @phpstan-return ($id is int|string ? row_array|object|null : list) */ public function find($id = null) { @@ -553,7 +567,7 @@ abstract class BaseModel 'singleton' => $singleton, ]); - if (! empty($eventData['returnData'])) { + if (isset($eventData['returnData']) && $eventData['returnData'] === true) { return $eventData['data']; } } @@ -615,7 +629,7 @@ abstract class BaseModel 'singleton' => false, ]); - if (! empty($eventData['returnData'])) { + if (isset($eventData['returnData']) && $eventData['returnData'] === true) { return $eventData['data']; } } @@ -653,7 +667,7 @@ abstract class BaseModel 'singleton' => true, ]); - if (! empty($eventData['returnData'])) { + if (isset($eventData['returnData']) && $eventData['returnData'] === true) { return $eventData['data']; } } @@ -682,20 +696,21 @@ abstract class BaseModel * you must ensure that the class will provide access to the class * variables, even if through a magic method. * - * @param array|object $data Data + * @param array|object $row Row data + * @phpstan-param row_array|object $row * * @throws ReflectionException */ - public function save($data): bool + public function save($row): bool { - if (empty($data)) { + if ((array) $row === []) { return true; } - if ($this->shouldUpdate($data)) { - $response = $this->update($this->getIdValue($data), $data); + if ($this->shouldUpdate($row)) { + $response = $this->update($this->getIdValue($row), $row); } else { - $response = $this->insert($data, false); + $response = $this->insert($row, false); if ($response !== false) { $response = true; @@ -709,11 +724,14 @@ abstract class BaseModel * This method is called on save to determine if entry have to be updated. * If this method returns false insert operation will be executed * - * @param array|object $data Data + * @param array|object $row Row data + * @phpstan-param row_array|object $row */ - protected function shouldUpdate($data): bool + protected function shouldUpdate($row): bool { - return ! empty($this->getIdValue($data)); + $id = $this->getIdValue($row); + + return ! ($id === null || $id === []); } /** @@ -730,14 +748,16 @@ abstract class BaseModel * Inserts data into the database. If an object is provided, * it will attempt to convert it to an array. * - * @param array|object|null $data Data - * @param bool $returnID Whether insert ID should be returned or not. + * @param array|object|null $row Row data + * @phpstan-param row_array|object|null $row + * @param bool $returnID Whether insert ID should be returned or not. * * @return bool|int|string insert ID or true on success. false on failure. + * @phpstan-return ($returnID is true ? int|string|false : bool) * * @throws ReflectionException */ - public function insert($data = null, bool $returnID = true) + public function insert($row = null, bool $returnID = true) { $this->insertID = 0; @@ -745,10 +765,10 @@ abstract class BaseModel $cleanValidationRules = $this->cleanValidationRules; $this->cleanValidationRules = false; - $data = $this->transformDataToArray($data, 'insert'); + $row = $this->transformDataToArray($row, 'insert'); // Validate data before saving. - if (! $this->skipValidation && ! $this->validate($data)) { + if (! $this->skipValidation && ! $this->validate($row)) { // Restore $cleanValidationRules $this->cleanValidationRules = $cleanValidationRules; @@ -760,26 +780,20 @@ abstract class BaseModel // Must be called first, so we don't // strip out created_at values. - $data = $this->doProtectFieldsForInsert($data); + $row = $this->doProtectFieldsForInsert($row); // doProtectFields() can further remove elements from - // $data so we need to check for empty dataset again - if (! $this->allowEmptyInserts && empty($data)) { + // $row, so we need to check for empty dataset again + if (! $this->allowEmptyInserts && $row === []) { throw DataException::forEmptyDataset('insert'); } // Set created_at and updated_at with same time $date = $this->setDate(); + $row = $this->setCreatedField($row, $date); + $row = $this->setUpdatedField($row, $date); - if ($this->useTimestamps && $this->createdField && ! array_key_exists($this->createdField, $data)) { - $data[$this->createdField] = $date; - } - - if ($this->useTimestamps && $this->updatedField && ! array_key_exists($this->updatedField, $data)) { - $data[$this->updatedField] = $date; - } - - $eventData = ['data' => $data]; + $eventData = ['data' => $row]; if ($this->tempAllowCallbacks) { $eventData = $this->trigger('beforeInsert', $eventData); @@ -809,13 +823,44 @@ abstract class BaseModel return $returnID ? $this->insertID : $result; } + /** + * Set datetime to created field. + * + * @phpstan-param row_array $row + * @param int|string $date timestamp or datetime string + */ + protected function setCreatedField(array $row, $date): array + { + if ($this->useTimestamps && $this->createdField !== '' && ! array_key_exists($this->createdField, $row)) { + $row[$this->createdField] = $date; + } + + return $row; + } + + /** + * Set datetime to updated field. + * + * @phpstan-param row_array $row + * @param int|string $date timestamp or datetime string + */ + protected function setUpdatedField(array $row, $date): array + { + if ($this->useTimestamps && $this->updatedField !== '' && ! array_key_exists($this->updatedField, $row)) { + $row[$this->updatedField] = $date; + } + + return $row; + } + /** * Compiles batch insert runs the queries, validating each row prior. * - * @param array|null $set an associative array of insert values - * @param bool|null $escape Whether to escape values - * @param int $batchSize The size of the batch to run - * @param bool $testing True means only number of records is returned, false will execute the query + * @param list|null $set an associative array of insert values + * @phpstan-param list|null $set + * @param bool|null $escape Whether to escape values + * @param int $batchSize The size of the batch to run + * @param bool $testing True means only number of records is returned, false will execute the query * * @return bool|int Number of rows inserted or FALSE on failure * @@ -829,7 +874,7 @@ abstract class BaseModel if (is_array($set)) { foreach ($set as &$row) { - // If $data is using a custom class with public or protected + // If $row is using a custom class with public or protected // properties representing the collection elements, we need to grab // them as an array. if (is_object($row) && ! $row instanceof stdClass) { @@ -857,14 +902,8 @@ abstract class BaseModel // Set created_at and updated_at with same time $date = $this->setDate(); - - if ($this->useTimestamps && $this->createdField && ! array_key_exists($this->createdField, $row)) { - $row[$this->createdField] = $date; - } - - if ($this->useTimestamps && $this->updatedField && ! array_key_exists($this->updatedField, $row)) { - $row[$this->updatedField] = $date; - } + $row = $this->setCreatedField($row, $date); + $row = $this->setUpdatedField($row, $date); } } @@ -899,11 +938,12 @@ abstract class BaseModel * it will attempt to convert it into an array. * * @param array|int|string|null $id - * @param array|object|null $data + * @param array|object|null $row Row data + * @phpstan-param row_array|object|null $row * * @throws ReflectionException */ - public function update($id = null, $data = null): bool + public function update($id = null, $row = null): bool { if (is_bool($id)) { throw new InvalidArgumentException('update(): argument #1 ($id) should not be boolean.'); @@ -913,30 +953,28 @@ abstract class BaseModel $id = [$id]; } - $data = $this->transformDataToArray($data, 'update'); + $row = $this->transformDataToArray($row, 'update'); // Validate data before saving. - if (! $this->skipValidation && ! $this->validate($data)) { + if (! $this->skipValidation && ! $this->validate($row)) { return false; } // Must be called first, so we don't // strip out updated_at values. - $data = $this->doProtectFields($data); + $row = $this->doProtectFields($row); // doProtectFields() can further remove elements from - // $data, so we need to check for empty dataset again - if (empty($data)) { + // $row, so we need to check for empty dataset again + if ($row === []) { throw DataException::forEmptyDataset('update'); } - if ($this->useTimestamps && $this->updatedField && ! array_key_exists($this->updatedField, $data)) { - $data[$this->updatedField] = $this->setDate(); - } + $row = $this->setUpdatedField($row, $this->setDate()); $eventData = [ 'id' => $id, - 'data' => $data, + 'data' => $row, ]; if ($this->tempAllowCallbacks) { @@ -961,7 +999,8 @@ abstract class BaseModel /** * Compiles an update and runs the query. * - * @param array|null $set An associative array of update values + * @param list|null $set an associative array of insert values + * @phpstan-param list|null $set * @param string|null $index The where key * @param int $batchSize The size of the batch to run * @param bool $returnSQL True means SQL is returned, false will execute the query @@ -975,7 +1014,7 @@ abstract class BaseModel { if (is_array($set)) { foreach ($set as &$row) { - // If $data is using a custom class with public or protected + // If $row is using a custom class with public or protected // properties representing the collection elements, we need to grab // them as an array. if (is_object($row) && ! $row instanceof stdClass) { @@ -1015,9 +1054,7 @@ abstract class BaseModel $row[$index] = $updateIndex; } - if ($this->useTimestamps && $this->updatedField && ! array_key_exists($this->updatedField, $row)) { - $row[$this->updatedField] = $this->setDate(); - } + $row = $this->setUpdatedField($row, $this->setDate()); } } @@ -1136,23 +1173,22 @@ abstract class BaseModel /** * Compiles a replace and runs the query. * - * @param array|null $data Data - * @param bool $returnSQL Set to true to return Query String + * @param array|null $row Row data + * @phpstan-param row_array|null $row + * @param bool $returnSQL Set to true to return Query String * * @return BaseResult|false|Query|string */ - public function replace(?array $data = null, bool $returnSQL = false) + public function replace(?array $row = null, bool $returnSQL = false) { // Validate data before saving. - if ($data && ! $this->skipValidation && ! $this->validate($data)) { + if (($row !== null) && ! $this->skipValidation && ! $this->validate($row)) { return false; } - if ($this->useTimestamps && $this->updatedField && ! array_key_exists($this->updatedField, (array) $data)) { - $data[$this->updatedField] = $this->setDate(); - } + $row = $this->setUpdatedField((array) $row, $this->setDate()); - return $this->doReplace($data, $returnSQL); + return $this->doReplace($row, $returnSQL); } /** @@ -1240,46 +1276,48 @@ abstract class BaseModel * Ensures that only the fields that are allowed to be updated are * in the data array. * - * Used by update() and updateBatch() to protect against mass assignment - * vulnerabilities. + * @used-by update() to protect against mass assignment vulnerabilities. + * @used-by updateBatch() to protect against mass assignment vulnerabilities. * - * @param array $data Data + * @param array $row Row data + * @phpstan-param row_array $row * * @throws DataException */ - protected function doProtectFields(array $data): array + protected function doProtectFields(array $row): array { if (! $this->protectFields) { - return $data; + return $row; } - if (empty($this->allowedFields)) { + if ($this->allowedFields === []) { throw DataException::forInvalidAllowedFields(static::class); } - foreach (array_keys($data) as $key) { + foreach (array_keys($row) as $key) { if (! in_array($key, $this->allowedFields, true)) { - unset($data[$key]); + unset($row[$key]); } } - return $data; + return $row; } /** * Ensures that only the fields that are allowed to be inserted are in * the data array. * - * Used by insert() and insertBatch() to protect against mass assignment - * vulnerabilities. + * @used-by insert() to protect against mass assignment vulnerabilities. + * @used-by insertBatch() to protect against mass assignment vulnerabilities. * - * @param array $data Data + * @param array $row Row data + * @phpstan-param row_array $row * * @throws DataException */ - protected function doProtectFieldsForInsert(array $data): array + protected function doProtectFieldsForInsert(array $row): array { - return $this->doProtectFields($data); + return $this->doProtectFields($row); } /** @@ -1464,35 +1502,36 @@ abstract class BaseModel } /** - * Validate the data against the validation rules (or the validation group) + * Validate the row data against the validation rules (or the validation group) * specified in the class property, $validationRules. * - * @param array|object $data Data + * @param array|object $row Row data + * @phpstan-param row_array|object $row */ - public function validate($data): bool + public function validate($row): bool { $rules = $this->getValidationRules(); - if ($this->skipValidation || empty($rules) || empty($data)) { + // Validation requires array, so cast away. + if (is_object($row)) { + $row = (array) $row; + } + + if ($this->skipValidation || $rules === [] || $row === []) { return true; } - // Validation requires array, so cast away. - if (is_object($data)) { - $data = (array) $data; - } - - $rules = $this->cleanValidationRules ? $this->cleanValidationRules($rules, $data) : $rules; + $rules = $this->cleanValidationRules ? $this->cleanValidationRules($rules, $row) : $rules; // If no data existed that needs validation // our job is done here. - if (empty($rules)) { + if ($rules === []) { return true; } $this->validation->reset()->setRules($rules, $this->validationMessages); - return $this->validation->run($data, null, $this->DBGroup); + return $this->validation->run($row, null, $this->DBGroup); } /** @@ -1536,17 +1575,18 @@ abstract class BaseModel * currently so that rules don't block updating when only updating * a partial row. * - * @param array $rules Array containing field name and rule - * @param array|null $data Data + * @param array $rules Array containing field name and rule + * @param array $row Row data (@TODO Remove null in param type) + * @phpstan-param row_array $row */ - protected function cleanValidationRules(array $rules, ?array $data = null): array + protected function cleanValidationRules(array $rules, ?array $row = null): array { - if (empty($data)) { + if ($row === null || $row === []) { return []; } foreach (array_keys($rules) as $field) { - if (! array_key_exists($field, $data)) { + if (! array_key_exists($field, $row)) { unset($rules[$field]); } } @@ -1594,7 +1634,7 @@ abstract class BaseModel protected function trigger(string $event, array $eventData) { // Ensure it's a valid event - if (! isset($this->{$event}) || empty($this->{$event})) { + if (! isset($this->{$event}) || $this->{$event} === []) { return $eventData; } @@ -1644,52 +1684,63 @@ abstract class BaseModel * This method uses objectToRawArray() internally and does conversion * to string on all Time instances * - * @param object|string $data Data - * @param bool $onlyChanged Only Changed Property - * @param bool $recursive If true, inner entities will be cast as array as well + * @param object $object Object + * @param bool $onlyChanged Only Changed Property + * @param bool $recursive If true, inner entities will be cast as array as well * - * @return array Array + * @return array * * @throws ReflectionException */ - protected function objectToArray($data, bool $onlyChanged = true, bool $recursive = false): array + protected function objectToArray($object, bool $onlyChanged = true, bool $recursive = false): array { - $properties = $this->objectToRawArray($data, $onlyChanged, $recursive); - - assert(is_array($properties)); + $properties = $this->objectToRawArray($object, $onlyChanged, $recursive); // Convert any Time instances to appropriate $dateFormat - if ($properties !== []) { - $properties = array_map(function ($value) { - if ($value instanceof Time) { - return $this->timeToDate($value); - } + return $this->timeToString($properties); + } - return $value; - }, $properties); + /** + * Convert any Time instances to appropriate $dateFormat. + * + * @param array $properties + * + * @return array + */ + protected function timeToString(array $properties): array + { + if ($properties === []) { + return []; } - return $properties; + return array_map(function ($value) { + if ($value instanceof Time) { + return $this->timeToDate($value); + } + + return $value; + }, $properties); } /** * Takes a class and returns an array of its public and protected * properties as an array with raw values. * - * @param object|string $data Data - * @param bool $onlyChanged Only Changed Property - * @param bool $recursive If true, inner entities will be casted as array as well + * @param object $object Object + * @param bool $onlyChanged Only Changed Property + * @param bool $recursive If true, inner entities will be casted as array as well * - * @return array|null Array + * @return array * * @throws ReflectionException */ - protected function objectToRawArray($data, bool $onlyChanged = true, bool $recursive = false): ?array + protected function objectToRawArray($object, bool $onlyChanged = true, bool $recursive = false): array { - if (method_exists($data, 'toRawArray')) { - $properties = $data->toRawArray($onlyChanged, $recursive); + // Entity::toRawArray() returns array. + if (method_exists($object, 'toRawArray')) { + $properties = $object->toRawArray($onlyChanged, $recursive); } else { - $mirror = new ReflectionClass($data); + $mirror = new ReflectionClass($object); $props = $mirror->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED); $properties = []; @@ -1699,7 +1750,7 @@ abstract class BaseModel foreach ($props as $prop) { // Must make protected values accessible. $prop->setAccessible(true); - $properties[$prop->getName()] = $prop->getValue($data); + $properties[$prop->getName()] = $prop->getValue($object); } } @@ -1709,47 +1760,48 @@ abstract class BaseModel /** * Transform data to array. * - * @param array|object|null $data Data - * @param string $type Type of data (insert|update) + * @param array|object|null $row Row data + * @phpstan-param row_array|object|null $row + * @param string $type Type of data (insert|update) * * @throws DataException * @throws InvalidArgumentException * @throws ReflectionException */ - protected function transformDataToArray($data, string $type): array + protected function transformDataToArray($row, string $type): array { if (! in_array($type, ['insert', 'update'], true)) { throw new InvalidArgumentException(sprintf('Invalid type "%s" used upon transforming data to array.', $type)); } - if (! $this->allowEmptyInserts && empty($data)) { + if (! $this->allowEmptyInserts && ($row === null || (array) $row === [])) { throw DataException::forEmptyDataset($type); } - // If $data is using a custom class with public or protected + // If $row is using a custom class with public or protected // properties representing the collection elements, we need to grab // them as an array. - if (is_object($data) && ! $data instanceof stdClass) { + if (is_object($row) && ! $row instanceof stdClass) { // If it validates with entire rules, all fields are needed. $onlyChanged = ($this->skipValidation === false && $this->cleanValidationRules === false) ? false : ($type === 'update'); - $data = $this->objectToArray($data, $onlyChanged, true); + $row = $this->objectToArray($row, $onlyChanged, true); } // If it's still a stdClass, go ahead and convert to // an array so doProtectFields and other model methods // don't have to do special checks. - if (is_object($data)) { - $data = (array) $data; + if (is_object($row)) { + $row = (array) $row; } - // If it's still empty here, means $data is no change or is empty object - if (! $this->allowEmptyInserts && empty($data)) { + // If it's still empty here, means $row is no change or is empty object + if (! $this->allowEmptyInserts && ($row === null || $row === [])) { throw DataException::forEmptyDataset($type); } - return $data; + return $row; } /** @@ -1829,7 +1881,7 @@ abstract class BaseModel $replacements['{' . $key . '}'] = $value; } - if (! empty($replacements)) { + if ($replacements !== []) { foreach ($rules as &$rule) { if (is_array($rule)) { foreach ($rule as &$row) { diff --git a/system/CLI/BaseCommand.php b/system/CLI/BaseCommand.php index c4448d56..8101283b 100644 --- a/system/CLI/BaseCommand.php +++ b/system/CLI/BaseCommand.php @@ -19,14 +19,14 @@ use Throwable; /** * BaseCommand is the base class used in creating CLI commands. * - * @property array $arguments - * @property Commands $commands - * @property string $description - * @property string $group - * @property LoggerInterface $logger - * @property string $name - * @property array $options - * @property string $usage + * @property array $arguments + * @property Commands $commands + * @property string $description + * @property string $group + * @property LoggerInterface $logger + * @property string $name + * @property array $options + * @property string $usage */ abstract class BaseCommand { @@ -62,14 +62,14 @@ abstract class BaseCommand /** * the Command's options description * - * @var array + * @var array */ protected $options = []; /** * the Command's Arguments description * - * @var array + * @var array */ protected $arguments = []; @@ -143,7 +143,7 @@ abstract class BaseCommand } else { $usage = $this->name; - if (! empty($this->arguments)) { + if ($this->arguments !== []) { $usage .= ' [arguments]'; } } @@ -156,7 +156,7 @@ abstract class BaseCommand CLI::write($this->setPad($this->description, 0, 0, 2)); } - if (! empty($this->arguments)) { + if ($this->arguments !== []) { CLI::newLine(); CLI::write(lang('CLI.helpArguments'), 'yellow'); $length = max(array_map('strlen', array_keys($this->arguments))); @@ -166,7 +166,7 @@ abstract class BaseCommand } } - if (! empty($this->options)) { + if ($this->options !== []) { CLI::newLine(); CLI::write(lang('CLI.helpOptions'), 'yellow'); $length = max(array_map('strlen', array_keys($this->options))); diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 50a846a2..9a50e403 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -252,7 +252,7 @@ class CLI unset($opts[0]); - if (empty($opts)) { + if ($opts === []) { $extraOutput = $extraOutputDefault; } else { $extraOutput = '[' . $extraOutputDefault . ', ' . implode(', ', $opts) . ']'; @@ -324,7 +324,7 @@ class CLI $opts = $options; unset($opts[0]); - if (empty($opts)) { + if ($opts === []) { $extraOutput = $extraOutputDefault; } else { $optsKey = []; @@ -849,7 +849,7 @@ class CLI */ public static function wrap(?string $string = null, int $max = 0, int $padLeft = 0): string { - if (empty($string)) { + if ($string === null || $string === '') { return ''; } @@ -1000,7 +1000,7 @@ class CLI */ public static function getOptionString(bool $useLongOpts = false, bool $trim = false): string { - if (empty(static::$options)) { + if (static::$options === []) { return ''; } @@ -1041,7 +1041,7 @@ class CLI $tableRows = []; // We need only indexes and not keys - if (! empty($thead)) { + if ($thead !== []) { $tableRows[] = array_values($thead); } @@ -1115,7 +1115,7 @@ class CLI $table .= '| ' . implode(' | ', $tableRows[$row]) . ' |' . PHP_EOL; // Set the thead and table borders-bottom - if (($row === 0 && ! empty($thead)) || ($row + 1 === $totalRows)) { + if (($row === 0 && $thead !== []) || ($row + 1 === $totalRows)) { $table .= $cols . PHP_EOL; } } diff --git a/system/Cache/CacheFactory.php b/system/Cache/CacheFactory.php index 95e07ef8..6402b002 100644 --- a/system/Cache/CacheFactory.php +++ b/system/Cache/CacheFactory.php @@ -40,6 +40,9 @@ class CacheFactory /** * Attempts to create the desired cache handler, based upon the * + * @param non-empty-string|null $handler + * @param non-empty-string|null $backup + * * @return CacheInterface */ public static function getHandler(Cache $config, ?string $handler = null, ?string $backup = null) @@ -52,8 +55,8 @@ class CacheFactory throw CacheException::forNoBackup(); } - $handler = ! empty($handler) ? $handler : $config->handler; - $backup = ! empty($backup) ? $backup : $config->backupHandler; + $handler ??= $config->handler; + $backup ??= $config->backupHandler; if (! array_key_exists($handler, $config->validHandlers) || ! array_key_exists($backup, $config->validHandlers)) { throw CacheException::forHandlerNotFound(); diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index c12b330b..061a4a66 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -76,10 +76,9 @@ abstract class BaseHandler implements CacheInterface /** * Get an item from the cache, or execute the given Closure and store the result. * - * @param string $key Cache item name - * @param int $ttl Time to live - * @param Closure $callback Callback return value - * @phpstan-param Closure(): mixed $callback + * @param string $key Cache item name + * @param int $ttl Time to live + * @param Closure(): mixed $callback Callback return value * * @return array|bool|float|int|object|string|null */ diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index e22543d2..0a39c361 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -150,8 +150,8 @@ class FileHandler extends BaseHandler */ public function increment(string $key, int $offset = 1) { - $key = static::validateKey($key, $this->prefix); - $tmp = $this->getItem($key); + $prefixedKey = static::validateKey($key, $this->prefix); + $tmp = $this->getItem($prefixedKey); if ($tmp === false) { $tmp = ['data' => 0, 'ttl' => 60]; @@ -222,8 +222,7 @@ class FileHandler extends BaseHandler * Does the heavy lifting of actually retrieving the file and * verifying it's age. * - * @return array|false - * @phpstan-return array{data: mixed, ttl: int, time: int}|false + * @return array{data: mixed, ttl: int, time: int}|false */ protected function getItem(string $filename) { diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index 42b44cd9..953de2dc 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -245,6 +245,7 @@ class RedisHandler extends BaseHandler if ($value !== null) { $time = Time::now()->getTimestamp(); $ttl = $this->redis->ttl(static::validateKey($key, $this->prefix)); + assert(is_int($ttl)); return [ 'expire' => $ttl > 0 ? $time + $ttl : null, diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 9f9f6e17..46eb419f 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -54,7 +54,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.4.3'; + public const CI_VERSION = '4.4.5'; /** * App startup time. @@ -108,7 +108,7 @@ class CodeIgniter /** * Controller to use. * - * @var Closure|string + * @var (Closure(mixed...): ResponseInterface|string)|string|null */ protected $controller; @@ -138,7 +138,7 @@ class CodeIgniter /** * Request path to use. * - * @var string + * @var string|null * * @deprecated No longer used. */ @@ -302,7 +302,7 @@ class CodeIgniter Kint::$display_called_from = $config->displayCalledFrom; Kint::$expanded = $config->expanded; - if (! empty($config->plugins) && is_array($config->plugins)) { + if (isset($config->plugins) && is_array($config->plugins)) { Kint::$plugins = $config->plugins; } @@ -315,10 +315,10 @@ class CodeIgniter RichRenderer::$theme = $config->richTheme; RichRenderer::$folder = $config->richFolder; RichRenderer::$sort = $config->richSort; - if (! empty($config->richObjectPlugins) && is_array($config->richObjectPlugins)) { + if (isset($config->richObjectPlugins) && is_array($config->richObjectPlugins)) { RichRenderer::$value_plugins = $config->richObjectPlugins; } - if (! empty($config->richTabPlugins) && is_array($config->richTabPlugins)) { + if (isset($config->richTabPlugins) && is_array($config->richTabPlugins)) { RichRenderer::$tab_plugins = $config->richTabPlugins; } @@ -848,11 +848,10 @@ class CodeIgniter */ protected function determinePath() { - if (! empty($this->path)) { - return $this->path; - } - - return method_exists($this->request, 'getPath') ? $this->request->getPath() : $this->request->getUri()->getPath(); + return $this->path ?? + (method_exists($this->request, 'getPath') + ? $this->request->getPath() + : $this->request->getUri()->getPath()); } /** @@ -892,7 +891,7 @@ class CodeIgniter } // No controller specified - we don't know what to do now. - if (empty($this->controller)) { + if (! isset($this->controller)) { throw PageNotFoundException::forEmptyController(); } @@ -1091,7 +1090,7 @@ class CodeIgniter $method = $this->request->getPost('_method'); - if (empty($method)) { + if ($method === null) { return; } diff --git a/system/Commands/Cache/ClearCache.php b/system/Commands/Cache/ClearCache.php index b903425b..79fab9df 100644 --- a/system/Commands/Cache/ClearCache.php +++ b/system/Commands/Cache/ClearCache.php @@ -52,7 +52,7 @@ class ClearCache extends BaseCommand /** * the Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'driver' => 'The cache driver to use', diff --git a/system/Commands/Database/Migrate.php b/system/Commands/Database/Migrate.php index bcf71cad..11933517 100644 --- a/system/Commands/Database/Migrate.php +++ b/system/Commands/Database/Migrate.php @@ -53,7 +53,7 @@ class Migrate extends BaseCommand /** * the Command's Options * - * @var array + * @var array */ protected $options = [ '-n' => 'Set migration namespace', diff --git a/system/Commands/Database/MigrateRefresh.php b/system/Commands/Database/MigrateRefresh.php index 689c7542..f683219e 100644 --- a/system/Commands/Database/MigrateRefresh.php +++ b/system/Commands/Database/MigrateRefresh.php @@ -52,7 +52,7 @@ class MigrateRefresh extends BaseCommand /** * the Command's Options * - * @var array + * @var array */ protected $options = [ '-n' => 'Set migration namespace', diff --git a/system/Commands/Database/MigrateRollback.php b/system/Commands/Database/MigrateRollback.php index c104a81f..2df223c0 100644 --- a/system/Commands/Database/MigrateRollback.php +++ b/system/Commands/Database/MigrateRollback.php @@ -54,7 +54,7 @@ class MigrateRollback extends BaseCommand /** * the Command's Options * - * @var array + * @var array */ protected $options = [ '-b' => 'Specify a batch to roll back to; e.g. "3" to return to batch #3', diff --git a/system/Commands/Database/ShowTableInfo.php b/system/Commands/Database/ShowTableInfo.php index 07aa60c9..4c8f1bfd 100644 --- a/system/Commands/Database/ShowTableInfo.php +++ b/system/Commands/Database/ShowTableInfo.php @@ -84,7 +84,7 @@ class ShowTableInfo extends BaseCommand ]; /** - * @phpstan-var list> Table Data. + * @var list> Table Data. */ private array $tbody; @@ -125,7 +125,7 @@ class ShowTableInfo extends BaseCommand $limitRows = (int) ($params['limit-rows'] ?? 10); $limitFieldValue = (int) ($params['limit-field-value'] ?? 15); - if (! in_array($tableName, $tables, true)) { + while (! in_array($tableName, $tables, true)) { $tableNameNo = CLI::promptByKey( ['Here is the list of your database tables:', 'Which table do you want to see?'], $tables, @@ -133,7 +133,7 @@ class ShowTableInfo extends BaseCommand ); CLI::newLine(); - $tableName = $tables[$tableNameNo]; + $tableName = $tables[$tableNameNo] ?? null; } if (array_key_exists('metadata', $params)) { diff --git a/system/Commands/Encryption/GenerateKey.php b/system/Commands/Encryption/GenerateKey.php index 5837bfb0..419ae6ef 100644 --- a/system/Commands/Encryption/GenerateKey.php +++ b/system/Commands/Encryption/GenerateKey.php @@ -52,7 +52,7 @@ class GenerateKey extends BaseCommand /** * The command's options * - * @var array + * @var array */ protected $options = [ '--force' => 'Force overwrite existing key in `.env` file.', diff --git a/system/Commands/Generators/CellGenerator.php b/system/Commands/Generators/CellGenerator.php index cdb819d8..358bacc0 100644 --- a/system/Commands/Generators/CellGenerator.php +++ b/system/Commands/Generators/CellGenerator.php @@ -40,7 +40,7 @@ class CellGenerator extends BaseCommand * * @var string */ - protected $description = 'Generates a new Cell file and its view.'; + protected $description = 'Generates a new Controlled Cell file and its view.'; /** * The Command's Usage @@ -52,16 +52,16 @@ class CellGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ - 'name' => 'The cell class name.', + 'name' => 'The Controlled Cell class name.', ]; /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".', diff --git a/system/Commands/Generators/CommandGenerator.php b/system/Commands/Generators/CommandGenerator.php index 7ccae13f..b844666a 100644 --- a/system/Commands/Generators/CommandGenerator.php +++ b/system/Commands/Generators/CommandGenerator.php @@ -53,7 +53,7 @@ class CommandGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The command class name.', @@ -62,12 +62,12 @@ class CommandGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--command' => 'The command name. Default: "command:name"', '--type' => 'The command type. Options [basic, generator]. Default: "basic".', - '--group' => 'The command group. Default: [basic -> "CodeIgniter", generator -> "Generators"].', + '--group' => 'The command group. Default: [basic -> "App", generator -> "Generators"].', '--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".', '--suffix' => 'Append the component title to the class name (e.g. User => UserCommand).', '--force' => 'Force overwrite existing file.', @@ -106,7 +106,7 @@ class CommandGenerator extends BaseCommand } if (! is_string($group)) { - $group = $type === 'generator' ? 'Generators' : 'CodeIgniter'; + $group = $type === 'generator' ? 'Generators' : 'App'; } return $this->parseTemplate( diff --git a/system/Commands/Generators/ConfigGenerator.php b/system/Commands/Generators/ConfigGenerator.php index 606af47b..a83a9671 100644 --- a/system/Commands/Generators/ConfigGenerator.php +++ b/system/Commands/Generators/ConfigGenerator.php @@ -52,7 +52,7 @@ class ConfigGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The config class name.', @@ -61,7 +61,7 @@ class ConfigGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".', diff --git a/system/Commands/Generators/ControllerGenerator.php b/system/Commands/Generators/ControllerGenerator.php index f27c77ee..2cf912b1 100644 --- a/system/Commands/Generators/ControllerGenerator.php +++ b/system/Commands/Generators/ControllerGenerator.php @@ -56,7 +56,7 @@ class ControllerGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The controller class name.', @@ -65,7 +65,7 @@ class ControllerGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--bare' => 'Extends from CodeIgniter\Controller instead of BaseController.', diff --git a/system/Commands/Generators/EntityGenerator.php b/system/Commands/Generators/EntityGenerator.php index 8cd84785..bd20daf5 100644 --- a/system/Commands/Generators/EntityGenerator.php +++ b/system/Commands/Generators/EntityGenerator.php @@ -52,7 +52,7 @@ class EntityGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The entity class name.', @@ -61,7 +61,7 @@ class EntityGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".', diff --git a/system/Commands/Generators/FilterGenerator.php b/system/Commands/Generators/FilterGenerator.php index 02060b02..620bee5a 100644 --- a/system/Commands/Generators/FilterGenerator.php +++ b/system/Commands/Generators/FilterGenerator.php @@ -52,7 +52,7 @@ class FilterGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The filter class name.', @@ -61,7 +61,7 @@ class FilterGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".', diff --git a/system/Commands/Generators/MigrateCreate.php b/system/Commands/Generators/MigrateCreate.php index 0b493534..a2fa6bf7 100644 --- a/system/Commands/Generators/MigrateCreate.php +++ b/system/Commands/Generators/MigrateCreate.php @@ -55,7 +55,7 @@ class MigrateCreate extends BaseCommand /** * The Command's arguments. * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The migration file name.', @@ -64,7 +64,7 @@ class MigrateCreate extends BaseCommand /** * The Command's options. * - * @var array + * @var array */ protected $options = [ '--namespace' => 'Set root namespace. Defaults to APP_NAMESPACE', diff --git a/system/Commands/Generators/MigrationGenerator.php b/system/Commands/Generators/MigrationGenerator.php index 82428a69..52f9e6e5 100644 --- a/system/Commands/Generators/MigrationGenerator.php +++ b/system/Commands/Generators/MigrationGenerator.php @@ -56,7 +56,7 @@ class MigrationGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The migration class name.', @@ -65,7 +65,7 @@ class MigrationGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--session' => 'Generates the migration file for database sessions.', diff --git a/system/Commands/Generators/ModelGenerator.php b/system/Commands/Generators/ModelGenerator.php index eb0a1aa9..f4946a94 100644 --- a/system/Commands/Generators/ModelGenerator.php +++ b/system/Commands/Generators/ModelGenerator.php @@ -53,7 +53,7 @@ class ModelGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The model class name.', @@ -62,7 +62,7 @@ class ModelGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--table' => 'Supply a table name. Default: "the lowercased plural of the class name".', diff --git a/system/Commands/Generators/ScaffoldGenerator.php b/system/Commands/Generators/ScaffoldGenerator.php index 351ee009..ef34b92e 100644 --- a/system/Commands/Generators/ScaffoldGenerator.php +++ b/system/Commands/Generators/ScaffoldGenerator.php @@ -53,7 +53,7 @@ class ScaffoldGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The class name', @@ -62,7 +62,7 @@ class ScaffoldGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--bare' => 'Add the "--bare" option to controller component.', diff --git a/system/Commands/Generators/SeederGenerator.php b/system/Commands/Generators/SeederGenerator.php index 09a1037f..e60525ae 100644 --- a/system/Commands/Generators/SeederGenerator.php +++ b/system/Commands/Generators/SeederGenerator.php @@ -52,7 +52,7 @@ class SeederGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The seeder class name.', @@ -61,7 +61,7 @@ class SeederGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".', diff --git a/system/Commands/Generators/SessionMigrationGenerator.php b/system/Commands/Generators/SessionMigrationGenerator.php index a04527e8..cb7da589 100644 --- a/system/Commands/Generators/SessionMigrationGenerator.php +++ b/system/Commands/Generators/SessionMigrationGenerator.php @@ -59,7 +59,7 @@ class SessionMigrationGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '-t' => 'Supply a table name.', diff --git a/system/Commands/Generators/ValidationGenerator.php b/system/Commands/Generators/ValidationGenerator.php index 0c58ca5b..1b2efb8d 100644 --- a/system/Commands/Generators/ValidationGenerator.php +++ b/system/Commands/Generators/ValidationGenerator.php @@ -52,7 +52,7 @@ class ValidationGenerator extends BaseCommand /** * The Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'name' => 'The validation class name.', @@ -61,7 +61,7 @@ class ValidationGenerator extends BaseCommand /** * The Command's Options * - * @var array + * @var array */ protected $options = [ '--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".', diff --git a/system/Commands/Generators/Views/controller.tpl.php b/system/Commands/Generators/Views/controller.tpl.php index de4447c5..2fe99e1b 100644 --- a/system/Commands/Generators/Views/controller.tpl.php +++ b/system/Commands/Generators/Views/controller.tpl.php @@ -3,6 +3,7 @@ namespace {namespace}; use {useStatement}; +use CodeIgniter\HTTP\ResponseInterface; class {class} extends {extends} { @@ -10,7 +11,7 @@ class {class} extends {extends} /** * Return an array of resource objects, themselves in array format * - * @return mixed + * @return ResponseInterface */ public function index() { @@ -20,7 +21,7 @@ class {class} extends {extends} /** * Return the properties of a resource object * - * @return mixed + * @return ResponseInterface */ public function show($id = null) { @@ -30,7 +31,7 @@ class {class} extends {extends} /** * Return a new resource object, with default properties * - * @return mixed + * @return ResponseInterface */ public function new() { @@ -40,7 +41,7 @@ class {class} extends {extends} /** * Create a new resource object, from "posted" parameters * - * @return mixed + * @return ResponseInterface */ public function create() { @@ -50,7 +51,7 @@ class {class} extends {extends} /** * Return the editable properties of a resource object * - * @return mixed + * @return ResponseInterface */ public function edit($id = null) { @@ -60,7 +61,7 @@ class {class} extends {extends} /** * Add or update a model resource, from "posted" properties * - * @return mixed + * @return ResponseInterface */ public function update($id = null) { @@ -70,7 +71,7 @@ class {class} extends {extends} /** * Delete the designated resource object from the model * - * @return mixed + * @return ResponseInterface */ public function delete($id = null) { @@ -80,7 +81,7 @@ class {class} extends {extends} /** * Present a view of resource objects * - * @return mixed + * @return ResponseInterface */ public function index() { @@ -90,9 +91,9 @@ class {class} extends {extends} /** * Present a view to present a specific resource object * - * @param mixed $id + * @param string $id * - * @return mixed + * @return ResponseInterface */ public function show($id = null) { diff --git a/system/Commands/Generators/Views/filter.tpl.php b/system/Commands/Generators/Views/filter.tpl.php index c080e920..767ac0bd 100644 --- a/system/Commands/Generators/Views/filter.tpl.php +++ b/system/Commands/Generators/Views/filter.tpl.php @@ -21,7 +21,7 @@ class {class} implements FilterInterface * @param RequestInterface $request * @param array|null $arguments * - * @return mixed + * @return RequestInterface|ResponseInterface|string|void */ public function before(RequestInterface $request, $arguments = null) { @@ -38,7 +38,7 @@ class {class} implements FilterInterface * @param ResponseInterface $response * @param array|null $arguments * - * @return mixed + * @return ResponseInterface|void */ public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { diff --git a/system/Commands/Generators/Views/model.tpl.php b/system/Commands/Generators/Views/model.tpl.php index b9b9c99e..72509cdb 100644 --- a/system/Commands/Generators/Views/model.tpl.php +++ b/system/Commands/Generators/Views/model.tpl.php @@ -17,6 +17,8 @@ class {class} extends Model protected $protectFields = true; protected $allowedFields = []; + protected bool $allowEmptyInserts = false; + // Dates protected $useTimestamps = false; protected $dateFormat = 'datetime'; diff --git a/system/Commands/Help.php b/system/Commands/Help.php index 74dcbeab..338a5c86 100644 --- a/system/Commands/Help.php +++ b/system/Commands/Help.php @@ -53,7 +53,7 @@ class Help extends BaseCommand /** * the Command's Arguments * - * @var array + * @var array */ protected $arguments = [ 'command_name' => 'The command name [default: "help"]', @@ -62,7 +62,7 @@ class Help extends BaseCommand /** * the Command's Options * - * @var array + * @var array */ protected $options = []; diff --git a/system/Commands/Housekeeping/ClearLogs.php b/system/Commands/Housekeeping/ClearLogs.php index eb2489c5..56cfeab3 100644 --- a/system/Commands/Housekeeping/ClearLogs.php +++ b/system/Commands/Housekeeping/ClearLogs.php @@ -51,7 +51,7 @@ class ClearLogs extends BaseCommand /** * The Command's options * - * @var array + * @var array */ protected $options = [ '--force' => 'Force delete of all logs files without prompting.', diff --git a/system/Commands/ListCommands.php b/system/Commands/ListCommands.php index 0c808322..9accecf0 100644 --- a/system/Commands/ListCommands.php +++ b/system/Commands/ListCommands.php @@ -54,14 +54,14 @@ class ListCommands extends BaseCommand /** * the Command's Arguments * - * @var array + * @var array */ protected $arguments = []; /** * the Command's Options * - * @var array + * @var array */ protected $options = [ '--simple' => 'Prints a list of the commands with no other info', diff --git a/system/Commands/Server/Serve.php b/system/Commands/Server/Serve.php index 2dc9e5dd..b5382020 100644 --- a/system/Commands/Server/Serve.php +++ b/system/Commands/Server/Serve.php @@ -54,7 +54,7 @@ class Serve extends BaseCommand /** * Arguments * - * @var array + * @var array */ protected $arguments = []; @@ -75,7 +75,7 @@ class Serve extends BaseCommand /** * Options * - * @var array + * @var array */ protected $options = [ '--php' => 'The PHP Binary [default: "PHP_BINARY"]', diff --git a/system/Commands/Utilities/Environment.php b/system/Commands/Utilities/Environment.php index b844e7a2..fd8f68fa 100644 --- a/system/Commands/Utilities/Environment.php +++ b/system/Commands/Utilities/Environment.php @@ -62,7 +62,7 @@ final class Environment extends BaseCommand /** * The Command's options * - * @var array + * @var array */ protected $options = []; diff --git a/system/Commands/Utilities/FilterCheck.php b/system/Commands/Utilities/FilterCheck.php index d2eb2fb7..1b9937d2 100644 --- a/system/Commands/Utilities/FilterCheck.php +++ b/system/Commands/Utilities/FilterCheck.php @@ -57,13 +57,13 @@ class FilterCheck extends BaseCommand */ protected $arguments = [ 'method' => 'The HTTP method. get, post, put, etc.', - 'route' => 'The route (URI path) to check filtes.', + 'route' => 'The route (URI path) to check filters.', ]; /** * the Command's Options * - * @var array + * @var array */ protected $options = []; diff --git a/system/Commands/Utilities/Namespaces.php b/system/Commands/Utilities/Namespaces.php index ef37736c..71461933 100644 --- a/system/Commands/Utilities/Namespaces.php +++ b/system/Commands/Utilities/Namespaces.php @@ -57,14 +57,14 @@ class Namespaces extends BaseCommand /** * the Command's Arguments * - * @var array + * @var array */ protected $arguments = []; /** * the Command's Options * - * @var array + * @var array */ protected $options = [ '-c' => 'Show only CodeIgniter config namespaces.', diff --git a/system/Commands/Utilities/Publish.php b/system/Commands/Utilities/Publish.php index cfed0472..1e4103c1 100644 --- a/system/Commands/Utilities/Publish.php +++ b/system/Commands/Utilities/Publish.php @@ -63,7 +63,7 @@ class Publish extends BaseCommand /** * the Command's Options * - * @var array + * @var array */ protected $options = []; diff --git a/system/Commands/Utilities/Routes.php b/system/Commands/Utilities/Routes.php index b3693ec6..5d20da07 100644 --- a/system/Commands/Utilities/Routes.php +++ b/system/Commands/Utilities/Routes.php @@ -61,7 +61,7 @@ class Routes extends BaseCommand /** * the Command's Arguments * - * @var array + * @var array */ protected $arguments = []; diff --git a/system/Commands/Utilities/Routes/AutoRouteCollector.php b/system/Commands/Utilities/Routes/AutoRouteCollector.php index ee331332..cc45608f 100644 --- a/system/Commands/Utilities/Routes/AutoRouteCollector.php +++ b/system/Commands/Utilities/Routes/AutoRouteCollector.php @@ -37,8 +37,7 @@ final class AutoRouteCollector } /** - * @return array> - * @phpstan-return list> + * @return list> */ public function get(): array { diff --git a/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php b/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php index 0b9089ce..18a158cf 100644 --- a/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php +++ b/system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php @@ -62,8 +62,7 @@ final class AutoRouteCollector } /** - * @return array> - * @phpstan-return list> + * @return list> */ public function get(): array { diff --git a/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php b/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php index a8b6912d..a077633f 100644 --- a/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php +++ b/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved; +use Config\Routing; use ReflectionClass; use ReflectionMethod; @@ -27,11 +28,12 @@ final class ControllerMethodReader private string $namespace; /** - * @var array - * @phpstan-var list + * @var list */ private array $httpMethods; + private bool $translateURIDashes; + /** * @param string $namespace the default namespace */ @@ -39,15 +41,17 @@ final class ControllerMethodReader { $this->namespace = $namespace; $this->httpMethods = $httpMethods; + + $config = config(Routing::class); + $this->translateURIDashes = $config->translateURIDashes; } /** * Returns found route info in the controller. * - * @phpstan-param class-string $class + * @param class-string $class * - * @return array> - * @phpstan-return list> + * @return list> */ public function read(string $class, string $defaultController = 'Home', string $defaultMethod = 'index'): array { @@ -69,7 +73,7 @@ final class ControllerMethodReader foreach ($this->httpMethods as $httpVerb) { if (strpos($methodName, $httpVerb) === 0) { // Remove HTTP verb prefix. - $methodInUri = lcfirst(substr($methodName, strlen($httpVerb))); + $methodInUri = $this->getUriByMethod($httpVerb, $methodName); // Check if it is the default method. if ($methodInUri === $defaultMethod) { @@ -154,7 +158,7 @@ final class ControllerMethodReader } /** - * @phpstan-param class-string $classname + * @param class-string $classname * * @return string URI path part from the folder(s) and controller */ @@ -173,13 +177,33 @@ final class ControllerMethodReader $classPath .= lcfirst($part) . '/'; } - return rtrim($classPath, '/'); + $classUri = rtrim($classPath, '/'); + + if ($this->translateURIDashes) { + $classUri = str_replace('_', '-', $classUri); + } + + return $classUri; + } + + /** + * @return string URI path part from the method + */ + private function getUriByMethod(string $httpVerb, string $methodName): string + { + $methodUri = lcfirst(substr($methodName, strlen($httpVerb))); + + if ($this->translateURIDashes) { + $methodUri = str_replace('_', '-', $methodUri); + } + + return $methodUri; } /** * Gets a route for the default controller. * - * @phpstan-return list + * @return list */ private function getRouteForDefaultController( string $classShortname, diff --git a/system/Commands/Utilities/Routes/ControllerFinder.php b/system/Commands/Utilities/Routes/ControllerFinder.php index f801d531..3982c454 100644 --- a/system/Commands/Utilities/Routes/ControllerFinder.php +++ b/system/Commands/Utilities/Routes/ControllerFinder.php @@ -38,8 +38,7 @@ final class ControllerFinder } /** - * @return string[] - * @phpstan-return class-string[] + * @return class-string[] */ public function find(): array { @@ -66,7 +65,7 @@ final class ControllerFinder $classnameOrEmpty = $this->locator->getClassname($file); if ($classnameOrEmpty !== '') { - /** @phpstan-var class-string $classname */ + /** @var class-string $classname */ $classname = $classnameOrEmpty; $classes[] = $classname; diff --git a/system/Commands/Utilities/Routes/ControllerMethodReader.php b/system/Commands/Utilities/Routes/ControllerMethodReader.php index eb0152de..31bcde12 100644 --- a/system/Commands/Utilities/Routes/ControllerMethodReader.php +++ b/system/Commands/Utilities/Routes/ControllerMethodReader.php @@ -35,10 +35,9 @@ final class ControllerMethodReader } /** - * @phpstan-param class-string $class + * @param class-string $class * - * @return array - * @phpstan-return list + * @return list */ public function read(string $class, string $defaultController = 'Home', string $defaultMethod = 'index'): array { @@ -128,7 +127,7 @@ final class ControllerMethodReader } /** - * @phpstan-param class-string $classname + * @param class-string $classname * * @return string URI path part from the folder(s) and controller */ diff --git a/system/Commands/Utilities/Routes/SampleURIGenerator.php b/system/Commands/Utilities/Routes/SampleURIGenerator.php index 38b333fd..43d19344 100644 --- a/system/Commands/Utilities/Routes/SampleURIGenerator.php +++ b/system/Commands/Utilities/Routes/SampleURIGenerator.php @@ -13,6 +13,7 @@ namespace CodeIgniter\Commands\Utilities\Routes; use CodeIgniter\Config\Services; use CodeIgniter\Router\RouteCollection; +use Config\App; /** * Generate a sample URI path from route key regex. @@ -51,6 +52,14 @@ final class SampleURIGenerator { $sampleUri = $routeKey; + if (strpos($routeKey, '{locale}') !== false) { + $sampleUri = str_replace( + '{locale}', + config(App::class)->defaultLocale, + $routeKey + ); + } + foreach ($this->routes->getPlaceholders() as $placeholder => $regex) { $sample = $this->samples[$placeholder] ?? '::unknown::'; diff --git a/system/Common.php b/system/Common.php index 80dca72c..690fa3fc 100644 --- a/system/Common.php +++ b/system/Common.php @@ -25,9 +25,7 @@ use CodeIgniter\HTTP\Exceptions\RedirectException; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\RequestInterface; -use CodeIgniter\HTTP\Response; use CodeIgniter\HTTP\ResponseInterface; -use CodeIgniter\HTTP\URI; use CodeIgniter\Model; use CodeIgniter\Session\Session; use CodeIgniter\Test\TestLogger; @@ -290,20 +288,24 @@ if (! function_exists('csrf_hash')) { if (! function_exists('csrf_field')) { /** * Generates a hidden input field for use within manually generated forms. + * + * @param non-empty-string|null $id */ function csrf_field(?string $id = null): string { - return ''; + return ''; } } if (! function_exists('csrf_meta')) { /** * Generates a meta tag for use within javascript calls. + * + * @param non-empty-string|null $id */ function csrf_meta(?string $id = null): string { - return ''; + return ''; } } @@ -502,27 +504,11 @@ if (! function_exists('force_https')) { Services::session()->regenerate(); // @codeCoverageIgnore } - $baseURL = config(App::class)->baseURL; - - if (strpos($baseURL, 'https://') === 0) { - $authority = substr($baseURL, strlen('https://')); - } elseif (strpos($baseURL, 'http://') === 0) { - $authority = substr($baseURL, strlen('http://')); - } else { - $authority = $baseURL; - } - - $uri = URI::createURIString( - 'https', - $authority, - $request->getUri()->getPath(), // Absolute URIs should use a "/" for an empty path - $request->getUri()->getQuery(), - $request->getUri()->getFragment() - ); + $uri = $request->getUri()->withScheme('https'); // Set an HSTS header $response->setHeader('Strict-Transport-Security', 'max-age=' . $duration) - ->redirect($uri) + ->redirect((string) $uri) ->setStatusCode(307) ->setBody('') ->getCookieStore() @@ -755,7 +741,7 @@ if (! function_exists('lang')) { * A convenience method to translate a string or array of them and format * the result with the intl extension's MessageFormatter. * - * @return string + * @return list|string */ function lang(string $line, array $args = [], ?string $locale = null) { @@ -868,13 +854,13 @@ if (! function_exists('redirect')) { * * If more control is needed, you must use $response->redirect explicitly. * - * @param string|null $route Route name or Controller::method + * @param non-empty-string|null $route Route name or Controller::method */ function redirect(?string $route = null): RedirectResponse { $response = Services::redirectresponse(null, true); - if (! empty($route)) { + if ($route !== null) { return $response->route($route); } @@ -1139,15 +1125,17 @@ if (! function_exists('timer')) { * returns its return value if any. * Otherwise will start or stop the timer intelligently. * - * @phpstan-param (callable(): mixed)|null $callable + * @param non-empty-string|null $name + * @param (callable(): mixed)|null $callable * - * @return Timer + * @return mixed|Timer + * @phpstan-return ($name is null ? Timer : ($callable is (callable(): mixed) ? mixed : Timer)) */ function timer(?string $name = null, ?callable $callable = null) { $timer = Services::timer(); - if (empty($name)) { + if ($name === null) { return $timer; } diff --git a/system/Config/AutoloadConfig.php b/system/Config/AutoloadConfig.php index da08dd6f..33c977cc 100644 --- a/system/Config/AutoloadConfig.php +++ b/system/Config/AutoloadConfig.php @@ -45,8 +45,7 @@ class AutoloadConfig * but this should be done prior to creating any namespaced classes, * else you will need to modify all of those classes for this to work. * - * @var array|string> - * @phpstan-var array> + * @var array|string> */ public $psr4 = []; @@ -72,8 +71,7 @@ class AutoloadConfig * that will be autoloaded. This can be useful for bootstrap operations * or for loading functions. * - * @var array - * @phpstan-var list + * @var list */ public $files = []; diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index 22daf7b6..1c80c80d 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -343,7 +343,7 @@ class BaseService foreach ($files as $file) { $classname = $locator->getClassname($file); - if (! in_array($classname, [Services::class], true)) { + if ($classname !== Services::class) { static::$services[] = new $classname(); } } diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 8eb68b0d..c86bfd95 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -62,8 +62,7 @@ class Factories * * [component => [alias => FQCN]] * - * @var array> - * @phpstan-var array> + * @var array> */ protected static $aliases = []; @@ -76,8 +75,7 @@ class Factories * * [component => [FQCN => instance]] * - * @var array> - * @phpstan-var array> + * @var array> */ protected static $instances = []; @@ -93,10 +91,9 @@ class Factories /** * Define the class to load. You can *override* the concrete class. * - * @param string $component Lowercase, plural component name - * @param string $alias Class alias. See the $aliases property. - * @param string $classname FQCN to be loaded - * @phpstan-param class-string $classname FQCN to be loaded + * @param string $component Lowercase, plural component name + * @param string $alias Class alias. See the $aliases property. + * @param class-string $classname FQCN to be loaded */ public static function define(string $component, string $alias, string $classname): void { diff --git a/system/Config/Publisher.php b/system/Config/Publisher.php index fa04e24c..d5f938e0 100644 --- a/system/Config/Publisher.php +++ b/system/Config/Publisher.php @@ -26,7 +26,7 @@ class Publisher extends BaseConfig * result in a PublisherException. Files that do no fit the * pattern will cause copy/merge to fail. * - * @var array + * @var array */ public $restrictions = [ ROOTPATH => '*', diff --git a/system/Config/Routing.php b/system/Config/Routing.php index 409bcf09..e6d25138 100644 --- a/system/Config/Routing.php +++ b/system/Config/Routing.php @@ -24,7 +24,7 @@ class Routing extends BaseConfig * Default: APPPATH . 'Config/Routes.php' */ public array $routeFiles = [ - APPPATH . 'Routes.php', + APPPATH . 'Config/Routes.php', ]; /** @@ -95,4 +95,17 @@ class Routing extends BaseConfig * Default: false */ public bool $prioritize = false; + + /** + * Map of URI segments and namespaces. For Auto Routing (Improved). + * + * The key is the first URI segment. The value is the controller namespace. + * E.g., + * [ + * 'blog' => 'Acme\Blog\Controllers', + * ] + * + * @var array [ uri_segment => namespace ] + */ + public array $moduleRoutes = []; } diff --git a/system/Config/View.php b/system/Config/View.php index 5a6a6404..101b67f5 100644 --- a/system/Config/View.php +++ b/system/Config/View.php @@ -16,8 +16,8 @@ use CodeIgniter\View\ViewDecoratorInterface; /** * View configuration * - * @phpstan-type ParserCallable (callable(mixed): mixed) - * @phpstan-type ParserCallableString (callable(mixed): mixed)&string + * @phpstan-type parser_callable (callable(mixed): mixed) + * @phpstan-type parser_callable_string (callable(mixed): mixed)&string */ class View extends BaseConfig { @@ -40,7 +40,7 @@ class View extends BaseConfig * @psalm-suppress UndefinedDocblockClass * * @var array - * @phpstan-var array + * @phpstan-var array */ public $filters = []; @@ -52,7 +52,7 @@ class View extends BaseConfig * @psalm-suppress UndefinedDocblockClass * * @var array|callable|string> - * @phpstan-var array|ParserCallableString|ParserCallable> + * @phpstan-var array|parser_callable_string|parser_callable> */ public $plugins = []; @@ -60,7 +60,7 @@ class View extends BaseConfig * Built-in View filters. * * @var array - * @phpstan-var array + * @phpstan-var array */ protected $coreFilters = [ 'abs' => '\abs', @@ -90,7 +90,7 @@ class View extends BaseConfig * Built-in View plugins. * * @var array|callable|string> - * @phpstan-var array|ParserCallableString|ParserCallable> + * @phpstan-var array|parser_callable_string|parser_callable> */ protected $corePlugins = [ 'csp_script_nonce' => '\CodeIgniter\View\Plugins::cspScriptNonce', diff --git a/system/Controller.php b/system/Controller.php index 45fe0e49..a28a7a59 100644 --- a/system/Controller.php +++ b/system/Controller.php @@ -65,7 +65,7 @@ class Controller /** * Once validation has been run, will hold the Validation instance. * - * @var ValidationInterface + * @var ValidationInterface|null */ protected $validator; @@ -132,7 +132,7 @@ class Controller */ protected function loadHelpers() { - if (empty($this->helpers)) { + if ($this->helpers === []) { return; } diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 30202032..48302ab2 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -88,8 +88,7 @@ class BaseBuilder * QB keys * list of column names. * - * @var string[] - * @phpstan-var list + * @var list */ protected $QBKeys = []; @@ -131,8 +130,7 @@ class BaseBuilder /** * QB data sets * - * @var array[]|string[] - * @phpstan-var array|list> + * @var array|list> */ protected $QBSet = []; @@ -164,16 +162,18 @@ class BaseBuilder * Holds additional options and data used to render SQL * and is reset by resetWrite() * - * @phpstan-var array{ + * @var array{ * updateFieldsAdditional?: array, * tableIdentity?: string, * updateFields?: array, * constraints?: array, * setQueryAsData?: string, * sql?: string, - * alias?: string + * alias?: string, + * fieldTypes?: array> * } - * @var array + * + * fieldTypes: [ProtectedTableName => [FieldName => Type]] */ protected $QBOptions; @@ -321,7 +321,7 @@ class BaseBuilder $this->from($tableName); - if (! empty($options)) { + if ($options !== null && $options !== []) { foreach ($options as $key => $value) { if (property_exists($this, $key)) { $this->{$key} = $value; @@ -923,6 +923,7 @@ class BaseBuilder * @used-by whereNotIn() * @used-by orWhereNotIn() * + * @param non-empty-string|null $key * @param array|BaseBuilder|Closure|null $values The values searched on, or anonymous function with subquery * * @return $this @@ -931,7 +932,7 @@ class BaseBuilder */ protected function _whereIn(?string $key = null, $values = null, bool $not = false, string $type = 'AND ', ?bool $escape = null, string $clause = 'QBWhere') { - if (empty($key) || ! is_string($key)) { + if ($key === null || $key === '') { throw new InvalidArgumentException(sprintf('%s() expects $key to be a non-empty string', debug_backtrace(0, 2)[1]['function'])); } @@ -1437,7 +1438,7 @@ class BaseBuilder public function orderBy(string $orderBy, string $direction = '', ?bool $escape = null) { $qbOrderBy = []; - if (empty($orderBy)) { + if ($orderBy === '') { return $this; } @@ -1493,7 +1494,7 @@ class BaseBuilder $this->QBLimit = $value; } - if (! empty($offset)) { + if ($offset !== null && $offset !== 0) { $this->QBOffset = $offset; } @@ -1507,8 +1508,8 @@ class BaseBuilder */ public function offset(int $offset) { - if (! empty($offset)) { - $this->QBOffset = (int) $offset; + if ($offset !== 0) { + $this->QBOffset = $offset; } return $this; @@ -1739,7 +1740,7 @@ class BaseBuilder $this->where($where); } - if (! empty($limit)) { + if ($limit !== null && $limit !== 0) { $this->limit($limit, $offset); } @@ -1760,6 +1761,8 @@ class BaseBuilder /** * Compiles batch insert/update/upsert strings and runs the queries * + * @param '_deleteBatch'|'_insertBatch'|'_updateBatch'|'_upsertBatch' $renderMethod + * * @return false|int|string[] Number of rows inserted or FALSE on failure, SQL array when testMode * * @throws DatabaseException @@ -1967,11 +1970,9 @@ class BaseBuilder * * @used-by batchExecute * - * @param string $table Protected table name - * @param string[] $keys QBKeys - * @phpstan-param list $keys QBKeys - * @param array> $values QBSet - * @phpstan-param list> $values QBSet + * @param string $table Protected table name + * @param list $keys QBKeys + * @param list> $values QBSet */ protected function _upsertBatch(string $table, array $keys, array $values): string { @@ -2196,11 +2197,9 @@ class BaseBuilder * * @used-by batchExecute * - * @param string $table Protected table name - * @param string[] $keys QBKeys - * @phpstan-param list $keys QBKeys - * @param array> $values QBSet - * @phpstan-param list> $values QBSet + * @param string $table Protected table name + * @param list $keys QBKeys + * @param list> $values QBSet */ protected function _insertBatch(string $table, array $keys, array $values): string { @@ -2277,7 +2276,7 @@ class BaseBuilder * * @param array|object|null $set * - * @return bool + * @return BaseResult|bool|Query * * @throws DatabaseException */ @@ -2459,7 +2458,7 @@ class BaseBuilder $this->where($where); } - if (! empty($limit)) { + if ($limit !== null && $limit !== 0) { if (! $this->canLimitWhereUpdates) { throw new DatabaseException('This driver does not allow LIMITs on UPDATE queries using WHERE.'); } @@ -2566,11 +2565,9 @@ class BaseBuilder * * @used-by batchExecute * - * @param string $table Protected table name - * @param string[] $keys QBKeys - * @phpstan-param list $keys QBKeys - * @param array[] $values QBSet - * @phpstan-param list> $values QBSet + * @param string $table Protected table name + * @param list $keys QBKeys + * @param list> $values QBSet */ protected function _updateBatch(string $table, array $keys, array $values): string { @@ -2747,9 +2744,9 @@ class BaseBuilder /** * Compiles a delete string and runs the query * - * @param mixed $where + * @param array|RawSql|string $where * - * @return bool|string Returns a string if in test mode. + * @return bool|string Returns a SQL string if in test mode. * * @throws DatabaseException */ @@ -2771,7 +2768,7 @@ class BaseBuilder $sql = $this->_delete($this->removeAlias($table)); - if (! empty($limit)) { + if ($limit !== null && $limit !== 0) { $this->QBLimit = $limit; } @@ -2830,11 +2827,9 @@ class BaseBuilder * * @used-by batchExecute * - * @param string $table Protected table name - * @param string[] $keys QBKeys - * @phpstan-param list $keys QBKeys - * @param array> $values QBSet - * @phpstan-param list> $values QBSet + * @param string $table Protected table name + * @param list $keys QBKeys + * @paramst> $values QBSet */ protected function _deleteBatch(string $table, array $keys, array $values): string { @@ -3183,7 +3178,7 @@ class BaseBuilder */ protected function compileOrderBy(): string { - if (is_array($this->QBOrderBy) && ! empty($this->QBOrderBy)) { + if (is_array($this->QBOrderBy) && $this->QBOrderBy !== []) { foreach ($this->QBOrderBy as &$orderBy) { if ($orderBy['escape'] !== false && ! $this->isLiteral($orderBy['field'])) { $orderBy['field'] = $this->db->protectIdentifiers($orderBy['field']); @@ -3276,7 +3271,7 @@ class BaseBuilder { $str = trim($str); - if (empty($str) + if ($str === '' || ctype_digit($str) || (string) (float) $str === $str || in_array(strtoupper($str), ['TRUE', 'FALSE'], true) @@ -3412,8 +3407,7 @@ class BaseBuilder /** * Returns the SQL string operator from where key * - * @return array|false - * @phpstan-return list|false + * @return false|list */ private function getOperatorFromWhereKey(string $whereKey) { diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index c5f603a2..b807b0bb 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -174,7 +174,7 @@ abstract class BaseResult implements ResultInterface */ public function getResultArray(): array { - if (! empty($this->resultArray)) { + if ($this->resultArray !== []) { return $this->resultArray; } @@ -214,7 +214,7 @@ abstract class BaseResult implements ResultInterface */ public function getResultObject(): array { - if (! empty($this->resultObject)) { + if ($this->resultObject !== []) { return $this->resultObject; } @@ -254,14 +254,16 @@ abstract class BaseResult implements ResultInterface * * If row doesn't exist, returns null. * - * @param int $n The index of the results to return - * @param string $type The type of result object. 'array', 'object' or class name. + * @param int|string $n The index of the results to return, or column name. + * @param string $type The type of result object. 'array', 'object' or class name. + * @phpstan-param class-string|'array'|'object' $type * * @return array|object|stdClass|null * @phpstan-return ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : object|null)) */ public function getRow($n = 0, string $type = 'object') { + // $n is a column name. if (! is_numeric($n)) { // We cache the row data for subsequent uses if (! is_array($this->rowData)) { @@ -321,7 +323,7 @@ abstract class BaseResult implements ResultInterface public function getRowArray(int $n = 0) { $result = $this->getResultArray(); - if (empty($result)) { + if ($result === []) { return null; } @@ -342,7 +344,7 @@ abstract class BaseResult implements ResultInterface public function getRowObject(int $n = 0) { $result = $this->getResultObject(); - if (empty($result)) { + if ($result === []) { return null; } @@ -390,7 +392,7 @@ abstract class BaseResult implements ResultInterface { $result = $this->getResult($type); - return (empty($result)) ? null : $result[0]; + return ($result === []) ? null : $result[0]; } /** @@ -402,7 +404,7 @@ abstract class BaseResult implements ResultInterface { $result = $this->getResult($type); - return (empty($result)) ? null : $result[count($result) - 1]; + return ($result === []) ? null : $result[count($result) - 1]; } /** @@ -413,7 +415,7 @@ abstract class BaseResult implements ResultInterface public function getNextRow(string $type = 'object') { $result = $this->getResult($type); - if (empty($result)) { + if ($result === []) { return null; } @@ -428,7 +430,7 @@ abstract class BaseResult implements ResultInterface public function getPreviousRow(string $type = 'object') { $result = $this->getResult($type); - if (empty($result)) { + if ($result === []) { return null; } diff --git a/system/Database/Config.php b/system/Database/Config.php index 62fa7920..92556c6d 100644 --- a/system/Database/Config.php +++ b/system/Database/Config.php @@ -41,10 +41,9 @@ class Config extends BaseConfig /** * Returns the database connection * - * @param array|BaseConnection|string|null $group The name of the connection group to use, - * or an array of configuration settings. - * @phpstan-param array|BaseConnection|non-empty-string|null $group - * @param bool $getShared Whether to return a shared instance of the connection. + * @param array|BaseConnection|non-empty-string|null $group The name of the connection group to use, + * or an array of configuration settings. + * @param bool $getShared Whether to return a shared instance of the connection. * * @return BaseConnection */ @@ -127,7 +126,7 @@ class Config extends BaseConfig /** * Returns a new instance of the Database Seeder. * - * @phpstan-param null|non-empty-string $group + * @param non-empty-string|null $group * * @return Seeder */ diff --git a/system/Database/Database.php b/system/Database/Database.php index 2818fe22..58a63e15 100644 --- a/system/Database/Database.php +++ b/system/Database/Database.php @@ -104,7 +104,7 @@ class Database 'database' => isset($dsn['path']) ? rawurldecode(substr($dsn['path'], 1)) : '', ]; - if (! empty($dsn['query'])) { + if (isset($dsn['query']) && ($dsn['query'] !== '')) { parse_str($dsn['query'], $extra); foreach ($extra as $key => $val) { diff --git a/system/Database/Forge.php b/system/Database/Forge.php index ae26f6e7..adbdc208 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -32,14 +32,14 @@ class Forge /** * List of fields. * - * @var array + * @var array [name => attributes] */ protected $fields = []; /** * List of keys. * - * @phpstan-var array{}|list + * @var list */ protected $keys = []; @@ -53,7 +53,7 @@ class Forge /** * Primary keys. * - * @phpstan-var array{}|array{fields: string[], keyName: string} + * @var array{fields?: string[], keyName?: string} */ protected $primaryKeys = []; @@ -351,14 +351,14 @@ class Forge /** * Add Field * - * @param array|string $field + * @param array|string $fields Field array or Field string * * @return Forge */ - public function addField($field) + public function addField($fields) { - if (is_string($field)) { - if ($field === 'id') { + if (is_string($fields)) { + if ($fields === 'id') { $this->addField([ 'id' => [ 'type' => 'INT', @@ -368,27 +368,27 @@ class Forge ]); $this->addKey('id', true); } else { - if (strpos($field, ' ') === false) { + if (strpos($fields, ' ') === false) { throw new InvalidArgumentException('Field information is required for that operation.'); } - $fieldName = explode(' ', $field, 2)[0]; + $fieldName = explode(' ', $fields, 2)[0]; $fieldName = trim($fieldName, '`\'"'); - $this->fields[$fieldName] = $field; + $this->fields[$fieldName] = $fields; } } - if (is_array($field)) { - foreach ($field as $idx => $f) { - if (is_string($f)) { - $this->addField($f); + if (is_array($fields)) { + foreach ($fields as $name => $attributes) { + if (is_string($attributes)) { + $this->addField($attributes); continue; } - if (is_array($f)) { - $this->fields = array_merge($this->fields, [$idx => $f]); + if (is_array($attributes)) { + $this->fields = array_merge($this->fields, [$name => $attributes]); } } } @@ -404,8 +404,14 @@ class Forge * * @throws DatabaseException */ - public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '', string $fkName = ''): Forge - { + public function addForeignKey( + $fieldName = '', + string $tableName = '', + $tableField = '', + string $onUpdate = '', + string $onDelete = '', + string $fkName = '' + ): Forge { $fieldName = (array) $fieldName; $tableField = (array) $tableField; @@ -428,8 +434,9 @@ class Forge */ public function dropKey(string $table, string $keyName, bool $prefixKeyName = true): bool { - $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); - $table = $this->db->escapeIdentifiers($this->db->DBPrefix . $table); + $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); + $table = $this->db->escapeIdentifiers($this->db->DBPrefix . $table); + $dropKeyAsConstraint = $this->dropKeyAsConstraint($table, $keyName); if ($dropKeyAsConstraint === true) { @@ -458,7 +465,7 @@ class Forge } /** - * Checks if if key needs to be dropped as a constraint. + * Checks if key needs to be dropped as a constraint. */ protected function dropKeyAsConstraint(string $table, string $constraintName): bool { @@ -494,7 +501,7 @@ class Forge } /** - * @return BaseResult|bool|false|mixed|Query + * @return bool * * @throws DatabaseException */ @@ -518,7 +525,9 @@ class Forge } /** - * @return mixed + * @param array $attributes Table attributes + * + * @return bool * * @throws DatabaseException */ @@ -562,28 +571,30 @@ class Forge } /** + * @param array $attributes Table attributes + * * @return string SQL string * * @deprecated $ifNotExists is no longer used, and will be removed. */ protected function _createTable(string $table, bool $ifNotExists, array $attributes) { - $columns = $this->_processFields(true); + $processedFields = $this->_processFields(true); - for ($i = 0, $c = count($columns); $i < $c; $i++) { - $columns[$i] = ($columns[$i]['_literal'] !== false) ? "\n\t" . $columns[$i]['_literal'] - : "\n\t" . $this->_processColumn($columns[$i]); + for ($i = 0, $c = count($processedFields); $i < $c; $i++) { + $processedFields[$i] = ($processedFields[$i]['_literal'] !== false) ? "\n\t" . $processedFields[$i]['_literal'] + : "\n\t" . $this->_processColumn($processedFields[$i]); } - $columns = implode(',', $columns); + $processedFields = implode(',', $processedFields); - $columns .= $this->_processPrimaryKeys($table); - $columns .= current($this->_processForeignKeys($table)); + $processedFields .= $this->_processPrimaryKeys($table); + $processedFields .= current($this->_processForeignKeys($table)); if ($this->createTableKeys === true) { $indexes = current($this->_processIndexes($table)); if (is_string($indexes)) { - $columns .= $indexes; + $processedFields .= $indexes; } } @@ -591,7 +602,7 @@ class Forge $this->createTableStr . '%s', 'CREATE TABLE', $this->db->escapeIdentifiers($table), - $columns, + $processedFields, $this->_createTableAttributes($attributes) ); } @@ -610,7 +621,7 @@ class Forge } /** - * @return mixed + * @return bool * * @throws DatabaseException */ @@ -676,7 +687,7 @@ class Forge } /** - * @return mixed + * @return bool * * @throws DatabaseException */ @@ -716,23 +727,24 @@ class Forge } /** - * @param array|string $field + * @param array|string $fields Field array or Field string * * @throws DatabaseException */ - public function addColumn(string $table, $field): bool + public function addColumn(string $table, $fields): bool { // Work-around for literal column definitions - if (! is_array($field)) { - $field = [$field]; + if (is_string($fields)) { + $fields = [$fields]; } - foreach (array_keys($field) as $k) { - $this->addField([$k => $field[$k]]); + foreach (array_keys($fields) as $name) { + $this->addField([$name => $fields[$name]]); } $sqls = $this->_alterTable('ADD', $this->db->DBPrefix . $table, $this->_processFields()); $this->reset(); + if ($sqls === false) { if ($this->db->DBDebug) { throw new DatabaseException('This feature is not available for the database you are using.'); @@ -751,15 +763,16 @@ class Forge } /** - * @param array|string $columnName + * @param array|string $columnNames column names to DROP * - * @return mixed + * @return bool * * @throws DatabaseException */ - public function dropColumn(string $table, $columnName) + public function dropColumn(string $table, $columnNames) { - $sql = $this->_alterTable('DROP', $this->db->DBPrefix . $table, $columnName); + $sql = $this->_alterTable('DROP', $this->db->DBPrefix . $table, $columnNames); + if ($sql === false) { if ($this->db->DBDebug) { throw new DatabaseException('This feature is not available for the database you are using.'); @@ -772,19 +785,19 @@ class Forge } /** - * @param array|string $field + * @param array|string $fields Field array or Field string * * @throws DatabaseException */ - public function modifyColumn(string $table, $field): bool + public function modifyColumn(string $table, $fields): bool { // Work-around for literal column definitions - if (! is_array($field)) { - $field = [$field]; + if (is_string($fields)) { + $fields = [$fields]; } - foreach (array_keys($field) as $k) { - $this->addField([$k => $field[$k]]); + foreach (array_keys($fields) as $name) { + $this->addField([$name => $fields[$name]]); } if ($this->fields === []) { @@ -793,6 +806,7 @@ class Forge $sqls = $this->_alterTable('CHANGE', $this->db->DBPrefix . $table, $this->_processFields()); $this->reset(); + if ($sqls === false) { if ($this->db->DBDebug) { throw new DatabaseException('This feature is not available for the database you are using.'); @@ -813,48 +827,53 @@ class Forge } /** - * @param array|string $fields + * @param 'ADD'|'CHANGE'|'DROP' $alterType + * @param array|string $processedFields Processed column definitions + * or column names to DROP * - * @return false|string|string[] + * @return false|list|string|null SQL string + * @phpstan-return ($alterType is 'DROP' ? string : list|false|null) */ - protected function _alterTable(string $alterType, string $table, $fields) + protected function _alterTable(string $alterType, string $table, $processedFields) { $sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table) . ' '; // DROP has everything it needs now. if ($alterType === 'DROP') { - if (is_string($fields)) { - $fields = explode(',', $fields); + $columnNamesToDrop = $processedFields; + + if (is_string($columnNamesToDrop)) { + $columnNamesToDrop = explode(',', $columnNamesToDrop); } - $fields = array_map(fn ($field) => 'DROP COLUMN ' . $this->db->escapeIdentifiers(trim($field)), $fields); + $columnNamesToDrop = array_map(fn ($field) => 'DROP COLUMN ' . $this->db->escapeIdentifiers(trim($field)), $columnNamesToDrop); - return $sql . implode(', ', $fields); + return $sql . implode(', ', $columnNamesToDrop); } $sql .= ($alterType === 'ADD') ? 'ADD ' : $alterType . ' COLUMN '; $sqls = []; - foreach ($fields as $data) { - $sqls[] = $sql . ($data['_literal'] !== false - ? $data['_literal'] - : $this->_processColumn($data)); + foreach ($processedFields as $field) { + $sqls[] = $sql . ($field['_literal'] !== false + ? $field['_literal'] + : $this->_processColumn($field)); } return $sqls; } /** - * Process fields + * Returns $processedFields array from $this->fields data. */ protected function _processFields(bool $createTable = false): array { - $fields = []; + $processedFields = []; - foreach ($this->fields as $key => $attributes) { + foreach ($this->fields as $name => $attributes) { if (! is_array($attributes)) { - $fields[] = ['_literal' => $attributes]; + $processedFields[] = ['_literal' => $attributes]; continue; } @@ -870,7 +889,7 @@ class Forge } $field = [ - 'name' => $key, + 'name' => $name, 'new_name' => $attributes['NAME'] ?? null, 'type' => $attributes['TYPE'] ?? null, 'length' => '', @@ -928,24 +947,24 @@ class Forge $field['length'] = '(' . $attributes['CONSTRAINT'] . ')'; } - $fields[] = $field; + $processedFields[] = $field; } - return $fields; + return $processedFields; } /** - * Process column + * Converts $processedField array to field definition string. */ - protected function _processColumn(array $field): string + protected function _processColumn(array $processedField): string { - return $this->db->escapeIdentifiers($field['name']) - . ' ' . $field['type'] . $field['length'] - . $field['unsigned'] - . $field['default'] - . $field['null'] - . $field['auto_increment'] - . $field['unique']; + return $this->db->escapeIdentifiers($processedField['name']) + . ' ' . $processedField['type'] . $processedField['length'] + . $processedField['unsigned'] + . $processedField['default'] + . $processedField['null'] + . $processedField['auto_increment'] + . $processedField['unique']; } /** @@ -1163,10 +1182,10 @@ class Forge { $errorNames = []; - foreach ($this->foreignKeys as $name) { - foreach ($name['field'] as $f) { - if (! isset($this->fields[$f])) { - $errorNames[] = $f; + foreach ($this->foreignKeys as $fkeyInfo) { + foreach ($fkeyInfo['field'] as $fieldName) { + if (! isset($this->fields[$fieldName])) { + $errorNames[] = $fieldName; } } } diff --git a/system/Database/Migration.php b/system/Database/Migration.php index 1616830f..f12509d6 100644 --- a/system/Database/Migration.php +++ b/system/Database/Migration.php @@ -21,7 +21,7 @@ abstract class Migration /** * The name of the database group to use. * - * @var string + * @var string|null */ protected $DBGroup; @@ -39,12 +39,15 @@ abstract class Migration */ protected $forge; - /** - * Constructor. - */ public function __construct(?Forge $forge = null) { - $this->forge = $forge ?? Database::forge($this->DBGroup ?? config(Database::class)->defaultGroup); + if (isset($this->DBGroup)) { + $this->forge = Database::forge($this->DBGroup); + } elseif ($forge !== null) { + $this->forge = $forge; + } else { + $this->forge = Database::forge(config(Database::class)->defaultGroup); + } $this->db = $this->forge->getConnection(); } diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index 241a9e6d..435c1185 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -122,14 +122,11 @@ class MigrationRunner protected $groupSkip = false; /** - * Constructor. + * The migration can manage multiple databases. So it should always use the + * default DB group so that it creates the `migrations` table in the default + * DB group. Therefore, passing $db is for testing purposes only. * - * When passing in $db, you may pass any of the following to connect: - * - group name - * - existing connection instance - * - array of database configuration values - * - * @param array|ConnectionInterface|string|null $db + * @param array|ConnectionInterface|string|null $db DB group. For testing purposes only. * * @throws ConfigException */ @@ -138,16 +135,12 @@ class MigrationRunner $this->enabled = $config->enabled ?? false; $this->table = $config->table ?? 'migrations'; - // Default name space is the app namespace $this->namespace = APP_NAMESPACE; - // get default database group - $config = config(Database::class); - $this->group = $config->defaultGroup; - unset($config); + // Even if a DB connection is passed, since it is a test, + // it is assumed to use the default group name + $this->group = is_string($db) ? $db : config(Database::class)->defaultGroup; - // If no db connection passed in, use - // default database group. $this->db = db_connect($db); } @@ -174,7 +167,7 @@ class MigrationRunner $migrations = $this->findMigrations(); - if (empty($migrations)) { + if ($migrations === []) { return true; } @@ -242,7 +235,7 @@ class MigrationRunner $targetBatch = $batches[count($batches) - 1 + $targetBatch] ?? 0; } - if (empty($batches) && $targetBatch === 0) { + if ($batches === [] && $targetBatch === 0) { return true; } @@ -646,7 +639,7 @@ class MigrationRunner $builder = $this->db->table($this->table); // If group was specified then use it - if (! empty($group)) { + if ($group !== '') { $builder->where('group', $group); } @@ -839,8 +832,9 @@ class MigrationRunner throw new RuntimeException($message); } - $instance = new $class(); - $group = $instance->getDBGroup() ?? config(Database::class)->defaultGroup; + /** @var Migration $instance */ + $instance = new $class(Database::forge($this->db)); + $group = $instance->getDBGroup() ?? $this->group; if (ENVIRONMENT !== 'testing' && $group === 'tests' && $this->groupFilter !== 'tests') { // @codeCoverageIgnoreStart @@ -856,8 +850,6 @@ class MigrationRunner return true; } - $this->setGroup($group); - if (! is_callable([$instance, $direction])) { $message = sprintf(lang('Migrations.missingMethod'), $direction); diff --git a/system/Database/MySQLi/Builder.php b/system/Database/MySQLi/Builder.php index 2b95aedc..096bbedd 100644 --- a/system/Database/MySQLi/Builder.php +++ b/system/Database/MySQLi/Builder.php @@ -49,7 +49,7 @@ class Builder extends BaseBuilder */ protected function _fromTables(): string { - if (! empty($this->QBJoin) && count($this->QBFrom) > 1) { + if ($this->QBJoin !== [] && count($this->QBFrom) > 1) { return '(' . implode(', ', $this->QBFrom) . ')'; } diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 45b306ce..3139185f 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -150,7 +150,7 @@ class Connection extends BaseConnection $ssl['cipher'] = $this->encrypt['ssl_cipher']; } - if (! empty($ssl)) { + if ($ssl !== []) { if (isset($this->encrypt['ssl_verify'])) { if ($this->encrypt['ssl_verify']) { if (defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT')) { diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index 0d294c2d..b1beba2a 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -128,57 +128,59 @@ class Forge extends BaseForge /** * ALTER TABLE * - * @param string $alterType ALTER type - * @param string $table Table name - * @param array|string $field Column definition + * @param string $alterType ALTER type + * @param string $table Table name + * @param array|string $processedFields Processed column definitions + * or column names to DROP * - * @return string|string[] + * @return list|string SQL string + * @phpstan-return ($alterType is 'DROP' ? string : list) */ - protected function _alterTable(string $alterType, string $table, $field) + protected function _alterTable(string $alterType, string $table, $processedFields) { if ($alterType === 'DROP') { - return parent::_alterTable($alterType, $table, $field); + return parent::_alterTable($alterType, $table, $processedFields); } $sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table); - foreach ($field as $i => $data) { - if ($data['_literal'] !== false) { - $field[$i] = ($alterType === 'ADD') ? "\n\tADD " . $data['_literal'] : "\n\tMODIFY " . $data['_literal']; + foreach ($processedFields as $i => $field) { + if ($field['_literal'] !== false) { + $processedFields[$i] = ($alterType === 'ADD') ? "\n\tADD " . $field['_literal'] : "\n\tMODIFY " . $field['_literal']; } else { if ($alterType === 'ADD') { - $field[$i]['_literal'] = "\n\tADD "; + $processedFields[$i]['_literal'] = "\n\tADD "; } else { - $field[$i]['_literal'] = empty($data['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE "; + $processedFields[$i]['_literal'] = empty($field['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE "; } - $field[$i] = $field[$i]['_literal'] . $this->_processColumn($field[$i]); + $processedFields[$i] = $processedFields[$i]['_literal'] . $this->_processColumn($processedFields[$i]); } } - return [$sql . implode(',', $field)]; + return [$sql . implode(',', $processedFields)]; } /** * Process column */ - protected function _processColumn(array $field): string + protected function _processColumn(array $processedField): string { - $extraClause = isset($field['after']) ? ' AFTER ' . $this->db->escapeIdentifiers($field['after']) : ''; + $extraClause = isset($processedField['after']) ? ' AFTER ' . $this->db->escapeIdentifiers($processedField['after']) : ''; - if (empty($extraClause) && isset($field['first']) && $field['first'] === true) { + if (empty($extraClause) && isset($processedField['first']) && $processedField['first'] === true) { $extraClause = ' FIRST'; } - return $this->db->escapeIdentifiers($field['name']) - . (empty($field['new_name']) ? '' : ' ' . $this->db->escapeIdentifiers($field['new_name'])) - . ' ' . $field['type'] . $field['length'] - . $field['unsigned'] - . $field['null'] - . $field['default'] - . $field['auto_increment'] - . $field['unique'] - . (empty($field['comment']) ? '' : ' COMMENT ' . $field['comment']) + return $this->db->escapeIdentifiers($processedField['name']) + . (empty($processedField['new_name']) ? '' : ' ' . $this->db->escapeIdentifiers($processedField['new_name'])) + . ' ' . $processedField['type'] . $processedField['length'] + . $processedField['unsigned'] + . $processedField['null'] + . $processedField['default'] + . $processedField['auto_increment'] + . $processedField['unique'] + . (empty($processedField['comment']) ? '' : ' COMMENT ' . $processedField['comment']) . $extraClause; } diff --git a/system/Database/OCI8/Builder.php b/system/Database/OCI8/Builder.php index bccb0bc5..f5527ce7 100644 --- a/system/Database/OCI8/Builder.php +++ b/system/Database/OCI8/Builder.php @@ -166,7 +166,7 @@ class Builder extends BaseBuilder */ public function delete($where = '', ?int $limit = null, bool $resetData = true) { - if (! empty($limit)) { + if ($limit !== null && $limit !== 0) { $this->QBLimit = $limit; } diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 6e1e85f6..2735b886 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -93,21 +93,29 @@ class Forge extends BaseForge /** * ALTER TABLE * - * @param string $alterType ALTER type - * @param string $table Table name - * @param array|string $field Column definition + * @param string $alterType ALTER type + * @param string $table Table name + * @param array|string $processedFields Processed column definitions + * or column names to DROP * - * @return string|string[] + * @return list|string SQL string + * @phpstan-return ($alterType is 'DROP' ? string : list) */ - protected function _alterTable(string $alterType, string $table, $field) + protected function _alterTable(string $alterType, string $table, $processedFields) { $sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table); if ($alterType === 'DROP') { - $fields = array_map(fn ($field) => $this->db->escapeIdentifiers(trim($field)), is_string($field) ? explode(',', $field) : $field); + $columnNamesToDrop = $processedFields; + + $fields = array_map( + fn ($field) => $this->db->escapeIdentifiers(trim($field)), + is_string($columnNamesToDrop) ? explode(',', $columnNamesToDrop) : $columnNamesToDrop + ); return $sql . ' DROP (' . implode(',', $fields) . ') CASCADE CONSTRAINT INVALIDATE'; } + if ($alterType === 'CHANGE') { $alterType = 'MODIFY'; } @@ -115,50 +123,50 @@ class Forge extends BaseForge $nullableMap = array_column($this->db->getFieldData($table), 'nullable', 'name'); $sqls = []; - for ($i = 0, $c = count($field); $i < $c; $i++) { + for ($i = 0, $c = count($processedFields); $i < $c; $i++) { if ($alterType === 'MODIFY') { // If a null constraint is added to a column with a null constraint, // ORA-01451 will occur, // so add null constraint is used only when it is different from the current null constraint. // If a not null constraint is added to a column with a not null constraint, // ORA-01442 will occur. - $wantToAddNull = strpos($field[$i]['null'], ' NOT') === false; - $currentNullable = $nullableMap[$field[$i]['name']]; + $wantToAddNull = strpos($processedFields[$i]['null'], ' NOT') === false; + $currentNullable = $nullableMap[$processedFields[$i]['name']]; if ($wantToAddNull === true && $currentNullable === true) { - $field[$i]['null'] = ''; - } elseif ($field[$i]['null'] === '' && $currentNullable === false) { + $processedFields[$i]['null'] = ''; + } elseif ($processedFields[$i]['null'] === '' && $currentNullable === false) { // Nullable by default - $field[$i]['null'] = ' NULL'; + $processedFields[$i]['null'] = ' NULL'; } elseif ($wantToAddNull === false && $currentNullable === false) { - $field[$i]['null'] = ''; + $processedFields[$i]['null'] = ''; } } - if ($field[$i]['_literal'] !== false) { - $field[$i] = "\n\t" . $field[$i]['_literal']; + if ($processedFields[$i]['_literal'] !== false) { + $processedFields[$i] = "\n\t" . $processedFields[$i]['_literal']; } else { - $field[$i]['_literal'] = "\n\t" . $this->_processColumn($field[$i]); + $processedFields[$i]['_literal'] = "\n\t" . $this->_processColumn($processedFields[$i]); - if (! empty($field[$i]['comment'])) { + if (! empty($processedFields[$i]['comment'])) { $sqls[] = 'COMMENT ON COLUMN ' - . $this->db->escapeIdentifiers($table) . '.' . $this->db->escapeIdentifiers($field[$i]['name']) - . ' IS ' . $field[$i]['comment']; + . $this->db->escapeIdentifiers($table) . '.' . $this->db->escapeIdentifiers($processedFields[$i]['name']) + . ' IS ' . $processedFields[$i]['comment']; } - if ($alterType === 'MODIFY' && ! empty($field[$i]['new_name'])) { - $sqls[] = $sql . ' RENAME COLUMN ' . $this->db->escapeIdentifiers($field[$i]['name']) - . ' TO ' . $this->db->escapeIdentifiers($field[$i]['new_name']); + if ($alterType === 'MODIFY' && ! empty($processedFields[$i]['new_name'])) { + $sqls[] = $sql . ' RENAME COLUMN ' . $this->db->escapeIdentifiers($processedFields[$i]['name']) + . ' TO ' . $this->db->escapeIdentifiers($processedFields[$i]['new_name']); } - $field[$i] = "\n\t" . $field[$i]['_literal']; + $processedFields[$i] = "\n\t" . $processedFields[$i]['_literal']; } } $sql .= ' ' . $alterType . ' '; - $sql .= count($field) === 1 - ? $field[0] - : '(' . implode(',', $field) . ')'; + $sql .= count($processedFields) === 1 + ? $processedFields[0] + : '(' . implode(',', $processedFields) . ')'; // RENAME COLUMN must be executed after MODIFY array_unshift($sqls, $sql); @@ -184,26 +192,26 @@ class Forge extends BaseForge /** * Process column */ - protected function _processColumn(array $field): string + protected function _processColumn(array $processedField): string { $constraint = ''; // @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'] . ')'; + if ($processedField['type'] === 'VARCHAR2' && strpos($processedField['length'], "('") === 0) { + $constraint = ' CHECK(' . $this->db->escapeIdentifiers($processedField['name']) + . ' IN ' . $processedField['length'] . ')'; - $field['length'] = '(' . max(array_map('mb_strlen', explode("','", mb_substr($field['length'], 2, -2)))) . ')' . $constraint; - } elseif (isset($this->primaryKeys['fields']) && count($this->primaryKeys['fields']) === 1 && $field['name'] === $this->primaryKeys['fields'][0]) { - $field['unique'] = ''; + $processedField['length'] = '(' . max(array_map('mb_strlen', explode("','", mb_substr($processedField['length'], 2, -2)))) . ')' . $constraint; + } elseif (isset($this->primaryKeys['fields']) && count($this->primaryKeys['fields']) === 1 && $processedField['name'] === $this->primaryKeys['fields'][0]) { + $processedField['unique'] = ''; } - return $this->db->escapeIdentifiers($field['name']) - . ' ' . $field['type'] . $field['length'] - . $field['unsigned'] - . $field['default'] - . $field['auto_increment'] - . $field['null'] - . $field['unique']; + return $this->db->escapeIdentifiers($processedField['name']) + . ' ' . $processedField['type'] . $processedField['length'] + . $processedField['unsigned'] + . $processedField['default'] + . $processedField['auto_increment'] + . $processedField['null'] + . $processedField['unique']; } /** @@ -246,7 +254,6 @@ class Forge extends BaseForge $attributes['TYPE'] = 'NUMBER'; $attributes['CONSTRAINT'] = 1; $attributes['UNSIGNED'] = true; - $attributes['NULL'] = false; return; diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index e9763089..3749d838 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -146,7 +146,7 @@ class Builder extends BaseBuilder $this->set($set); } - if (! $this->QBSet) { + if ($this->QBSet === []) { if ($this->db->DBDebug) { throw new DatabaseException('You must use the "set" method to update an entry.'); } @@ -229,7 +229,7 @@ class Builder extends BaseBuilder */ public function delete($where = '', ?int $limit = null, bool $resetData = true) { - if (! empty($limit) || ! empty($this->QBLimit)) { + if ($limit !== null && $limit !== 0 || ! empty($this->QBLimit)) { throw new DatabaseException('PostgreSQL does not allow LIMITs on DELETE queries.'); } @@ -312,6 +312,132 @@ class Builder extends BaseBuilder return parent::join($table, $cond, $type, $escape); } + /** + * Generates a platform-specific batch update string from the supplied data + * + * @used-by batchExecute + * + * @param string $table Protected table name + * @param list $keys QBKeys + * @param list> $values QBSet + */ + protected function _updateBatch(string $table, array $keys, array $values): string + { + $sql = $this->QBOptions['sql'] ?? ''; + + // if this is the first iteration of batch then we need to build skeleton sql + if ($sql === '') { + $constraints = $this->QBOptions['constraints'] ?? []; + + if ($constraints === []) { + if ($this->db->DBDebug) { + throw new DatabaseException('You must specify a constraint to match on for batch updates.'); // @codeCoverageIgnore + } + + return ''; // @codeCoverageIgnore + } + + $updateFields = $this->QBOptions['updateFields'] ?? + $this->updateFields($keys, false, $constraints)->QBOptions['updateFields'] ?? + []; + + $alias = $this->QBOptions['alias'] ?? '_u'; + + $sql = 'UPDATE ' . $this->compileIgnore('update') . $table . "\n"; + + $sql .= "SET\n"; + + $that = $this; + $sql .= implode( + ",\n", + array_map( + static fn ($key, $value) => $key . ($value instanceof RawSql ? + ' = ' . $value : + ' = ' . $that->cast($alias . '.' . $value, $that->getFieldType($table, $key))), + array_keys($updateFields), + $updateFields + ) + ) . "\n"; + + $sql .= "FROM (\n{:_table_:}"; + + $sql .= ') ' . $alias . "\n"; + + $sql .= 'WHERE ' . implode( + ' AND ', + array_map( + static function ($key, $value) use ($table, $alias, $that) { + if ($value instanceof RawSql && is_string($key)) { + return $table . '.' . $key . ' = ' . $value; + } + + if ($value instanceof RawSql) { + return $value; + } + + return $table . '.' . $value . ' = ' + . $that->cast($alias . '.' . $value, $that->getFieldType($table, $value)); + }, + array_keys($constraints), + $constraints + ) + ); + + $this->QBOptions['sql'] = $sql; + } + + if (isset($this->QBOptions['setQueryAsData'])) { + $data = $this->QBOptions['setQueryAsData']; + } else { + $data = implode( + " UNION ALL\n", + array_map( + static fn ($value) => 'SELECT ' . implode(', ', array_map( + static fn ($key, $index) => $index . ' ' . $key, + $keys, + $value + )), + $values + ) + ) . "\n"; + } + + return str_replace('{:_table_:}', $data, $sql); + } + + /** + * Returns cast expression. + * + * @TODO move this to BaseBuilder in 4.5.0 + * + * @param float|int|string $expression + */ + private function cast($expression, ?string $type): string + { + return ($type === null) ? $expression : 'CAST(' . $expression . ' AS ' . strtoupper($type) . ')'; + } + + /** + * Returns the filed type from database meta data. + * + * @param string $table Protected table name. + * @param string $fieldName Field name. May be protected. + */ + private function getFieldType(string $table, string $fieldName): ?string + { + $fieldName = trim($fieldName, $this->db->escapeChar); + + if (! isset($this->QBOptions['fieldTypes'][$table])) { + $this->QBOptions['fieldTypes'][$table] = []; + + foreach ($this->db->getFieldData($table) as $field) { + $this->QBOptions['fieldTypes'][$table][$field->name] = $field->type; + } + } + + return $this->QBOptions['fieldTypes'][$table][$fieldName] ?? null; + } + /** * Generates a platform-specific upsertBatch string from the supplied data * @@ -436,18 +562,25 @@ class Builder extends BaseBuilder $sql .= ') ' . $alias . "\n"; + $that = $this; $sql .= 'WHERE ' . implode( ' AND ', array_map( - static fn ($key, $value) => ( - $value instanceof RawSql ? - $value : - ( - is_string($key) ? - $table . '.' . $key . ' = ' . $alias . '.' . $value : - $table . '.' . $value . ' = ' . $alias . '.' . $value - ) - ), + static function ($key, $value) use ($table, $alias, $that) { + if ($value instanceof RawSql) { + return $value; + } + + if (is_string($key)) { + return $table . '.' . $key . ' = ' + . $that->cast( + $alias . '.' . $value, + $that->getFieldType($table, $key) + ); + } + + return $table . '.' . $value . ' = ' . $alias . '.' . $value; + }, array_keys($constraints), $constraints ) diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index 47be1b06..ee758364 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -81,50 +81,52 @@ class Forge extends BaseForge } /** - * @param array|string $field + * @param array|string $processedFields Processed column definitions + * or column names to DROP * - * @return array|bool|string + * @return false|list|string SQL string or false + * @phpstan-return ($alterType is 'DROP' ? string : list|false) */ - protected function _alterTable(string $alterType, string $table, $field) + protected function _alterTable(string $alterType, string $table, $processedFields) { if (in_array($alterType, ['DROP', 'ADD'], true)) { - return parent::_alterTable($alterType, $table, $field); + return parent::_alterTable($alterType, $table, $processedFields); } $sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table); $sqls = []; - foreach ($field as $data) { - if ($data['_literal'] !== false) { + foreach ($processedFields as $field) { + if ($field['_literal'] !== false) { return false; } - if (version_compare($this->db->getVersion(), '8', '>=') && isset($data['type'])) { - $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name']) - . " TYPE {$data['type']}{$data['length']}"; + if (version_compare($this->db->getVersion(), '8', '>=') && isset($field['type'])) { + $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($field['name']) + . " TYPE {$field['type']}{$field['length']}"; } - if (! empty($data['default'])) { - $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name']) - . " SET DEFAULT {$data['default']}"; + if (! empty($field['default'])) { + $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($field['name']) + . " SET DEFAULT {$field['default']}"; } $nullable = true; // Nullable by default. - if (isset($data['null']) && ($data['null'] === false || $data['null'] === ' NOT ' . $this->null)) { + if (isset($field['null']) && ($field['null'] === false || $field['null'] === ' NOT ' . $this->null)) { $nullable = false; } - $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name']) + $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($field['name']) . ($nullable === true ? ' DROP' : ' SET') . ' NOT NULL'; - if (! empty($data['new_name'])) { - $sqls[] = $sql . ' RENAME COLUMN ' . $this->db->escapeIdentifiers($data['name']) - . ' TO ' . $this->db->escapeIdentifiers($data['new_name']); + if (! empty($field['new_name'])) { + $sqls[] = $sql . ' RENAME COLUMN ' . $this->db->escapeIdentifiers($field['name']) + . ' TO ' . $this->db->escapeIdentifiers($field['new_name']); } - if (! empty($data['comment'])) { + if (! empty($field['comment'])) { $sqls[] = 'COMMENT ON COLUMN' . $this->db->escapeIdentifiers($table) - . '.' . $this->db->escapeIdentifiers($data['name']) - . " IS {$data['comment']}"; + . '.' . $this->db->escapeIdentifiers($field['name']) + . " IS {$field['comment']}"; } } @@ -134,14 +136,14 @@ class Forge extends BaseForge /** * Process column */ - protected function _processColumn(array $field): string + protected function _processColumn(array $processedField): string { - return $this->db->escapeIdentifiers($field['name']) - . ' ' . $field['type'] . ($field['type'] === 'text' ? '' : $field['length']) - . $field['default'] - . $field['null'] - . $field['auto_increment'] - . $field['unique']; + return $this->db->escapeIdentifiers($processedField['name']) + . ' ' . $processedField['type'] . ($processedField['type'] === 'text' ? '' : $processedField['length']) + . $processedField['default'] + . $processedField['null'] + . $processedField['auto_increment'] + . $processedField['unique']; } /** diff --git a/system/Database/ResultInterface.php b/system/Database/ResultInterface.php index c959a7cb..b06de85a 100644 --- a/system/Database/ResultInterface.php +++ b/system/Database/ResultInterface.php @@ -57,8 +57,9 @@ interface ResultInterface * * If row doesn't exist, returns null. * - * @param int $n The index of the results to return - * @param string $type The type of result object. 'array', 'object' or class name. + * @param int|string $n The index of the results to return, or column name. + * @param string $type The type of result object. 'array', 'object' or class name. + * @phpstan-param class-string|'array'|'object' $type * * @return array|object|stdClass|null * @phpstan-return ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : object|null)) diff --git a/system/Database/SQLSRV/Builder.php b/system/Database/SQLSRV/Builder.php old mode 100644 new mode 100755 index 5e3e4a73..c86e8263 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -332,7 +332,7 @@ class Builder extends BaseBuilder $this->set($set); } - if (empty($this->QBSet)) { + if ($this->QBSet === []) { if ($this->db->DBDebug) { throw new DatabaseException('You must use the "set" method to update an entry.'); } @@ -514,7 +514,7 @@ class Builder extends BaseBuilder $this->where($where); } - if (empty($this->QBWhere)) { + if ($this->QBWhere === []) { if ($this->db->DBDebug) { throw new DatabaseException('Deletes are not allowed unless they contain a "where" or "like" clause.'); } @@ -522,7 +522,7 @@ class Builder extends BaseBuilder return false; // @codeCoverageIgnore } - if (! empty($limit)) { + if ($limit !== null && $limit !== 0) { $this->QBLimit = $limit; } @@ -551,7 +551,7 @@ class Builder extends BaseBuilder $sql = (! $this->QBDistinct) ? 'SELECT ' : 'SELECT DISTINCT '; // SQL Server can't work with select * if group by is specified - if (empty($this->QBSelect) && ! empty($this->QBGroupBy) && is_array($this->QBGroupBy)) { + if (empty($this->QBSelect) && $this->QBGroupBy !== [] && is_array($this->QBGroupBy)) { foreach ($this->QBGroupBy as $field) { $this->QBSelect[] = is_array($field) ? $field['field'] : $field; } @@ -573,7 +573,7 @@ class Builder extends BaseBuilder } // Write the "FROM" portion of the query - if (! empty($this->QBFrom)) { + if ($this->QBFrom !== []) { $sql .= "\nFROM " . $this->_fromTables(); } @@ -660,7 +660,7 @@ class Builder extends BaseBuilder }); // if no primary found then look for unique - since indexes have no order - if (empty($uniqueIndexes)) { + if ($uniqueIndexes === []) { $uniqueIndexes = array_filter($tableIndexes, static function ($index) use ($fieldNames) { $hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields); diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php old mode 100644 new mode 100755 index f1a9fb0b..bd9a22e9 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -444,7 +444,7 @@ class Connection extends BaseConnection */ public function setDatabase(?string $databaseName = null) { - if (empty($databaseName)) { + if ($databaseName === null || $databaseName === '') { $databaseName = $this->database; } @@ -538,7 +538,7 @@ class Connection extends BaseConnection return $this->dataCache['version']; } - if (! $this->connID || empty($info = sqlsrv_server_info($this->connID))) { + if (! $this->connID || ($info = sqlsrv_server_info($this->connID)) === []) { $this->initialize(); } diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php old mode 100644 new mode 100755 index e4462476..522d5fa0 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -126,26 +126,30 @@ class Forge extends BaseForge } /** - * @param array|string $field + * @param array|string $processedFields Processed column definitions + * or column names to DROP * - * @return false|string|string[] + * @return false|list|string SQL string or false + * @phpstan-return ($alterType is 'DROP' ? string : list|false) */ - protected function _alterTable(string $alterType, string $table, $field) + protected function _alterTable(string $alterType, string $table, $processedFields) { // Handle DROP here if ($alterType === 'DROP') { + $columnNamesToDrop = $processedFields; + // check if fields are part of any indexes $indexData = $this->db->getIndexData($table); foreach ($indexData as $index) { - if (is_string($field)) { - $field = explode(',', $field); + if (is_string($columnNamesToDrop)) { + $columnNamesToDrop = explode(',', $columnNamesToDrop); } - $fld = array_intersect($field, $index->fields); + $fld = array_intersect($columnNamesToDrop, $index->fields); // Drop index if field is part of an index - if (! empty($fld)) { + if ($fld !== []) { $this->_dropIndex($table, $index); } } @@ -153,7 +157,7 @@ class Forge extends BaseForge $fullTable = $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table); // Drop default constraints - $fields = implode(',', $this->db->escape((array) $field)); + $fields = implode(',', $this->db->escape((array) $columnNamesToDrop)); $sql = << 'COLUMN [' . trim($item) . ']', (array) $field); + $fields = array_map(static fn ($item) => 'COLUMN [' . trim($item) . ']', (array) $columnNamesToDrop); return $sql . implode(',', $fields); } @@ -181,45 +185,45 @@ class Forge extends BaseForge $sqls = []; if ($alterType === 'ADD') { - foreach ($field as $data) { - $sqls[] = $sql . ($data['_literal'] !== false ? $data['_literal'] : $this->_processColumn($data)); + foreach ($processedFields as $field) { + $sqls[] = $sql . ($field['_literal'] !== false ? $field['_literal'] : $this->_processColumn($field)); } return $sqls; } - foreach ($field as $data) { - if ($data['_literal'] !== false) { + foreach ($processedFields as $field) { + if ($field['_literal'] !== false) { return false; } - if (isset($data['type'])) { - $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name']) - . " {$data['type']}{$data['length']}"; + if (isset($field['type'])) { + $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($field['name']) + . " {$field['type']}{$field['length']}"; } - if (! empty($data['default'])) { - $sqls[] = $sql . ' ALTER COLUMN ADD CONSTRAINT ' . $this->db->escapeIdentifiers($data['name']) . '_def' - . " DEFAULT {$data['default']} FOR " . $this->db->escapeIdentifiers($data['name']); + if (! empty($field['default'])) { + $sqls[] = $sql . ' ALTER COLUMN ADD CONSTRAINT ' . $this->db->escapeIdentifiers($field['name']) . '_def' + . " DEFAULT {$field['default']} FOR " . $this->db->escapeIdentifiers($field['name']); } $nullable = true; // Nullable by default. - if (isset($data['null']) && ($data['null'] === false || $data['null'] === ' NOT ' . $this->null)) { + if (isset($field['null']) && ($field['null'] === false || $field['null'] === ' NOT ' . $this->null)) { $nullable = false; } - $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name']) - . " {$data['type']}{$data['length']} " . ($nullable === true ? '' : 'NOT') . ' NULL'; + $sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($field['name']) + . " {$field['type']}{$field['length']} " . ($nullable === true ? '' : 'NOT') . ' NULL'; - if (! empty($data['comment'])) { + if (! empty($field['comment'])) { $sqls[] = 'EXEC sys.sp_addextendedproperty ' - . "@name=N'Caption', @value=N'" . $data['comment'] . "' , " + . "@name=N'Caption', @value=N'" . $field['comment'] . "' , " . "@level0type=N'SCHEMA',@level0name=N'" . $this->db->schema . "', " . "@level1type=N'TABLE',@level1name=N'" . $this->db->escapeIdentifiers($table) . "', " - . "@level2type=N'COLUMN',@level2name=N'" . $this->db->escapeIdentifiers($data['name']) . "'"; + . "@level2type=N'COLUMN',@level2name=N'" . $this->db->escapeIdentifiers($field['name']) . "'"; } - if (! empty($data['new_name'])) { - $sqls[] = "EXEC sp_rename '[" . $this->db->schema . '].[' . $table . '].[' . $data['name'] . "]' , '" . $data['new_name'] . "', 'COLUMN';"; + if (! empty($field['new_name'])) { + $sqls[] = "EXEC sp_rename '[" . $this->db->schema . '].[' . $table . '].[' . $field['name'] . "]' , '" . $field['new_name'] . "', 'COLUMN';"; } } @@ -287,16 +291,16 @@ class Forge extends BaseForge /** * Process column */ - protected function _processColumn(array $field): string + protected function _processColumn(array $processedField): string { - return $this->db->escapeIdentifiers($field['name']) - . (empty($field['new_name']) ? '' : ' ' . $this->db->escapeIdentifiers($field['new_name'])) - . ' ' . $field['type'] . ($field['type'] === 'text' ? '' : $field['length']) - . $field['default'] - . $field['null'] - . $field['auto_increment'] + return $this->db->escapeIdentifiers($processedField['name']) + . (empty($processedField['new_name']) ? '' : ' ' . $this->db->escapeIdentifiers($processedField['new_name'])) + . ' ' . $processedField['type'] . ($processedField['type'] === 'text' ? '' : $processedField['length']) + . $processedField['default'] + . $processedField['null'] + . $processedField['auto_increment'] . '' - . $field['unique']; + . $processedField['unique']; } /** diff --git a/system/Database/SQLSRV/PreparedQuery.php b/system/Database/SQLSRV/PreparedQuery.php old mode 100644 new mode 100755 diff --git a/system/Database/SQLSRV/Result.php b/system/Database/SQLSRV/Result.php old mode 100644 new mode 100755 diff --git a/system/Database/SQLSRV/Utils.php b/system/Database/SQLSRV/Utils.php old mode 100644 new mode 100755 diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index b1dcb1dd..d7112c61 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -109,51 +109,56 @@ class Forge extends BaseForge } /** - * @param array|string $field + * @param array|string $processedFields Processed column definitions + * or column names to DROP * * @return array|string|null + * @return list|string|null SQL string or null + * @phpstan-return ($alterType is 'DROP' ? string : list|null) */ - protected function _alterTable(string $alterType, string $table, $field) + protected function _alterTable(string $alterType, string $table, $processedFields) { switch ($alterType) { case 'DROP': + $columnNamesToDrop = $processedFields; + $sqlTable = new Table($this->db, $this); $sqlTable->fromTable($table) - ->dropColumn($field) + ->dropColumn($columnNamesToDrop) ->run(); - return ''; + return ''; // Why empty string? case 'CHANGE': (new Table($this->db, $this)) ->fromTable($table) - ->modifyColumn($field) + ->modifyColumn($processedFields) // @TODO Bug: should be NOT processed fields ->run(); - return null; + return null; // Why null? default: - return parent::_alterTable($alterType, $table, $field); + return parent::_alterTable($alterType, $table, $processedFields); } } /** * Process column */ - protected function _processColumn(array $field): string + protected function _processColumn(array $processedField): string { - if ($field['type'] === 'TEXT' && strpos($field['length'], "('") === 0) { - $field['type'] .= ' CHECK(' . $this->db->escapeIdentifiers($field['name']) - . ' IN ' . $field['length'] . ')'; + if ($processedField['type'] === 'TEXT' && strpos($processedField['length'], "('") === 0) { + $processedField['type'] .= ' CHECK(' . $this->db->escapeIdentifiers($processedField['name']) + . ' IN ' . $processedField['length'] . ')'; } - return $this->db->escapeIdentifiers($field['name']) - . ' ' . $field['type'] - . $field['auto_increment'] - . $field['null'] - . $field['unique'] - . $field['default']; + return $this->db->escapeIdentifiers($processedField['name']) + . ' ' . $processedField['type'] + . $processedField['auto_increment'] + . $processedField['null'] + . $processedField['unique'] + . $processedField['default']; } /** @@ -183,8 +188,11 @@ class Forge extends BaseForge */ protected function _attributeAutoIncrement(array &$attributes, array &$field) { - if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true - && stripos($field['type'], 'int') !== false) { + if ( + ! empty($attributes['AUTO_INCREMENT']) + && $attributes['AUTO_INCREMENT'] === true + && stripos($field['type'], 'int') !== false + ) { $field['type'] = 'INTEGER PRIMARY KEY'; $field['default'] = ''; $field['null'] = ''; diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 4ed9b515..620d2846 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -12,7 +12,7 @@ namespace CodeIgniter\Database\SQLite3; use CodeIgniter\Database\Exceptions\DataException; -use stdclass; +use stdClass; /** * Class Table @@ -28,8 +28,7 @@ class Table /** * All of the fields this table represents. * - * @var array - * @phpstan-var array> + * @var array> [name => attributes] */ protected $fields = []; @@ -114,7 +113,7 @@ class Table // if primary key index exists twice then remove psuedo index name 'primary'. $primaryIndexes = array_filter($this->keys, static fn ($index) => $index['type'] === 'primary'); - if (! empty($primaryIndexes) && count($primaryIndexes) > 1 && array_key_exists('primary', $this->keys)) { + if ($primaryIndexes !== [] && count($primaryIndexes) > 1 && array_key_exists('primary', $this->keys)) { unset($this->keys['primary']); } @@ -157,7 +156,7 @@ class Table /** * Drops columns from the table. * - * @param array|string $columns + * @param list|string $columns Column names to drop. * * @return Table */ @@ -178,14 +177,15 @@ class Table } /** - * Modifies a field, including changing data type, - * renaming, etc. + * Modifies a field, including changing data type, renaming, etc. + * + * @param list> $fieldsToModify * * @return Table */ - public function modifyColumn(array $fields) + public function modifyColumn(array $fieldsToModify) { - foreach ($fields as $field) { + foreach ($fieldsToModify as $field) { $oldName = $field['name']; unset($field['name']); diff --git a/system/Database/Seeder.php b/system/Database/Seeder.php index f51fa417..c0f267d2 100644 --- a/system/Database/Seeder.php +++ b/system/Database/Seeder.php @@ -25,8 +25,7 @@ class Seeder /** * The name of the database group to use. * - * @var string - * @phpstan-var non-empty-string + * @var non-empty-string */ protected $DBGroup; @@ -79,7 +78,7 @@ class Seeder { $this->seedPath = $config->filesPath ?? APPPATH . 'Database/'; - if (empty($this->seedPath)) { + if ($this->seedPath === '') { throw new InvalidArgumentException('Invalid filesPath set in the Config\Database.'); } diff --git a/system/Debug/BaseExceptionHandler.php b/system/Debug/BaseExceptionHandler.php index 0330c53c..6a5b5e47 100644 --- a/system/Debug/BaseExceptionHandler.php +++ b/system/Debug/BaseExceptionHandler.php @@ -67,7 +67,14 @@ abstract class BaseExceptionHandler */ protected function collectVars(Throwable $exception, int $statusCode): array { - $trace = $exception->getTrace(); + // Get the first exception. + $firstException = $exception; + + while ($prevException = $firstException->getPrevious()) { + $firstException = $prevException; + } + + $trace = $firstException->getTrace(); if ($this->config->sensitiveDataInTrace !== []) { $trace = $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace); @@ -154,7 +161,7 @@ abstract class BaseExceptionHandler */ protected static function highlightFile(string $file, int $lineNumber, int $lines = 15) { - if (empty($file) || ! is_readable($file)) { + if ($file === '' || ! is_readable($file)) { return false; } @@ -175,8 +182,15 @@ abstract class BaseExceptionHandler $source = str_replace(["\r\n", "\r"], "\n", $source); $source = explode("\n", highlight_string($source, true)); - $source = str_replace('
', "\n", $source[1]); - $source = explode("\n", str_replace("\r\n", "\n", $source)); + + if (PHP_VERSION_ID < 80300) { + $source = str_replace('
', "\n", $source[1]); + $source = explode("\n", str_replace("\r\n", "\n", $source)); + } else { + // We have to remove these tags since we're preparing the result + // ourselves and these tags are added manually at the end. + $source = str_replace(['
', '
'], '', $source); + } // Get just the part to show $start = max($lineNumber - (int) round($lines / 2), 0); @@ -192,7 +206,7 @@ abstract class BaseExceptionHandler // of open and close span tags on one line, we need // to ensure we can close them all to get the lines // showing correctly. - $spans = 1; + $spans = 0; foreach ($source as $n => $row) { $spans += substr_count($row, '' . $format . ' %s', $n + $start + 1, $row) . "\n"; + // We're closing only one span tag we added manually line before, + // so we have to increment $spans count to close this tag later. + $spans++; } } @@ -232,7 +249,7 @@ abstract class BaseExceptionHandler exit(1); } - echo(function () use ($exception, $statusCode, $viewFile): string { + echo (function () use ($exception, $statusCode, $viewFile): string { $vars = $this->collectVars($exception, $statusCode); extract($vars, EXTR_SKIP); diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index bbcfcc8e..1f5a5b52 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -117,7 +117,6 @@ class Exceptions * and fire an event that allows custom actions to be taken at this point. * * @return void - * @phpstan-return never|void */ public function exceptionHandler(Throwable $exception) { @@ -126,22 +125,31 @@ class Exceptions [$statusCode, $exitCode] = $this->determineCodes($exception); if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes, true)) { - log_message('critical', "{message}\nin {exFile} on line {exLine}.\n{trace}", [ + log_message('critical', get_class($exception) . ": {message}\nin {exFile} on line {exLine}.\n{trace}", [ 'message' => $exception->getMessage(), 'exFile' => clean_path($exception->getFile()), // {file} refers to THIS file 'exLine' => $exception->getLine(), // {line} refers to THIS line 'trace' => self::renderBacktrace($exception->getTrace()), ]); + + // Get the first exception. + $last = $exception; + + while ($prevException = $last->getPrevious()) { + $last = $prevException; + + log_message('critical', '[Caused by] ' . get_class($prevException) . ": {message}\nin {exFile} on line {exLine}.\n{trace}", [ + 'message' => $prevException->getMessage(), + 'exFile' => clean_path($prevException->getFile()), // {file} refers to THIS file + 'exLine' => $prevException->getLine(), // {line} refers to THIS line + 'trace' => self::renderBacktrace($prevException->getTrace()), + ]); + } } $this->request = Services::request(); $this->response = Services::response(); - // Get the first exception. - while ($prevException = $exception->getPrevious()) { - $exception = $prevException; - } - if (method_exists($this->config, 'handler')) { // Use new ExceptionHandler $handler = $this->config->handler($statusCode, $exception); @@ -215,7 +223,6 @@ class Exceptions * @codeCoverageIgnore * * @return void - * @phpstan-return never|void */ public function shutdownHandler() { @@ -280,7 +287,6 @@ class Exceptions * Given an exception and status code will display the error to the client. * * @return void - * @phpstan-return never|void * * @deprecated 4.4.0 No longer used. Moved to BaseExceptionHandler. */ @@ -310,7 +316,7 @@ class Exceptions exit(1); } - echo(function () use ($exception, $statusCode, $viewFile): string { + echo (function () use ($exception, $statusCode, $viewFile): string { $vars = $this->collectVars($exception, $statusCode); extract($vars, EXTR_SKIP); @@ -328,7 +334,14 @@ class Exceptions */ protected function collectVars(Throwable $exception, int $statusCode): array { - $trace = $exception->getTrace(); + // Get the first exception. + $firstException = $exception; + + while ($prevException = $firstException->getPrevious()) { + $firstException = $prevException; + } + + $trace = $firstException->getTrace(); if ($this->config->sensitiveDataInTrace !== []) { $trace = $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace); @@ -509,7 +522,7 @@ class Exceptions */ public static function highlightFile(string $file, int $lineNumber, int $lines = 15) { - if (empty($file) || ! is_readable($file)) { + if ($file === '' || ! is_readable($file)) { return false; } diff --git a/system/Debug/Iterator.php b/system/Debug/Iterator.php index f54335b6..4bfb3a5b 100644 --- a/system/Debug/Iterator.php +++ b/system/Debug/Iterator.php @@ -38,7 +38,7 @@ class Iterator * Tests are simply closures that the user can define any sequence of * things to happen during the test. * - * @phpstan-param Closure(): mixed $closure + * @param Closure(): mixed $closure * * @return $this */ @@ -93,7 +93,7 @@ class Iterator */ public function getReport(): string { - if (empty($this->results)) { + if ($this->results === []) { return 'No results to display.'; } diff --git a/system/Debug/Timer.php b/system/Debug/Timer.php index d43bf1c5..45c0bf86 100644 --- a/system/Debug/Timer.php +++ b/system/Debug/Timer.php @@ -133,10 +133,10 @@ class Timer * Executes callable and measures its time. * Returns its return value if any. * - * @param string $name The name of the timer - * @phpstan-param callable(): mixed $callable callable to be executed + * @param string $name The name of the timer + * @param callable(): mixed $callable callable to be executed * - * @return array|bool|float|int|object|resource|string|null + * @return mixed */ public function record(string $name, callable $callable) { diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 73c777ba..b99d8392 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -121,7 +121,7 @@ class Toolbar $data['vars']['varData'][esc($heading)] = $varData; } - if (! empty($_SESSION)) { + if (isset($_SESSION)) { foreach ($_SESSION as $key => $value) { // Replace the binary data with string to avoid json_encode failure. if (is_string($value) && preg_match('~[^\x20-\x7E\t\r\n]~', $value)) { @@ -292,7 +292,7 @@ class Toolbar $element = array_shift($elements); // If we have children behind us, collect and attach them to us - while (! empty($elements) && $elements[array_key_first($elements)]['end'] <= $element['end']) { + while ($elements !== [] && $elements[array_key_first($elements)]['end'] <= $element['end']) { $element['children'][] = array_shift($elements); } @@ -302,7 +302,7 @@ class Toolbar } // If we have no younger siblings, we can return - if (empty($elements)) { + if ($elements === []) { return [$element]; } @@ -435,7 +435,6 @@ class Toolbar * @codeCoverageIgnore * * @return void - * @phpstan-return never|void */ public function respond() { diff --git a/system/Debug/Toolbar/Collectors/Database.php b/system/Debug/Toolbar/Collectors/Database.php index 4b2ff96f..5b5614ad 100644 --- a/system/Debug/Toolbar/Collectors/Database.php +++ b/system/Debug/Toolbar/Collectors/Database.php @@ -80,7 +80,6 @@ class Database extends BaseCollector * @internal * * @return void - * @phpstan-return never|void */ public static function collect(Query $query) { @@ -236,7 +235,7 @@ class Database extends BaseCollector */ public function isEmpty(): bool { - return empty(static::$queries); + return static::$queries === []; } /** diff --git a/system/Debug/Toolbar/Collectors/History.php b/system/Debug/Toolbar/Collectors/History.php index a639e35a..fcf06e16 100644 --- a/system/Debug/Toolbar/Collectors/History.php +++ b/system/Debug/Toolbar/Collectors/History.php @@ -128,7 +128,7 @@ class History extends BaseCollector */ public function isEmpty(): bool { - return empty($this->files); + return $this->files === []; } /** diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index 89555b4f..8a302b40 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -532,9 +532,6 @@ #debug-bar .timeline .timer { background-color: #DD8615; } - #debug-bar .timeline .timeline-parent-open td { - color: #252525; - } .debug-view.show-view { border-color: #DD8615; } @@ -647,9 +644,6 @@ #toolbarContainer.dark #debug-bar .timeline .timer { background-color: #DD8615; } -#toolbarContainer.dark #debug-bar .timeline .timeline-parent-open td { - color: #252525; -} #toolbarContainer.dark .debug-view.show-view { border-color: #DD8615; } diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 6e62340c..3652a536 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -1,21 +1,21 @@
diff --git a/system/Email/Email.php b/system/Email/Email.php index a6ad2d99..42835c3d 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -71,7 +71,7 @@ class Email public $protocol = 'mail'; /** - * STMP Server host + * STMP Server Hostname * * @var string */ @@ -352,8 +352,7 @@ class Email * Character sets valid for 7-bit encoding, * excluding language suffix. * - * @var array - * @phpstan-var list + * @var list */ protected $baseCharsets = [ 'us-ascii', @@ -706,6 +705,7 @@ class Email public function setAttachmentCID($filename) { foreach ($this->attachments as $i => $attachment) { + // For file path. if ($attachment['name'][0] === $filename) { $this->attachments[$i]['multipart'] = 'related'; @@ -713,6 +713,15 @@ class Email return $this->attachments[$i]['cid']; } + + // For buffer string. + if ($attachment['name'][1] === $filename) { + $this->attachments[$i]['multipart'] = 'related'; + + $this->attachments[$i]['cid'] = uniqid(basename($attachment['name'][1]) . '@', true); + + return $this->attachments[$i]['cid']; + } } return false; @@ -732,7 +741,7 @@ class Email } /** - * @param string $email + * @param array|string $email * * @return array */ @@ -1237,13 +1246,13 @@ class Email . $this->prepQuotedPrintable($this->body) . $this->newline . $this->newline . '--' . $altBoundary . '--' . $this->newline . $this->newline; - if (! empty($relBoundary)) { + if (isset($relBoundary)) { $body .= $this->newline . $this->newline; $this->appendAttachments($body, $relBoundary, 'related'); } // multipart/mixed attachments - if (! empty($atcBoundary)) { + if (isset($atcBoundary)) { $body .= $this->newline . $this->newline; $this->appendAttachments($body, $atcBoundary, 'mixed'); } diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php index 65898c01..0e751b68 100644 --- a/system/Entity/Entity.php +++ b/system/Entity/Entity.php @@ -55,8 +55,7 @@ class Entity implements JsonSerializable /** * The date fields. * - * @var array - * @phpstan-var list + * @var list */ protected $dates = [ 'created_at', @@ -320,7 +319,7 @@ class Entity implements JsonSerializable */ protected function mapProperty(string $key) { - if (empty($this->datamap)) { + if ($this->datamap === []) { return $key; } diff --git a/system/Files/File.php b/system/Files/File.php index 4e4ee388..01131129 100644 --- a/system/Files/File.php +++ b/system/Files/File.php @@ -140,7 +140,7 @@ class File extends SplFileInfo public function move(string $targetPath, ?string $name = null, bool $overwrite = false) { $targetPath = rtrim($targetPath, '/') . '/'; - $name ??= $this->getBaseName(); + $name ??= $this->getBasename(); $destination = $overwrite ? $targetPath . $name : $this->getDestination($targetPath . $name); $oldName = $this->getRealPath() ?: $this->__toString(); diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index df27e3f6..18001b10 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -87,16 +87,14 @@ class Filters /** * Any arguments to be passed to filters. * - * @var array|null> [name => params] - * @phpstan-var array|null> + * @var array|null> [name => params] */ protected $arguments = []; /** * Any arguments to be passed to filtersClass. * - * @var array [classname => arguments] - * @phpstan-var array>|null> + * @var array>|null> [classname => arguments] */ protected $argumentsClass = []; @@ -373,8 +371,7 @@ class Filters * * @param string $name filter_name or filter_name:arguments like 'role:admin,manager' * - * @return array [name, arguments] - * @phpstan-return array{0: string, 1: list} + * @return array{0: string, 1: list} [name, arguments] */ private function getCleanName(string $name): array { diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 878e8a1d..01823dbf 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -95,7 +95,7 @@ class CLIRequest extends Request { $path = implode('/', $this->segments); - return empty($path) ? '' : $path; + return ($path === '') ? '' : $path; } /** @@ -147,7 +147,7 @@ class CLIRequest extends Request */ public function getOptionString(bool $useLongOpts = false): string { - if (empty($this->options)) { + if ($this->options === []) { return ''; } diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index c8b3b9fe..500b425d 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -549,16 +549,18 @@ class CURLRequest extends OutgoingRequest // SSL Verification if (isset($config['verify'])) { if (is_string($config['verify'])) { - $file = realpath($config['ssl_key']) ?: $config['ssl_key']; + $file = realpath($config['verify']) ?: $config['verify']; if (! is_file($file)) { - throw HTTPException::forInvalidSSLKey($config['ssl_key']); + throw HTTPException::forInvalidSSLKey($config['verify']); } $curlOptions[CURLOPT_CAINFO] = $file; - $curlOptions[CURLOPT_SSL_VERIFYPEER] = 1; + $curlOptions[CURLOPT_SSL_VERIFYPEER] = true; + $curlOptions[CURLOPT_SSL_VERIFYHOST] = 2; } elseif (is_bool($config['verify'])) { $curlOptions[CURLOPT_SSL_VERIFYPEER] = $config['verify']; + $curlOptions[CURLOPT_SSL_VERIFYHOST] = $config['verify'] ? 2 : 0; } } diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 37b0ce65..f4970bf7 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -790,7 +790,7 @@ class ContentSecurityPolicy $reportSources = []; foreach ($values as $value => $reportOnly) { - if (is_numeric($value) && is_string($reportOnly) && ! empty($reportOnly)) { + if (is_numeric($value) && is_string($reportOnly) && ($reportOnly !== '')) { $value = $reportOnly; $reportOnly = $this->reportOnly; } @@ -806,11 +806,11 @@ class ContentSecurityPolicy } } - if (! empty($sources)) { + if ($sources !== []) { $this->tempHeaders[$name] = implode(' ', $sources); } - if (! empty($reportSources)) { + if ($reportSources !== []) { $this->reportOnlyHeaders[$name] = implode(' ', $reportSources); } } diff --git a/system/HTTP/Exceptions/HTTPException.php b/system/HTTP/Exceptions/HTTPException.php index d22c48bc..3fcfe73f 100644 --- a/system/HTTP/Exceptions/HTTPException.php +++ b/system/HTTP/Exceptions/HTTPException.php @@ -72,6 +72,19 @@ class HTTPException extends FrameworkException return new static(lang('HTTP.invalidNegotiationType', [$type])); } + /** + * Thrown in IncomingRequest when the json_decode() produces + * an error code other than JSON_ERROR_NONE. + * + * @param string $error The error message + * + * @return static + */ + public static function forInvalidJSON(?string $error = null) + { + return new static(lang('HTTP.invalidJSON', [$error])); + } + /** * For Message * @@ -215,4 +228,15 @@ class HTTPException extends FrameworkException { return new static(lang('Security.invalidSameSiteSetting', [$samesite])); } + + /** + * Thrown when the JSON format is not supported. + * This is specifically for cases where data validation is expected to work with key-value structures. + * + * @return HTTPException + */ + public static function forUnsupportedJSONFormat() + { + return new static(lang('HTTP.unsupportedJSONFormat')); + } } diff --git a/system/HTTP/Files/FileCollection.php b/system/HTTP/Files/FileCollection.php index f01cc69f..124381ec 100644 --- a/system/HTTP/Files/FileCollection.php +++ b/system/HTTP/Files/FileCollection.php @@ -77,7 +77,7 @@ class FileCollection /** * Verify if a file exist in the collection of uploaded files and is have been uploaded with multiple option. * - * @return array|null + * @return list|null */ public function getFileMultiple(string $name) { @@ -148,7 +148,7 @@ class FileCollection $this->files = []; - if (empty($_FILES)) { + if ($_FILES === []) { return; } @@ -245,7 +245,7 @@ class FileCollection * @param array $index The index sequence we are navigating down * @param array $value The portion of the array to process * - * @return UploadedFile|null + * @return list|UploadedFile|null */ protected function getValueDotNotationSyntax(array $index, array $value) { diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php old mode 100644 new mode 100755 index 079a3b69..8bc246f7 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -152,7 +152,7 @@ class IncomingRequest extends Request */ public function __construct($config, ?URI $uri = null, $body = 'php://input', ?UserAgent $userAgent = null) { - if (empty($uri) || empty($userAgent)) { + if (! $uri instanceof URI || ! $userAgent instanceof UserAgent) { throw new InvalidArgumentException('You must supply the parameters: uri, userAgent.'); } @@ -169,9 +169,14 @@ class IncomingRequest extends Request $body = file_get_contents('php://input'); } + // If file_get_contents() returns false or empty string, set null. + if ($body === false || $body === '') { + $body = null; + } + $this->config = $config; $this->uri = $uri; - $this->body = ! empty($body) ? $body : null; + $this->body = $body; $this->userAgent = $userAgent; $this->validLocales = $config->supportedLocales; @@ -251,7 +256,7 @@ class IncomingRequest extends Request */ public function detectPath(string $protocol = ''): string { - if (empty($protocol)) { + if ($protocol === '') { $protocol = 'REQUEST_URI'; } @@ -568,10 +573,22 @@ class IncomingRequest extends Request * @see http://php.net/manual/en/function.json-decode.php * * @return array|bool|float|int|stdClass|null + * + * @throws HTTPException When the body is invalid as JSON. */ public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0) { - return json_decode($this->body ?? '', $assoc, $depth, $options); + if ($this->body === null) { + return null; + } + + $result = json_decode($this->body, $assoc, $depth, $options); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw HTTPException::forInvalidJSON(json_last_error_msg()); + } + + return $result; } /** diff --git a/system/HTTP/Negotiate.php b/system/HTTP/Negotiate.php index b1d5fdfa..9c903908 100644 --- a/system/HTTP/Negotiate.php +++ b/system/HTTP/Negotiate.php @@ -93,7 +93,7 @@ class Negotiate // If no charset is shown as a match, ignore the directive // as allowed by the RFC, and tell it a default value. - if (empty($match)) { + if ($match === '') { return 'utf-8'; } @@ -154,11 +154,11 @@ class Negotiate bool $strictMatch = false, bool $matchLocales = false ): string { - if (empty($supported)) { + if ($supported === []) { throw HTTPException::forEmptySupportedNegotiations(); } - if (empty($header)) { + if ($header === null || $header === '') { return $strictMatch ? '' : $supported[0]; } diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index 52ffa812..6db903dd 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -35,10 +35,9 @@ trait RequestTrait protected $ipAddress = ''; /** - * Stores values we've retrieved from - * PHP globals. + * Stores values we've retrieved from PHP globals. * - * @var array + * @var array{get?: array, post?: array, request?: array, cookie?: array, server?: array} */ protected $globals = []; @@ -204,22 +203,27 @@ trait RequestTrait * @param array|int|null $flags * * @return mixed + * + * @deprecated 4.4.4 This method does not work from the beginning. Use `env()`. */ public function getEnv($index = null, $filter = null, $flags = null) { + // @phpstan-ignore-next-line return $this->fetchGlobal('env', $index, $filter, $flags); } /** * Allows manually setting the value of PHP global, like $_GET, $_POST, etc. * + * @param string $name Supergrlobal name (lowercase) + * @phpstan-param 'get'|'post'|'request'|'cookie'|'server' $name * @param mixed $value * * @return $this */ - public function setGlobal(string $method, $value) + public function setGlobal(string $name, $value) { - $this->globals[$method] = $value; + $this->globals[$name] = $value; return $this; } @@ -234,19 +238,18 @@ trait RequestTrait * * http://php.net/manual/en/filter.filters.sanitize.php * - * @param string $method Input filter constant + * @param string $name Supergrlobal name (lowercase) + * @phpstan-param 'get'|'post'|'request'|'cookie'|'server' $name * @param array|string|null $index * @param int|null $filter Filter constant * @param array|int|null $flags Options * * @return array|bool|float|int|object|string|null */ - public function fetchGlobal(string $method, $index = null, ?int $filter = null, $flags = null) + public function fetchGlobal(string $name, $index = null, ?int $filter = null, $flags = null) { - $method = strtolower($method); - - if (! isset($this->globals[$method])) { - $this->populateGlobals($method); + if (! isset($this->globals[$name])) { + $this->populateGlobals($name); } // Null filters cause null values to return. @@ -257,9 +260,9 @@ trait RequestTrait if ($index === null) { $values = []; - foreach ($this->globals[$method] as $key => $value) { + foreach ($this->globals[$name] as $key => $value) { $values[$key] = is_array($value) - ? $this->fetchGlobal($method, $key, $filter, $flags) + ? $this->fetchGlobal($name, $key, $filter, $flags) : filter_var($value, $filter, $flags); } @@ -271,7 +274,7 @@ trait RequestTrait $output = []; foreach ($index as $key) { - $output[$key] = $this->fetchGlobal($method, $key, $filter, $flags); + $output[$key] = $this->fetchGlobal($name, $key, $filter, $flags); } return $output; @@ -279,7 +282,7 @@ trait RequestTrait // Does the index contain array notation? if (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) { - $value = $this->globals[$method]; + $value = $this->globals[$name]; for ($i = 0; $i < $count; $i++) { $key = trim($matches[0][$i], '[]'); @@ -297,7 +300,7 @@ trait RequestTrait } if (! isset($value)) { - $value = $this->globals[$method][$index] ?? null; + $value = $this->globals[$name][$index] ?? null; } if (is_array($value) @@ -326,20 +329,23 @@ trait RequestTrait } /** - * Saves a copy of the current state of one of several PHP globals + * Saves a copy of the current state of one of several PHP globals, * so we can retrieve them later. * + * @param string $name Superglobal name (lowercase) + * @phpstan-param 'get'|'post'|'request'|'cookie'|'server' $name + * * @return void */ - protected function populateGlobals(string $method) + protected function populateGlobals(string $name) { - if (! isset($this->globals[$method])) { - $this->globals[$method] = []; + if (! isset($this->globals[$name])) { + $this->globals[$name] = []; } // Don't populate ENV as it might contain // sensitive data that we don't want to get logged. - switch ($method) { + switch ($name) { case 'get': $this->globals['get'] = $_GET; break; diff --git a/system/HTTP/ResponseInterface.php b/system/HTTP/ResponseInterface.php index 878d1488..a6d41c77 100644 --- a/system/HTTP/ResponseInterface.php +++ b/system/HTTP/ResponseInterface.php @@ -110,8 +110,6 @@ interface ResponseInterface extends MessageInterface * to understand and satisfy the request. * * @return int Status code. - * - * @deprecated To be replaced by the PSR-7 version (compatible) */ public function getStatusCode(): int; diff --git a/system/HTTP/ResponseTrait.php b/system/HTTP/ResponseTrait.php index 7c074131..b385bfea 100644 --- a/system/HTTP/ResponseTrait.php +++ b/system/HTTP/ResponseTrait.php @@ -156,13 +156,13 @@ trait ResponseTrait } // Unknown and no message? - if (! array_key_exists($code, static::$statusCodes) && empty($reason)) { + if (! array_key_exists($code, static::$statusCodes) && ($reason === '')) { throw HTTPException::forUnkownStatusCode($code); } $this->statusCode = $code; - $this->reason = ! empty($reason) ? $reason : static::$statusCodes[$code]; + $this->reason = ($reason !== '') ? $reason : static::$statusCodes[$code]; return $this; } @@ -226,7 +226,7 @@ trait ResponseTrait public function setContentType(string $mime, string $charset = 'UTF-8') { // add charset attribute if not already there and provided as parm - if ((strpos($mime, 'charset=') < 1) && ! empty($charset)) { + if ((strpos($mime, 'charset=') < 1) && ($charset !== '')) { $mime .= '; charset=' . $charset; } @@ -377,7 +377,7 @@ trait ResponseTrait */ public function setCache(array $options = []) { - if (empty($options)) { + if ($options === []) { return $this; } diff --git a/system/HTTP/SiteURI.php b/system/HTTP/SiteURI.php index d01f614a..6d565073 100644 --- a/system/HTTP/SiteURI.php +++ b/system/HTTP/SiteURI.php @@ -197,7 +197,7 @@ class SiteURI extends URI // Validate baseURL if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) { throw new ConfigException( - 'Config\App::$baseURL is invalid.' + 'Config\App::$baseURL "' . $baseURL . '" is not a valid URL.' ); } diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 608f8b75..5bec3801 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -163,11 +163,11 @@ class URI ?string $fragment = null ): string { $uri = ''; - if (! empty($scheme)) { + if ($scheme !== null && $scheme !== '') { $uri .= $scheme . '://'; } - if (! empty($authority)) { + if ($authority !== null && $authority !== '') { $uri .= $authority; } @@ -886,7 +886,7 @@ class URI } // Can't have leading ? - if (! empty($query) && strpos($query, '?') === 0) { + if ($query !== '' && strpos($query, '?') === 0) { $query = substr($query, 1); } @@ -1097,7 +1097,7 @@ class URI $transformed = clone $relative; // 5.2.2 Transform References in a non-strict method (no scheme) - if (! empty($relative->getAuthority())) { + if ($relative->getAuthority() !== '') { $transformed ->setAuthority($relative->getAuthority()) ->setPath($relative->getPath()) @@ -1139,7 +1139,7 @@ class URI */ protected function mergePaths(self $base, self $reference): string { - if (! empty($base->getAuthority()) && $base->getPath() === '') { + if ($base->getAuthority() !== '' && $base->getPath() === '') { return '/' . ltrim($reference->getPath(), '/ '); } diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 77e7cb43..2294b02a 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -260,7 +260,7 @@ class UserAgent // Set the new user-agent string and parse it, unless empty $this->agent = $string; - if (! empty($string)) { + if ($string !== '') { $this->compileData(); } } diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index 65645c3c..b8bd1a4d 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -85,7 +85,7 @@ if (! function_exists('_array_search_dot')) { // If this is the last index, make sure to return it now, // and not try to recurse through things. - if (empty($indexes)) { + if ($indexes === []) { return $array[$currentIndex]; } @@ -152,7 +152,7 @@ if (! function_exists('array_sort_by_multiple_keys')) { function array_sort_by_multiple_keys(array &$array, array $sortColumns): bool { // Check if there really are columns to sort after - if (empty($sortColumns) || empty($array)) { + if ($sortColumns === [] || $array === []) { return false; } diff --git a/system/Helpers/cookie_helper.php b/system/Helpers/cookie_helper.php old mode 100644 new mode 100755 diff --git a/system/Helpers/date_helper.php b/system/Helpers/date_helper.php index ba9c9a24..cf1dc33a 100644 --- a/system/Helpers/date_helper.php +++ b/system/Helpers/date_helper.php @@ -20,11 +20,13 @@ if (! function_exists('now')) { * Returns Time::now()->getTimestamp() based on the timezone parameter or on the * app_timezone() setting * + * @param non-empty-string|null $timezone + * * @throws Exception */ function now(?string $timezone = null): int { - $timezone = empty($timezone) ? app_timezone() : $timezone; + $timezone = ($timezone === null || $timezone === '') ? app_timezone() : $timezone; if ($timezone === 'local' || $timezone === date_default_timezone_get()) { return Time::now()->getTimestamp(); diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 47c829bb..4e0e8b15 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -209,7 +209,7 @@ if (! function_exists('get_filenames')) { try { foreach (new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS), + new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS), RecursiveIteratorIterator::SELF_FIRST ) as $name => $object) { $basename = pathinfo($name, PATHINFO_BASENAME); diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 7edb05cf..d3abd7e2 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -288,7 +288,7 @@ if (! function_exists('form_dropdown')) { } // If no selected state was submitted we will attempt to set it automatically - if (empty($selected)) { + if ($selected === []) { if (is_array($data)) { if (isset($data['name'], $_POST[$data['name']])) { $selected = [$_POST[$data['name']]]; @@ -312,7 +312,7 @@ if (! function_exists('form_dropdown')) { $key = (string) $key; if (is_array($val)) { - if (empty($val)) { + if ($val === []) { continue; } @@ -784,7 +784,7 @@ if (! function_exists('parse_form_attributes')) { unset($attributes[$key]); } } - if (! empty($attributes)) { + if ($attributes !== []) { $default = array_merge($default, $attributes); } } diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php old mode 100644 new mode 100755 index c7a36e27..075dcd51 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -313,7 +313,7 @@ if (! function_exists('video')) { $video .= _space_indent() . $track . "\n"; } - if (! empty($unsupportedMessage)) { + if ($unsupportedMessage !== '') { $video .= _space_indent() . $unsupportedMessage . "\n"; @@ -359,7 +359,7 @@ if (! function_exists('audio')) { $audio .= "\n" . _space_indent() . $track; } - if (! empty($unsupportedMessage)) { + if ($unsupportedMessage !== '') { $audio .= "\n" . _space_indent() . $unsupportedMessage . "\n"; } @@ -377,7 +377,7 @@ if (! function_exists('_media')) { { $media = '<' . $name; - if (empty($attributes)) { + if ($attributes === '') { $media .= '>'; } else { $media .= ' ' . $attributes . '>'; @@ -393,7 +393,7 @@ if (! function_exists('_media')) { $media .= _space_indent() . $track . "\n"; } - if (! empty($unsupportedMessage)) { + if ($unsupportedMessage !== '') { $media .= _space_indent() . $unsupportedMessage . "\n"; } @@ -421,7 +421,7 @@ if (! function_exists('source')) { $source = ''; - if (! empty($params)) { + if ($params !== []) { $object .= "\n"; } diff --git a/system/Helpers/inflector_helper.php b/system/Helpers/inflector_helper.php old mode 100644 new mode 100755 diff --git a/system/Helpers/kint_helper.php b/system/Helpers/kint_helper.php index eaa2e947..4221573a 100644 --- a/system/Helpers/kint_helper.php +++ b/system/Helpers/kint_helper.php @@ -18,7 +18,7 @@ if (! function_exists('dd')) { * * @param array $vars * - * @phpstan-return never + * @return never * * @codeCoverageIgnore Can't be tested ... exits */ diff --git a/system/Helpers/number_helper.php b/system/Helpers/number_helper.php index 74173501..8ca5d3e2 100644 --- a/system/Helpers/number_helper.php +++ b/system/Helpers/number_helper.php @@ -15,7 +15,8 @@ if (! function_exists('number_to_size')) { /** * Formats a numbers as bytes, based on size, and adds the appropriate suffix * - * @param int|string $num Will be cast as int + * @param int|string $num Will be cast as int + * @param non-empty-string|null $locale [optional] * * @return bool|string */ @@ -32,7 +33,7 @@ if (! function_exists('number_to_size')) { // ignore sub part $generalLocale = $locale; - if (! empty($locale) && ($underscorePos = strpos($locale, '_'))) { + if ($locale !== null && $locale !== '' && ($underscorePos = strpos($locale, '_'))) { $generalLocale = substr($locale, 0, $underscorePos); } @@ -68,9 +69,9 @@ if (! function_exists('number_to_amount')) { * * @see https://simple.wikipedia.org/wiki/Names_for_large_numbers * - * @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] + * @param int|string $num Will be cast as int + * @param int $precision [optional] The optional number of decimal digits to round to. + * @param non-empty-string|null $locale [optional] * * @return bool|string */ @@ -88,7 +89,7 @@ if (! function_exists('number_to_amount')) { // ignore sub part $generalLocale = $locale; - if (! empty($locale) && ($underscorePos = strpos($locale, '_'))) { + if ($locale !== null && $locale !== '' && ($underscorePos = strpos($locale, '_'))) { $generalLocale = substr($locale, 0, $underscorePos); } diff --git a/system/Helpers/text_helper.php b/system/Helpers/text_helper.php old mode 100644 new mode 100755 index 56cf856f..7db126fa --- a/system/Helpers/text_helper.php +++ b/system/Helpers/text_helper.php @@ -98,7 +98,7 @@ if (! function_exists('ascii_to_entities')) { $out .= $str[$i]; } else { - if (empty($temp)) { + if ($temp === []) { $count = ($ordinal < 224) ? 2 : 3; } @@ -172,7 +172,7 @@ if (! function_exists('word_censor')) { */ function word_censor(string $str, array $censored, string $replacement = ''): string { - if (empty($censored)) { + if ($censored === []) { return $str; } @@ -702,8 +702,6 @@ if (! function_exists('excerpt')) { * @param int $radius The amount of characters returned around the phrase. * @param string $ellipsis Ending that will be appended * - * @return string - * * If no $phrase is passed, will generate an excerpt of $radius characters * from the beginning of $text. */ diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index 27e3ce1d..8099558a 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -290,7 +290,7 @@ if (! function_exists('safe_mailto')) { if ($ordinal < 128) { $x[] = '|' . $ordinal; } else { - if (empty($temp)) { + if ($temp === []) { $count = ($ordinal < 224) ? 2 : 3; } diff --git a/system/Honeypot/Honeypot.php b/system/Honeypot/Honeypot.php index 82da9002..c0998c47 100644 --- a/system/Honeypot/Honeypot.php +++ b/system/Honeypot/Honeypot.php @@ -44,7 +44,7 @@ class Honeypot throw HoneypotException::forNoHiddenValue(); } - if (empty($this->config->container) || strpos($this->config->container, '{template}') === false) { + if ($this->config->container === '' || strpos($this->config->container, '{template}') === false) { $this->config->container = '
{template}
'; } @@ -78,6 +78,10 @@ class Honeypot */ public function attachHoneypot(ResponseInterface $response) { + if ($response->getBody() === null) { + return; + } + if ($response->getCSP()->enabled()) { // Add id attribute to the container tag. $this->config->container = str_ireplace( diff --git a/system/HotReloader/HotReloader.php b/system/HotReloader/HotReloader.php index 564ba544..579bcd4a 100644 --- a/system/HotReloader/HotReloader.php +++ b/system/HotReloader/HotReloader.php @@ -18,6 +18,10 @@ final class HotReloader { public function run(): void { + if (session_status() === PHP_SESSION_ACTIVE) { + session_write_close(); + } + ini_set('zlib.output_compression', 'Off'); header('Cache-Control: no-store'); diff --git a/system/I18n/Time.php b/system/I18n/Time.php index 520f38eb..f01718aa 100644 --- a/system/I18n/Time.php +++ b/system/I18n/Time.php @@ -19,7 +19,23 @@ use DateTimeImmutable; * * Requires the intl PHP extension. * - * @property string $date + * @property int $age read-only + * @property string $day read-only + * @property string $dayOfWeek read-only + * @property string $dayOfYear read-only + * @property bool $dst read-only + * @property string $hour read-only + * @property bool $local read-only + * @property string $minute read-only + * @property string $month read-only + * @property string $quarter read-only + * @property string $second read-only + * @property int $timestamp read-only + * @property bool $utc read-only + * @property string $weekOfMonth read-only + * @property string $weekOfYear read-only + * @property string $year read-only + * * @see \CodeIgniter\I18n\TimeTest */ class Time extends DateTimeImmutable diff --git a/system/I18n/TimeLegacy.php b/system/I18n/TimeLegacy.php index 7be9f80f..35523e32 100644 --- a/system/I18n/TimeLegacy.php +++ b/system/I18n/TimeLegacy.php @@ -20,7 +20,22 @@ use DateTime; * This is not immutable! Some methods are immutable, * but some methods can alter the state. * - * @property string $date + * @property int $age read-only + * @property string $day read-only + * @property string $dayOfWeek read-only + * @property string $dayOfYear read-only + * @property bool $dst read-only + * @property string $hour read-only + * @property bool $local read-only + * @property string $minute read-only + * @property string $month read-only + * @property string $quarter read-only + * @property string $second read-only + * @property int $timestamp read-only + * @property bool $utc read-only + * @property string $weekOfMonth read-only + * @property string $weekOfYear read-only + * @property string $year read-only * * @deprecated Use Time instead. * @see \CodeIgniter\I18n\TimeLegacyTest diff --git a/system/I18n/TimeTrait.php b/system/I18n/TimeTrait.php index 7f2068b7..93f03d8a 100644 --- a/system/I18n/TimeTrait.php +++ b/system/I18n/TimeTrait.php @@ -212,14 +212,22 @@ trait TimeTrait * * @throws Exception */ - public static function create(?int $year = null, ?int $month = null, ?int $day = null, ?int $hour = null, ?int $minutes = null, ?int $seconds = null, $timezone = null, ?string $locale = null) - { + public static function create( + ?int $year = null, + ?int $month = null, + ?int $day = null, + ?int $hour = null, + ?int $minutes = null, + ?int $seconds = null, + $timezone = null, + ?string $locale = null + ) { $year ??= date('Y'); $month ??= date('m'); $day ??= date('d'); - $hour = empty($hour) ? 0 : $hour; - $minutes = empty($minutes) ? 0 : $minutes; - $seconds = empty($seconds) ? 0 : $seconds; + $hour ??= 0; + $minutes ??= 0; + $seconds ??= 0; return new self(date('Y-m-d H:i:s', strtotime("{$year}-{$month}-{$day} {$hour}:{$minutes}:{$seconds}")), $timezone, $locale); } @@ -1182,6 +1190,8 @@ trait TimeTrait $timezone = $this->timezone; $this->timezone = new DateTimeZone($timezone); + + // @phpstan-ignore-next-line `$this->date` is a special property for PHP internal use. parent::__construct($this->date, $this->timezone); } } diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index cf23fd0f..92644eed 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -691,6 +691,8 @@ abstract class BaseHandler implements ImageHandlerInterface * $image->resize(100, 200, true) * ->save($target); * + * @param non-empty-string|null $target + * * @return bool */ abstract public function save(?string $target = null, int $quality = 90); diff --git a/system/Images/Handlers/GDHandler.php b/system/Images/Handlers/GDHandler.php index 15bb97b6..f2cd5587 100644 --- a/system/Images/Handlers/GDHandler.php +++ b/system/Images/Handlers/GDHandler.php @@ -204,11 +204,13 @@ class GDHandler extends BaseHandler * Example: * $image->resize(100, 200, true) * ->save(); + * + * @param non-empty-string|null $target */ public function save(?string $target = null, int $quality = 90): bool { $original = $target; - $target = empty($target) ? $this->image()->getPathname() : $target; + $target = ($target === null || $target === '') ? $this->image()->getPathname() : $target; // If no new resource has been created, then we're // simply copy the existing one. @@ -227,6 +229,7 @@ class GDHandler extends BaseHandler // for png and webp we can actually preserve transparency if (in_array($this->image()->imageType, $this->supportTransparency, true)) { + imagepalettetotruecolor($this->resource); imagealphablending($this->resource, false); imagesavealpha($this->resource, true); } diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index 857a273d..7dd5e5d7 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -223,11 +223,13 @@ class ImageMagickHandler extends BaseHandler * Example: * $image->resize(100, 200, true) * ->save(); + * + * @param non-empty-string|null $target */ public function save(?string $target = null, int $quality = 90): bool { $original = $target; - $target = empty($target) ? $this->image()->getPathname() : $target; + $target = ($target === null || $target === '') ? $this->image()->getPathname() : $target; // If no new resource has been created, then we're // simply copy the existing one. diff --git a/system/Images/ImageHandlerInterface.php b/system/Images/ImageHandlerInterface.php index 1c3593a9..76a016b0 100644 --- a/system/Images/ImageHandlerInterface.php +++ b/system/Images/ImageHandlerInterface.php @@ -132,7 +132,7 @@ interface ImageHandlerInterface * $image->resize(100, 200, true) * ->save($target); * - * @param string|null $target The path to save the file to. + * @param non-empty-string|null $target The path to save the file to. * * @return bool */ diff --git a/system/Language/Language.php b/system/Language/Language.php index 32eee39f..8633f8a2 100644 --- a/system/Language/Language.php +++ b/system/Language/Language.php @@ -88,7 +88,7 @@ class Language * Parses the language string for a file, loads the file, if necessary, * getting the line. * - * @return string|string[] + * @return list|string */ public function getLine(string $line, array $args = []) { diff --git a/system/Language/en/HTTP.php b/system/Language/en/HTTP.php index a7861c71..ee5ca4f8 100644 --- a/system/Language/en/HTTP.php +++ b/system/Language/en/HTTP.php @@ -19,6 +19,8 @@ return [ // IncomingRequest 'invalidNegotiationType' => '"{0}" is not a valid negotiation type. Must be one of: media, charset, encoding, language.', + 'invalidJSON' => 'Failed to parse JSON string. Error: {0}', + 'unsupportedJSONFormat' => 'The provided JSON format is not supported.', // Message 'invalidHTTPProtocol' => 'Invalid HTTP Protocol Version: {0}', diff --git a/system/Log/Logger.php b/system/Log/Logger.php index afd70314..e5f482cb 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -93,8 +93,7 @@ class Logger implements LoggerInterface * value is an associative array of configuration * items. * - * @var array - * @phpstan-var array|string|int>> + * @var array|string>> */ protected $handlerConfig = []; @@ -138,7 +137,7 @@ class Logger implements LoggerInterface $this->dateFormat = $config->dateFormat ?? $this->dateFormat; - if (! is_array($config->handlers) || empty($config->handlers)) { + if (! is_array($config->handlers) || $config->handlers === []) { throw LogException::forNoHandlers('LoggerConfig'); } diff --git a/system/Model.php b/system/Model.php index e53c0aae..5dba810c 100644 --- a/system/Model.php +++ b/system/Model.php @@ -84,6 +84,8 @@ use ReflectionProperty; * @method $this where($key, $value = null, ?bool $escape = null) * @method $this whereIn(?string $key = null, $values = null, ?bool $escape = null) * @method $this whereNotIn(?string $key = null, $values = null, ?bool $escape = null) + * + * @phpstan-import-type row_array from BaseModel */ class Model extends BaseModel { @@ -178,6 +180,7 @@ class Model extends BaseModel * @param array|int|string|null $id One primary key or an array of primary keys * * @return array|object|null The resulting row of data, or null. + * @phpstan-return ($singleton is true ? row_array|null|object : list) */ protected function doFind(bool $singleton, $id = null) { @@ -209,6 +212,7 @@ class Model extends BaseModel * @param string $columnName Column Name * * @return array|null The resulting row of data, or null if no data found. + * @phpstan-return list|null */ protected function doFindColumn(string $columnName) { @@ -224,6 +228,7 @@ class Model extends BaseModel * @param int $offset Offset * * @return array + * @phpstan-return list */ protected function doFindAll(int $limit = 0, int $offset = 0) { @@ -244,6 +249,7 @@ class Model extends BaseModel * This method works only with dbCalls. * * @return array|object|null + * @phpstan-return row_array|object|null */ protected function doFirst() { @@ -251,13 +257,13 @@ class Model extends BaseModel if ($this->tempUseSoftDeletes) { $builder->where($this->table . '.' . $this->deletedField, null); - } elseif ($this->useSoftDeletes && empty($builder->QBGroupBy) && $this->primaryKey) { + } elseif ($this->useSoftDeletes && ($builder->QBGroupBy === []) && $this->primaryKey) { $builder->groupBy($this->table . '.' . $this->primaryKey); } // Some databases, like PostgreSQL, need order // information to consistently return correct results. - if ($builder->QBGroupBy && empty($builder->QBOrderBy) && $this->primaryKey) { + if ($builder->QBGroupBy && ($builder->QBOrderBy === []) && $this->primaryKey) { $builder->orderBy($this->table . '.' . $this->primaryKey, 'asc'); } @@ -268,29 +274,30 @@ class Model extends BaseModel * Inserts data into the current table. * This method works only with dbCalls. * - * @param array $data Data + * @param array $row Row data + * @phpstan-param row_array $row * * @return bool */ - protected function doInsert(array $data) + protected function doInsert(array $row) { $escape = $this->escape; $this->escape = []; // Require non-empty primaryKey when // not using auto-increment feature - if (! $this->useAutoIncrement && empty($data[$this->primaryKey])) { + if (! $this->useAutoIncrement && ! isset($row[$this->primaryKey])) { throw DataException::forEmptyPrimaryKey('insert'); } $builder = $this->builder(); // Must use the set() method to ensure to set the correct escape flag - foreach ($data as $key => $val) { + foreach ($row as $key => $val) { $builder->set($key, $val, $escape[$key] ?? null); } - if ($this->allowEmptyInserts && empty($data)) { + if ($this->allowEmptyInserts && $row === []) { $table = $this->db->protectIdentifiers($this->table, true, null, false); if ($this->db->getPlatform() === 'MySQLi') { $sql = 'INSERT INTO ' . $table . ' VALUES ()'; @@ -321,7 +328,7 @@ class Model extends BaseModel // If insertion succeeded then save the insert ID if ($result) { - $this->insertID = ! $this->useAutoIncrement ? $data[$this->primaryKey] : $this->db->insertID(); + $this->insertID = ! $this->useAutoIncrement ? $row[$this->primaryKey] : $this->db->insertID(); } return $result; @@ -344,7 +351,7 @@ class Model extends BaseModel foreach ($set as $row) { // Require non-empty primaryKey when // not using auto-increment feature - if (! $this->useAutoIncrement && empty($row[$this->primaryKey])) { + if (! $this->useAutoIncrement && ! isset($row[$this->primaryKey])) { throw DataException::forEmptyPrimaryKey('insertBatch'); } } @@ -358,9 +365,10 @@ class Model extends BaseModel * This method works only with dbCalls. * * @param array|int|string|null $id - * @param array|null $data + * @param array|null $row Row data + * @phpstan-param row_array|null $row */ - protected function doUpdate($id = null, $data = null): bool + protected function doUpdate($id = null, $row = null): bool { $escape = $this->escape; $this->escape = []; @@ -372,7 +380,7 @@ class Model extends BaseModel } // Must use the set() method to ensure to set the correct escape flag - foreach ($data as $key => $val) { + foreach ($row as $key => $val) { $builder->set($key, $val, $escape[$key] ?? null); } @@ -411,7 +419,7 @@ class Model extends BaseModel * @param array|int|string|null $id The rows primary key(s) * @param bool $purge Allows overriding the soft deletes setting. * - * @return bool|string + * @return bool|string SQL string when testMode * * @throws DatabaseException */ @@ -425,7 +433,7 @@ class Model extends BaseModel } if ($this->useSoftDeletes && ! $purge) { - if (empty($builder->getCompiledQBWhere())) { + if ($builder->getCompiledQBWhere() === []) { throw new DatabaseException( 'Deletes are not allowed unless they contain a "where" or "like" clause.' ); @@ -435,7 +443,7 @@ class Model extends BaseModel $set[$this->deletedField] = $this->setDate(); - if ($this->useTimestamps && $this->updatedField) { + if ($this->useTimestamps && $this->updatedField !== '') { $set[$this->updatedField] = $this->setDate(); } @@ -450,7 +458,7 @@ class Model extends BaseModel * through soft deletes (deleted = 1) * This method works only with dbCalls. * - * @return bool|string Returns a string if in test mode. + * @return bool|string Returns a SQL string if in test mode. */ protected function doPurgeDeleted() { @@ -475,14 +483,15 @@ class Model extends BaseModel * Compiles a replace into string and runs the query * This method works only with dbCalls. * - * @param array|null $data Data - * @param bool $returnSQL Set to true to return Query String + * @param array|null $row Data + * @phpstan-param row_array|null $row + * @param bool $returnSQL Set to true to return Query String * * @return BaseResult|false|Query|string */ - protected function doReplace(?array $data = null, bool $returnSQL = false) + protected function doReplace(?array $row = null, bool $returnSQL = false) { - return $this->builder()->testMode($returnSQL)->replace($data); + return $this->builder()->testMode($returnSQL)->replace($row); } /** @@ -491,7 +500,7 @@ class Model extends BaseModel * ['source' => 'message'] * This method works only with dbCalls. * - * @return array + * @return array */ protected function doErrors() { @@ -522,18 +531,34 @@ class Model extends BaseModel /** * Returns the id value for the data array or object * - * @param array|object $data Data + * @param array|object $row Row data + * @phpstan-param row_array|object $row * * @return array|int|string|null */ - public function getIdValue($data) + public function getIdValue($row) { - if (is_object($data) && isset($data->{$this->primaryKey})) { - return $data->{$this->primaryKey}; + if (is_object($row) && isset($row->{$this->primaryKey})) { + // Get the raw primary key value of the Entity. + if ($row instanceof Entity) { + $cast = $row->cast(); + + // Disable Entity casting, because raw primary key value is needed for database. + $row->cast(false); + + $primaryKey = $row->{$this->primaryKey}; + + // Restore Entity casting setting. + $row->cast($cast); + + return $primaryKey; + } + + return $row->{$this->primaryKey}; } - if (is_array($data) && ! empty($data[$this->primaryKey])) { - return $data[$this->primaryKey]; + if (is_array($row) && isset($row[$this->primaryKey])) { + return $row[$this->primaryKey]; } return null; @@ -566,7 +591,7 @@ class Model extends BaseModel $offset += $size; - if (empty($rows)) { + if ($rows === []) { continue; } @@ -602,6 +627,8 @@ class Model extends BaseModel /** * Provides a shared instance of the Query Builder. * + * @param non-empty-string|null $table + * * @return BaseBuilder * * @throws ModelException @@ -621,11 +648,11 @@ class Model extends BaseModel // We're going to force a primary key to exist // so we don't have overly convoluted code, // and future features are likely to require them. - if (empty($this->primaryKey)) { + if ($this->primaryKey === '') { throw ModelException::forNoPrimaryKey(static::class); } - $table = empty($table) ? $this->table : $table; + $table = ($table === null || $table === '') ? $this->table : $table; // Ensure we have a good db connection if (! $this->db instanceof BaseConnection) { @@ -670,11 +697,11 @@ class Model extends BaseModel * This method is called on save to determine if entry have to be updated * If this method return false insert operation will be executed * - * @param array|object $data Data + * @param array|object $row Data */ - protected function shouldUpdate($data): bool + protected function shouldUpdate($row): bool { - if (parent::shouldUpdate($data) === false) { + if (parent::shouldUpdate($row) === false) { return false; } @@ -684,70 +711,73 @@ class Model extends BaseModel // When useAutoIncrement feature is disabled, check // in the database if given record already exists - return $this->where($this->primaryKey, $this->getIdValue($data))->countAllResults() === 1; + return $this->where($this->primaryKey, $this->getIdValue($row))->countAllResults() === 1; } /** * Inserts data into the database. If an object is provided, * it will attempt to convert it to an array. * - * @param array|object|null $data - * @param bool $returnID Whether insert ID should be returned or not. + * @param array|object|null $row + * @phpstan-param row_array|object|null $row + * @param bool $returnID Whether insert ID should be returned or not. * - * @return BaseResult|false|int|object|string + * @return bool|int|string + * @phpstan-return ($returnID is true ? int|string|false : bool) * * @throws ReflectionException */ - public function insert($data = null, bool $returnID = true) + public function insert($row = null, bool $returnID = true) { - if (! empty($this->tempData['data'])) { - if (empty($data)) { - $data = $this->tempData['data']; + if (isset($this->tempData['data'])) { + if ($row === null) { + $row = $this->tempData['data']; } else { - $data = $this->transformDataToArray($data, 'insert'); - $data = array_merge($this->tempData['data'], $data); + $row = $this->transformDataToArray($row, 'insert'); + $row = array_merge($this->tempData['data'], $row); } } $this->escape = $this->tempData['escape'] ?? []; $this->tempData = []; - return parent::insert($data, $returnID); + return parent::insert($row, $returnID); } /** * Ensures that only the fields that are allowed to be inserted are in * the data array. * - * Used by insert() and insertBatch() to protect against mass assignment - * vulnerabilities. + * @used-by insert() to protect against mass assignment vulnerabilities. + * @used-by insertBatch() to protect against mass assignment vulnerabilities. * - * @param array $data Data + * @param array $row Row data + * @phpstan-param row_array $row * * @throws DataException */ - protected function doProtectFieldsForInsert(array $data): array + protected function doProtectFieldsForInsert(array $row): array { if (! $this->protectFields) { - return $data; + return $row; } - if (empty($this->allowedFields)) { + if ($this->allowedFields === []) { throw DataException::forInvalidAllowedFields(static::class); } - foreach (array_keys($data) as $key) { + foreach (array_keys($row) as $key) { // Do not remove the non-auto-incrementing primary key data. if ($this->useAutoIncrement === false && $key === $this->primaryKey) { continue; } if (! in_array($key, $this->allowedFields, true)) { - unset($data[$key]); + unset($row[$key]); } } - return $data; + return $row; } /** @@ -755,71 +785,42 @@ class Model extends BaseModel * it will attempt to convert it into an array. * * @param array|int|string|null $id - * @param array|object|null $data + * @param array|object|null $row + * @phpstan-param row_array|object|null $row * * @throws ReflectionException */ - public function update($id = null, $data = null): bool + public function update($id = null, $row = null): bool { - if (! empty($this->tempData['data'])) { - if (empty($data)) { - $data = $this->tempData['data']; + if (isset($this->tempData['data'])) { + if ($row === null) { + $row = $this->tempData['data']; } else { - $data = $this->transformDataToArray($data, 'update'); - $data = array_merge($this->tempData['data'], $data); + $row = $this->transformDataToArray($row, 'update'); + $row = array_merge($this->tempData['data'], $row); } } $this->escape = $this->tempData['escape'] ?? []; $this->tempData = []; - return parent::update($id, $data); + return parent::update($id, $row); } /** * Takes a class and returns an array of its public and protected * properties as an array with raw values. * - * @param object|string $data - * @param bool $recursive If true, inner entities will be cast as array as well + * @param object $object Object + * @param bool $recursive If true, inner entities will be cast as array as well * - * @return array|null Array + * @return array * * @throws ReflectionException */ - protected function objectToRawArray($data, bool $onlyChanged = true, bool $recursive = false): ?array + protected function objectToRawArray($object, bool $onlyChanged = true, bool $recursive = false): array { - $properties = parent::objectToRawArray($data, $onlyChanged); - - $primaryKey = null; - - if ($data instanceof Entity) { - $cast = $data->cast(); - - // Disable Entity casting, because raw primary key data is needed for database. - $data->cast(false); - - $primaryKey = $data->{$this->primaryKey}; - - // Restore Entity casting setting. - $data->cast($cast); - } - - // Always grab the primary key otherwise updates will fail. - if ( - // @TODO Should use `$data instanceof Entity`. - method_exists($data, 'toRawArray') - && ( - ! empty($properties) - && ! empty($this->primaryKey) - && ! in_array($this->primaryKey, $properties, true) - && ! empty($primaryKey) - ) - ) { - $properties[$this->primaryKey] = $primaryKey; - } - - return $properties; + return parent::objectToRawArray($object, $onlyChanged); } /** @@ -909,7 +910,7 @@ class Model extends BaseModel $properties = $data->toRawArray($onlyChanged); // Always grab the primary key otherwise updates will fail. - if (! empty($properties) && ! empty($primaryKey) && ! in_array($primaryKey, $properties, true) && ! empty($data->{$primaryKey})) { + if ($properties !== [] && isset($primaryKey) && ! in_array($primaryKey, $properties, true) && isset($data->{$primaryKey})) { $properties[$primaryKey] = $data->{$primaryKey}; } } else { diff --git a/system/Modules/Modules.php b/system/Modules/Modules.php index f99e7215..e17b9f3c 100644 --- a/system/Modules/Modules.php +++ b/system/Modules/Modules.php @@ -37,7 +37,7 @@ class Modules /** * Auto-Discover Rules Handler * - * @var array + * @var list */ public $aliases = []; diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index b08cb004..d9de5faa 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -310,7 +310,7 @@ class Pager implements PagerInterface $curr = $this->getCurrentPage($group); $page = null; - if (! empty($last) && ! empty($curr) && $last === $curr) { + if (! empty($last) && $curr !== 0 && $last === $curr) { return null; } @@ -334,7 +334,7 @@ class Pager implements PagerInterface $curr = $this->getCurrentPage($group); $page = null; - if (! empty($first) && ! empty($curr) && $first === $curr) { + if ($first !== 0 && $curr !== 0 && $first === $curr) { return null; } diff --git a/system/Pager/PagerRenderer.php b/system/Pager/PagerRenderer.php index 454ac9f1..9e6d2950 100644 --- a/system/Pager/PagerRenderer.php +++ b/system/Pager/PagerRenderer.php @@ -25,14 +25,14 @@ use CodeIgniter\HTTP\URI; class PagerRenderer { /** - * First page number. + * First page number in the set of links to be displayed. * * @var int */ protected $first; /** - * Last page number. + * Last page number in the set of links to be displayed. * * @var int */ @@ -85,8 +85,11 @@ class PagerRenderer */ public function __construct(array $details) { - $this->first = 1; - $this->last = $details['pageCount']; + // `first` and `last` will be updated by `setSurroundCount()`. + // You must call `setSurroundCount()` after instantiation. + $this->first = 1; + $this->last = $details['pageCount']; + $this->current = $details['currentPage']; $this->total = $details['total']; $this->uri = $details['uri']; @@ -122,8 +125,6 @@ class PagerRenderer * page before the current page, but is the page just before the * "first" page. * - * You MUST call hasPrevious() first, or this value may be invalid. - * * @return string|null */ public function getPrevious() @@ -162,8 +163,6 @@ class PagerRenderer * page after the current page, but is the page that follows the * "last" page. * - * You MUST call hasNext() first, or this value may be invalid. - * * @return string|null */ public function getNext() @@ -260,6 +259,8 @@ class PagerRenderer * is represented by another array containing of the URI the link * should go to, the title (number) of the link, and a boolean * value representing whether this link is active or not. + * + * @return list */ public function links(): array { @@ -381,7 +382,7 @@ class PagerRenderer } /** - * Returns the page number of the first page. + * Returns the page number of the first page in the set of links to be displayed. */ public function getFirstPageNumber(): int { @@ -397,7 +398,7 @@ class PagerRenderer } /** - * Returns the page number of the last page. + * Returns the page number of the last page in the set of links to be displayed. */ public function getLastPageNumber(): int { diff --git a/system/Pager/Views/default_simple.php b/system/Pager/Views/default_simple.php index fc00ce88..df7f7e0e 100644 --- a/system/Pager/Views/default_simple.php +++ b/system/Pager/Views/default_simple.php @@ -15,7 +15,7 @@ $pager->setSurroundCount(0);
  • hasNext() ? '' : 'class="disabled"' ?>> - +
  • diff --git a/system/RESTful/ResourceController.php b/system/RESTful/ResourceController.php index 03ba4d31..6ffb602c 100644 --- a/system/RESTful/ResourceController.php +++ b/system/RESTful/ResourceController.php @@ -105,6 +105,7 @@ class ResourceController extends BaseResource * Set/change the expected response representation for returned objects * * @param string $format json/xml + * @phpstan-param 'json'|'xml' $format * * @return void */ diff --git a/system/Router/AutoRouter.php b/system/Router/AutoRouter.php index f7cd02a3..87a5b3ac 100644 --- a/system/Router/AutoRouter.php +++ b/system/Router/AutoRouter.php @@ -90,7 +90,7 @@ final class AutoRouter implements AutoRouterInterface // If we don't have any segments left - use the default controller; // If not empty, then the first segment should be the controller - if (! empty($segments)) { + if ($segments !== []) { $this->controller = ucfirst(array_shift($segments)); } @@ -103,7 +103,7 @@ final class AutoRouter implements AutoRouterInterface // Use the method name if it exists. // If it doesn't, no biggie - the default method name // has already been set. - if (! empty($segments)) { + if ($segments !== []) { $this->method = array_shift($segments) ?: $this->method; } @@ -115,7 +115,7 @@ final class AutoRouter implements AutoRouterInterface /** @var array $params An array of params to the controller method. */ $params = []; - if (! empty($segments)) { + if ($segments !== []) { $params = $segments; } @@ -259,7 +259,7 @@ final class AutoRouter implements AutoRouterInterface */ public function setDirectory(?string $dir = null, bool $append = false, bool $validate = true) { - if (empty($dir)) { + if ($dir === null || $dir === '') { $this->directory = null; return; @@ -275,7 +275,7 @@ final class AutoRouter implements AutoRouterInterface } } - if ($append !== true || empty($this->directory)) { + if ($append !== true || ($this->directory === null || $this->directory === '')) { $this->directory = trim($dir, '/') . '/'; } else { $this->directory .= trim($dir, '/') . '/'; @@ -290,7 +290,7 @@ final class AutoRouter implements AutoRouterInterface */ public function directory(): string { - return ! empty($this->directory) ? $this->directory : ''; + return ($this->directory !== null && $this->directory !== '') ? $this->directory : ''; } private function controllerName(): string diff --git a/system/Router/AutoRouterImproved.php b/system/Router/AutoRouterImproved.php index 0a26fc12..a86e92f0 100644 --- a/system/Router/AutoRouterImproved.php +++ b/system/Router/AutoRouterImproved.php @@ -49,7 +49,7 @@ final class AutoRouterImproved implements AutoRouterInterface /** * An array of params to the controller method. * - * @phpstan-var list + * @var list */ private array $params = []; @@ -77,7 +77,7 @@ final class AutoRouterImproved implements AutoRouterInterface /** * The URI segments. * - * @phpstan-var list + * @var list */ private array $segments = []; @@ -286,7 +286,7 @@ final class AutoRouterImproved implements AutoRouterInterface } // The first item may be a method name. - /** @phpstan-var list $params */ + /** @var list $params */ $params = $this->params; $methodParam = array_shift($params); diff --git a/system/Router/DefinedRouteCollector.php b/system/Router/DefinedRouteCollector.php index f8e245d1..4fdbbb02 100644 --- a/system/Router/DefinedRouteCollector.php +++ b/system/Router/DefinedRouteCollector.php @@ -29,7 +29,7 @@ final class DefinedRouteCollector } /** - * @phpstan-return Generator + * @return Generator */ public function collect(): Generator { diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index b3bb96e9..4407c107 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -560,11 +560,12 @@ class RouteCollection implements RouteCollectionInterface /** * Returns the raw array of available routes. * - * @param bool $includeWildcard Whether to include '*' routes. + * @param non-empty-string|null $verb + * @param bool $includeWildcard Whether to include '*' routes. */ public function getRoutes(?string $verb = null, bool $includeWildcard = true): array { - if (empty($verb)) { + if ($verb === null || $verb === '') { $verb = $this->getHTTPVerb(); } @@ -995,7 +996,7 @@ class RouteCollection implements RouteCollectionInterface */ public function match(array $verbs = [], string $from = '', $to = '', ?array $options = null): RouteCollectionInterface { - if (empty($from) || empty($to)) { + if ($from === '' || empty($to)) { throw new InvalidArgumentException('You must supply the parameters: from, to.'); } @@ -1267,8 +1268,7 @@ class RouteCollection implements RouteCollectionInterface * * @param string $search routeKey * - * @return array filter_name or filter_name:arguments like 'role:admin,manager' - * @phpstan-return list + * @return list filter_name or filter_name:arguments like 'role:admin,manager' */ public function getFiltersForRoute(string $search, ?string $verb = null): array { @@ -1305,8 +1305,7 @@ class RouteCollection implements RouteCollectionInterface * Build our resulting string, inserting the $params in * the appropriate places. * - * @var array $patterns - * @phpstan-var list $patterns + * @var list $patterns */ $patterns = $matches[0]; @@ -1357,8 +1356,7 @@ class RouteCollection implements RouteCollectionInterface * Build our resulting string, inserting the $params in * the appropriate places. * - * @var array $placeholders - * @phpstan-var list $placeholders + * @var list $placeholders */ $placeholders = $matches[0]; @@ -1691,8 +1689,7 @@ class RouteCollection implements RouteCollectionInterface /** * Load routes options based on verb * - * @return array> [routeKey(or from) => [key => value]] - * @phpstan-return array< + * @return array< * string, * array{ * filter?: string|list, namespace?: string, hostname?: string, @@ -1740,8 +1737,7 @@ class RouteCollection implements RouteCollectionInterface * * @param string|null $verb HTTP verb. `'*'` returns all controllers in any verb. * - * @return array controller name list - * @phpstan-return list + * @return list controller name list */ public function getRegisteredControllers(?string $verb = '*'): array { diff --git a/system/Router/Router.php b/system/Router/Router.php index 0013d7cd..723c292c 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -589,7 +589,7 @@ class Router implements RouterInterface */ public function setDirectory(?string $dir = null, bool $append = false, bool $validate = true) { - if (empty($dir)) { + if ($dir === null || $dir === '') { $this->directory = null; } @@ -623,7 +623,7 @@ class Router implements RouterInterface protected function setRequest(array $segments = []) { // If we don't have any segments - use the default controller; - if (empty($segments)) { + if ($segments === []) { return; } diff --git a/system/Session/Handlers/MemcachedHandler.php b/system/Session/Handlers/MemcachedHandler.php index abccfafb..aa0c1cd3 100644 --- a/system/Session/Handlers/MemcachedHandler.php +++ b/system/Session/Handlers/MemcachedHandler.php @@ -70,9 +70,7 @@ class MemcachedHandler extends BaseHandler $this->keyPrefix .= $this->ipAddress . ':'; } - if (! empty($this->keyPrefix)) { - ini_set('memcached.sess_prefix', $this->keyPrefix); - } + ini_set('memcached.sess_prefix', $this->keyPrefix); } /** @@ -125,7 +123,7 @@ class MemcachedHandler extends BaseHandler } } - if (empty($serverList)) { + if ($serverList === []) { $this->logger->error('Session: Memcached server pool is empty.'); return false; diff --git a/system/Session/Handlers/RedisHandler.php b/system/Session/Handlers/RedisHandler.php index 815e3644..b4297929 100644 --- a/system/Session/Handlers/RedisHandler.php +++ b/system/Session/Handlers/RedisHandler.php @@ -71,7 +71,7 @@ class RedisHandler extends BaseHandler parent::__construct($config, $ipAddress); // Store Session configurations - $this->sessionExpiration = empty($config->expiration) + $this->sessionExpiration = ($config->expiration === 0) ? (int) ini_get('session.gc_maxlifetime') : $config->expiration; // Add sessionCookieName for multiple session cookies. @@ -293,7 +293,10 @@ class RedisHandler extends BaseHandler $attempt = 0; do { - if (($ttl = $this->redis->ttl($lockKey)) > 0) { + $ttl = $this->redis->ttl($lockKey); + assert(is_int($ttl)); + + if ($ttl > 0) { sleep(1); continue; diff --git a/system/Session/Session.php b/system/Session/Session.php index 4e4a0158..875387f2 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -241,7 +241,7 @@ class Session implements SessionInterface $this->startSession(); // Is session ID auto-regeneration configured? (ignoring ajax requests) - if ((empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') + if ((! isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') && ($regenerateTime = $this->config->timeToUpdate) > 0 ) { if (! isset($_SESSION['__ci_last_regenerate'])) { @@ -299,7 +299,7 @@ class Session implements SessionInterface ini_set('session.gc_maxlifetime', (string) $this->config->expiration); } - if (! empty($this->config->savePath)) { + if ($this->config->savePath !== '') { ini_set('session.save_path', $this->config->savePath); } @@ -368,7 +368,7 @@ class Session implements SessionInterface */ protected function initVars() { - if (empty($_SESSION['__ci_vars'])) { + if (! isset($_SESSION['__ci_vars'])) { return; } @@ -384,7 +384,7 @@ class Session implements SessionInterface } } - if (empty($_SESSION['__ci_vars'])) { + if ($_SESSION['__ci_vars'] === []) { unset($_SESSION['__ci_vars']); } } @@ -486,21 +486,21 @@ class Session implements SessionInterface * * Replaces the legacy method $session->userdata(); * - * @param string|null $key Identifier of the session property to retrieve + * @param non-empty-string|null $key Identifier of the session property to retrieve * * @return array|bool|float|int|object|string|null The property value(s) */ public function get(?string $key = null) { - if (! empty($key) && (null !== ($value = $_SESSION[$key] ?? null) || null !== ($value = dot_array_search($key, $_SESSION ?? [])))) { + if ($key !== null && $key !== '' && (null !== ($value = $_SESSION[$key] ?? null) || null !== ($value = dot_array_search($key, $_SESSION ?? [])))) { return $value; } - if (empty($_SESSION)) { + if (! isset($_SESSION) || $_SESSION === []) { return $key === null ? [] : null; } - if (! empty($key)) { + if ($key !== null && $key !== '') { return null; } @@ -646,7 +646,7 @@ class Session implements SessionInterface $flashdata = []; - if (! empty($_SESSION['__ci_vars'])) { + if (isset($_SESSION['__ci_vars'])) { foreach ($_SESSION['__ci_vars'] as $key => &$value) { if (! is_int($value)) { $flashdata[$key] = $_SESSION[$key]; @@ -706,7 +706,7 @@ class Session implements SessionInterface */ public function unmarkFlashdata($key) { - if (empty($_SESSION['__ci_vars'])) { + if (! isset($_SESSION['__ci_vars'])) { return; } @@ -720,7 +720,7 @@ class Session implements SessionInterface } } - if (empty($_SESSION['__ci_vars'])) { + if ($_SESSION['__ci_vars'] === []) { unset($_SESSION['__ci_vars']); } } @@ -778,7 +778,7 @@ class Session implements SessionInterface $tempdata = []; - if (! empty($_SESSION['__ci_vars'])) { + if (isset($_SESSION['__ci_vars'])) { foreach ($_SESSION['__ci_vars'] as $key => &$value) { if (is_int($value)) { $tempdata[$key] = $_SESSION[$key]; @@ -856,7 +856,7 @@ class Session implements SessionInterface */ public function unmarkTempdata($key) { - if (empty($_SESSION['__ci_vars'])) { + if (! isset($_SESSION['__ci_vars'])) { return; } @@ -870,7 +870,7 @@ class Session implements SessionInterface } } - if (empty($_SESSION['__ci_vars'])) { + if ($_SESSION['__ci_vars'] === []) { unset($_SESSION['__ci_vars']); } } diff --git a/system/Session/SessionInterface.php b/system/Session/SessionInterface.php index e78736bf..424834c7 100644 --- a/system/Session/SessionInterface.php +++ b/system/Session/SessionInterface.php @@ -51,7 +51,7 @@ interface SessionInterface * * Replaces the legacy method $session->userdata(); * - * @param string $key Identifier of the session property to retrieve + * @param non-empty-string|null $key Identifier of the session property to retrieve * * @return array|bool|float|int|object|string|null The property value(s) */ diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index a0c84c48..b27db73f 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -123,7 +123,7 @@ abstract class CIUnitTestCase extends TestCase /** * The namespace(s) to help us find the migration classes. - * Empty is equivalent to running `spark migrate --all`. + * `null` is equivalent to running `spark migrate --all`. * Note that running "all" runs migrations in date order, * but specifying namespaces runs them in namespace order (then date) * @@ -135,8 +135,7 @@ abstract class CIUnitTestCase extends TestCase * The name of the database group to connect to. * If not present, will use the defaultGroup. * - * @var string - * @phpstan-var non-empty-string + * @var non-empty-string */ protected $DBGroup = 'tests'; diff --git a/system/Test/ControllerTestTrait.php b/system/Test/ControllerTestTrait.php index 2da65d1a..a99b899e 100644 --- a/system/Test/ControllerTestTrait.php +++ b/system/Test/ControllerTestTrait.php @@ -215,7 +215,7 @@ trait ControllerTestTrait /** * Set controller's config, with method chaining. * - * @param mixed $appConfig + * @param App $appConfig * * @return $this */ @@ -229,7 +229,7 @@ trait ControllerTestTrait /** * Set controller's request, with method chaining. * - * @param mixed $request + * @param IncomingRequest $request * * @return $this */ @@ -260,7 +260,7 @@ trait ControllerTestTrait /** * Set controller's logger, with method chaining. * - * @param mixed $logger + * @param LoggerInterface $logger * * @return $this */ diff --git a/system/Test/DOMParser.php b/system/Test/DOMParser.php index 100b14b7..15654521 100644 --- a/system/Test/DOMParser.php +++ b/system/Test/DOMParser.php @@ -189,23 +189,23 @@ class DOMParser $path = ''; // By ID - if (! empty($selector['id'])) { - $path = empty($selector['tag']) + if (isset($selector['id'])) { + $path = ($selector['tag'] === '') ? "id(\"{$selector['id']}\")" : "//{$selector['tag']}[@id=\"{$selector['id']}\"]"; } // By Class - elseif (! empty($selector['class'])) { - $path = empty($selector['tag']) + elseif (isset($selector['class'])) { + $path = ($selector['tag'] === '') ? "//*[@class=\"{$selector['class']}\"]" : "//{$selector['tag']}[@class=\"{$selector['class']}\"]"; } // By tag only - elseif (! empty($selector['tag'])) { + elseif ($selector['tag'] !== '') { $path = "//{$selector['tag']}"; } - if (! empty($selector['attr'])) { + if (isset($selector['attr'])) { foreach ($selector['attr'] as $key => $value) { $path .= "[@{$key}=\"{$value}\"]"; } @@ -213,7 +213,7 @@ class DOMParser // $paths might contain a number of different // ready to go xpath portions to tack on. - if (! empty($paths) && is_array($paths)) { + if ($paths !== [] && is_array($paths)) { foreach ($paths as $extra) { $path .= $extra; } @@ -231,7 +231,7 @@ class DOMParser /** * Look for the a selector in the passed text. * - * @return array + * @return array{tag: string, id: string|null, class: string|null, attr: array|null} */ public function parseSelector(string $selector) { diff --git a/system/Test/DatabaseTestTrait.php b/system/Test/DatabaseTestTrait.php index d7b8b02d..59dc5f82 100644 --- a/system/Test/DatabaseTestTrait.php +++ b/system/Test/DatabaseTestTrait.php @@ -79,7 +79,7 @@ trait DatabaseTestTrait $config = new Migrations(); $config->enabled = true; - $this->migrations = Services::migrations($config, $this->db); + $this->migrations = Services::migrations($config, $this->db, false); $this->migrations->setSilent(false); } @@ -120,7 +120,7 @@ trait DatabaseTestTrait } // If no namespace was specified then rollback all - if (empty($this->namespace)) { + if ($this->namespace === null) { $this->migrations->setNamespace(null); $this->migrations->regress(0, 'tests'); } @@ -146,7 +146,7 @@ trait DatabaseTestTrait } // If no namespace was specified then migrate all - if (empty($this->namespace)) { + if ($this->namespace === null) { $this->migrations->setNamespace(null); $this->migrations->latest('tests'); self::$doneMigration = true; @@ -182,8 +182,8 @@ trait DatabaseTestTrait */ protected function runSeeds() { - if (! empty($this->seed)) { - if (! empty($this->basePath)) { + if ($this->seed !== '') { + if ($this->basePath !== '') { $this->seeder->setPath(rtrim($this->basePath, '/') . '/Seeds'); } diff --git a/system/Test/Fabricator.php b/system/Test/Fabricator.php index 3a28f281..c247b874 100644 --- a/system/Test/Fabricator.php +++ b/system/Test/Fabricator.php @@ -128,7 +128,7 @@ class Fabricator // Determine eligible date fields foreach (['createdField', 'updatedField', 'deletedField'] as $field) { - if (! empty($this->model->{$field})) { + if (isset($this->model->{$field})) { $this->dateFields[] = $this->model->{$field}; } } @@ -152,7 +152,7 @@ class Fabricator */ public static function getCount(string $table): int { - return empty(self::$tableCounts[$table]) ? 0 : self::$tableCounts[$table]; + return ! isset(self::$tableCounts[$table]) ? 0 : self::$tableCounts[$table]; } /** @@ -282,7 +282,7 @@ class Fabricator { $this->formatters = []; - if (! empty($this->model->allowedFields)) { + if (isset($this->model->allowedFields)) { foreach ($this->model->allowedFields as $field) { $this->formatters[$field] = $this->guessFormatter($field); } @@ -513,12 +513,12 @@ class Fabricator // Determine which fields we will need $fields = []; - if (! empty($this->model->useTimestamps)) { + if ($this->model->useTimestamps) { $fields[$this->model->createdField] = $datetime; $fields[$this->model->updatedField] = $datetime; } - if (! empty($this->model->useSoftDeletes)) { + if ($this->model->useSoftDeletes) { $fields[$this->model->deletedField] = null; } diff --git a/system/Test/FeatureTestCase.php b/system/Test/FeatureTestCase.php index c94cf926..fff624a8 100644 --- a/system/Test/FeatureTestCase.php +++ b/system/Test/FeatureTestCase.php @@ -186,7 +186,7 @@ class FeatureTestCase extends CIUnitTestCase ->run($routes, true); $output = \ob_get_contents(); - if (empty($response->getBody()) && ! empty($output)) { + if (($response->getBody() === null) && ! ($output === '' || $output === false)) { $response->setBody($output); } @@ -335,7 +335,7 @@ class FeatureTestCase extends CIUnitTestCase { // $params should set the query vars if present, // otherwise set it from the URL. - $get = ! empty($params) && $method === 'get' + $get = ($params !== null && $params !== [] && $method === 'get') ? $params : $this->getPrivateProperty($request->getUri(), 'query'); @@ -371,16 +371,19 @@ class FeatureTestCase extends CIUnitTestCase } if (isset($this->bodyFormat) && $this->bodyFormat !== '') { - if (empty($params)) { + if ($params === null || $params === []) { $params = $request->fetchGlobal('request'); } + $formatMime = ''; + if ($this->bodyFormat === 'json') { $formatMime = 'application/json'; } elseif ($this->bodyFormat === 'xml') { $formatMime = 'application/xml'; } - if (! empty($formatMime) && ! empty($params)) { + + if ($formatMime !== '' && ! ($params === null || $params === [])) { $formatted = Services::format()->getFormatter($formatMime)->format($params); $request->setBody($formatted); $request->setHeader('Content-Type', $formatMime); diff --git a/system/Test/FeatureTestTrait.php b/system/Test/FeatureTestTrait.php index 6acef638..565e4e89 100644 --- a/system/Test/FeatureTestTrait.php +++ b/system/Test/FeatureTestTrait.php @@ -326,7 +326,8 @@ trait FeatureTestTrait * * Always populate the GET vars based on the URI. * - * @param string $method HTTP verb + * @param string $method HTTP verb + * @param non-empty-array|null $params * * @return Request * @@ -336,7 +337,7 @@ trait FeatureTestTrait { // $params should set the query vars if present, // otherwise set it from the URL. - $get = (! empty($params) && $method === 'get') + $get = ($params !== null && $params !== [] && $method === 'get') ? $params : $this->getPrivateProperty($request->getUri(), 'query'); diff --git a/system/Test/FilterTestTrait.php b/system/Test/FilterTestTrait.php index 084a6cd0..523717e5 100644 --- a/system/Test/FilterTestTrait.php +++ b/system/Test/FilterTestTrait.php @@ -125,6 +125,10 @@ trait FilterTestTrait throw new InvalidArgumentException('Invalid filter position passed: ' . $position); } + if ($filter instanceof FilterInterface) { + $filterInstances = [$filter]; + } + if (is_string($filter)) { // Check for an alias (no namespace) if (strpos($filter, '\\') === false) { @@ -132,28 +136,31 @@ trait FilterTestTrait throw new RuntimeException("No filter found with alias '{$filter}'"); } - $filterClasses = $this->filtersConfig->aliases[$filter]; + $filterClasses = (array) $this->filtersConfig->aliases[$filter]; + } else { + // FQCN + $filterClasses = [$filter]; } - $filterClasses = (array) $filterClasses; - } + $filterInstances = []; - foreach ($filterClasses as $class) { - // Get an instance - $filter = new $class(); + foreach ($filterClasses as $class) { + // Get an instance + $filter = new $class(); - if (! $filter instanceof FilterInterface) { - throw FilterException::forIncorrectInterface(get_class($filter)); + if (! $filter instanceof FilterInterface) { + throw FilterException::forIncorrectInterface(get_class($filter)); + } + + $filterInstances[] = $filter; } } $request = clone $this->request; if ($position === 'before') { - return static function (?array $params = null) use ($filterClasses, $request) { - foreach ($filterClasses as $class) { - $filter = new $class(); - + return static function (?array $params = null) use ($filterInstances, $request) { + foreach ($filterInstances as $filter) { $result = $filter->before($request, $params); // @TODO The following logic is in Filters class. @@ -177,10 +184,8 @@ trait FilterTestTrait $response = clone $this->response; - return static function (?array $params = null) use ($filterClasses, $request, $response) { - foreach ($filterClasses as $class) { - $filter = new $class(); - + return static function (?array $params = null) use ($filterInstances, $request, $response) { + foreach ($filterInstances as $filter) { $result = $filter->after($request, $response, $params); // @TODO The following logic is in Filters class. diff --git a/system/Test/IniTestTrait.php b/system/Test/IniTestTrait.php new file mode 100644 index 00000000..5820cfc1 --- /dev/null +++ b/system/Test/IniTestTrait.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Test; + +trait IniTestTrait +{ + private array $iniSettings = []; + + private function backupIniValues(array $keys): void + { + foreach ($keys as $key) { + $this->iniSettings[$key] = ini_get($key); + } + } + + private function restoreIniValues(): void + { + foreach ($this->iniSettings as $key => $value) { + ini_set($key, $value); + } + + $this->iniSettings = []; + } +} diff --git a/system/Test/Mock/MockCache.php b/system/Test/Mock/MockCache.php index 73a75580..082807b5 100644 --- a/system/Test/Mock/MockCache.php +++ b/system/Test/Mock/MockCache.php @@ -22,7 +22,7 @@ class MockCache extends BaseHandler implements CacheInterface /** * Mock cache storage. * - * @var array + * @var array */ protected $cache = []; @@ -155,7 +155,7 @@ class MockCache extends BaseHandler implements CacheInterface $key = static::validateKey($key, $this->prefix); $data = $this->cache[$key] ?: null; - if (empty($data)) { + if ($data === null) { $data = 0; } elseif (! is_int($data)) { return false; @@ -175,7 +175,7 @@ class MockCache extends BaseHandler implements CacheInterface $data = $this->cache[$key] ?: null; - if (empty($data)) { + if ($data === null) { $data = 0; } elseif (! is_int($data)) { return false; @@ -281,9 +281,9 @@ class MockCache extends BaseHandler implements CacheInterface { $item = $this->get($key); - // Let assertHas handle throwing the error for consistency + // Let assertHas() handle throwing the error for consistency // if the key is not found - if (empty($item)) { + if ($item === null) { $this->assertHas($key); } diff --git a/system/Test/Mock/MockConnection.php b/system/Test/Mock/MockConnection.php index ea4ebac9..2e9ebe3b 100644 --- a/system/Test/Mock/MockConnection.php +++ b/system/Test/Mock/MockConnection.php @@ -62,7 +62,7 @@ class MockConnection extends BaseConnection $query->setQuery($sql, $binds, $setEscapeFlags); - if (! empty($this->swapPre) && ! empty($this->DBPrefix)) { + if ($this->swapPre !== '' && $this->DBPrefix !== '') { $query->swapPrefix($this->DBPrefix, $this->swapPre); } diff --git a/system/Test/Mock/MockResponse.php b/system/Test/Mock/MockResponse.php old mode 100644 new mode 100755 diff --git a/system/Test/TestResponse.php b/system/Test/TestResponse.php index 956b3218..215942d5 100644 --- a/system/Test/TestResponse.php +++ b/system/Test/TestResponse.php @@ -136,8 +136,10 @@ class TestResponse extends TestCase return false; } + $body = (string) $this->response->getBody(); + // Empty bodies are not considered valid, unless in redirects - return ! ($status < 300 && empty($this->response->getBody())); + return ! ($status < 300 && $body === ''); } /** diff --git a/system/Test/bootstrap.php b/system/Test/bootstrap.php index 3145725a..3e2d54fd 100644 --- a/system/Test/bootstrap.php +++ b/system/Test/bootstrap.php @@ -46,7 +46,7 @@ defined('CIPATH') || define('CIPATH', realpath(SYSTEMPATH . '../') . DIRE defined('FCPATH') || define('FCPATH', realpath(PUBLICPATH) . DIRECTORY_SEPARATOR); defined('TESTPATH') || define('TESTPATH', realpath(HOMEPATH . 'tests/') . DIRECTORY_SEPARATOR); defined('SUPPORTPATH') || define('SUPPORTPATH', realpath(TESTPATH . '_support/') . DIRECTORY_SEPARATOR); -defined('COMPOSER_PATH') || define('COMPOSER_PATH', realpath(HOMEPATH . 'vendor/autoload.php')); +defined('COMPOSER_PATH') || define('COMPOSER_PATH', (string) realpath(HOMEPATH . 'vendor/autoload.php')); defined('VENDORPATH') || define('VENDORPATH', realpath(HOMEPATH . 'vendor') . DIRECTORY_SEPARATOR); // Load Common.php from App then System @@ -75,6 +75,7 @@ require_once APPPATH . 'Config/Services.php'; // Initialize and register the loader with the SPL autoloader stack. Services::autoloader()->initialize(new Autoload(), new Modules())->register(); +Services::autoloader()->loadHelpers(); // Now load Composer's if it's available if (is_file(COMPOSER_PATH)) { diff --git a/system/ThirdParty/Kint/CallFinder.php b/system/ThirdParty/Kint/CallFinder.php index e36e91ea..f85b1ae0 100644 --- a/system/ThirdParty/Kint/CallFinder.php +++ b/system/ThirdParty/Kint/CallFinder.php @@ -138,7 +138,7 @@ class CallFinder /** * @psalm-param callable-array|callable-string $function * - * @param mixed $function + * @psalm-return list}> * * @return array List of matching calls on the relevant line */ @@ -187,6 +187,7 @@ class CallFinder $identifier[T_NAME_RELATIVE] = true; } + /** @psalm-var list */ $tokens = \token_get_all($source); $cursor = 1; $function_calls = []; @@ -449,8 +450,6 @@ class CallFinder * for `$token[0]` then "..." will incorrectly match the "." operator. * * @psalm-param PhpToken $token The token to check - * - * @param mixed $token */ private static function tokenIsOperator($token): bool { diff --git a/system/ThirdParty/Kint/Kint.php b/system/ThirdParty/Kint/Kint.php index 47e34abe..418c1f3f 100644 --- a/system/ThirdParty/Kint/Kint.php +++ b/system/ThirdParty/Kint/Kint.php @@ -133,7 +133,7 @@ class Kint implements FacadeInterface public static $aliases = [ ['Kint\\Kint', 'dump'], ['Kint\\Kint', 'trace'], - ['Kint\\Kint', 'dumpArray'], + ['Kint\\Kint', 'dumpAll'], ]; /** @@ -537,7 +537,7 @@ class Kint implements FacadeInterface * * Functionally equivalent to Kint::dump(1) or Kint::dump(debug_backtrace()) * - * @param mixed ...$args + * @psalm-param array ...$args * * @return int|string */ @@ -599,6 +599,7 @@ class Kint implements FacadeInterface $match = '/'; foreach (static::$app_root_dirs as $path => $alias) { + /** @psalm-var string $path */ if (empty($path)) { continue; } diff --git a/system/ThirdParty/Kint/Parser/BlacklistPlugin.php b/system/ThirdParty/Kint/Parser/BlacklistPlugin.php index 90ec3c22..fa54a3a6 100644 --- a/system/ThirdParty/Kint/Parser/BlacklistPlugin.php +++ b/system/ThirdParty/Kint/Parser/BlacklistPlugin.php @@ -29,6 +29,7 @@ namespace Kint\Parser; use Kint\Zval\InstanceValue; use Kint\Zval\Value; +use Psr\Container\ContainerInterface; class BlacklistPlugin extends AbstractPlugin { @@ -44,7 +45,7 @@ class BlacklistPlugin extends AbstractPlugin * * @var array */ - public static $shallow_blacklist = ['Psr\\Container\\ContainerInterface']; + public static $shallow_blacklist = [ContainerInterface::class]; public function getTypes(): array { diff --git a/system/ThirdParty/Kint/Parser/ClosurePlugin.php b/system/ThirdParty/Kint/Parser/ClosurePlugin.php index 8b199781..84ea5826 100644 --- a/system/ThirdParty/Kint/Parser/ClosurePlugin.php +++ b/system/ThirdParty/Kint/Parser/ClosurePlugin.php @@ -67,7 +67,7 @@ class ClosurePlugin extends AbstractPlugin } $p = new Representation('Parameters'); - $p->contents = &$o->parameters; + $p->contents = $o->parameters; $o->addRepresentation($p, 0); $statics = []; diff --git a/system/ThirdParty/Kint/Parser/Parser.php b/system/ThirdParty/Kint/Parser/Parser.php index 50642b90..f044a9de 100644 --- a/system/ThirdParty/Kint/Parser/Parser.php +++ b/system/ThirdParty/Kint/Parser/Parser.php @@ -72,7 +72,7 @@ class Parser * @param int $depth_limit Maximum depth to parse data * @param ?string $caller Caller class name */ - public function __construct(int $depth_limit = 0, ?string $caller = null) + public function __construct(int $depth_limit = 0, string $caller = null) { $this->marker = "kint\0".\random_bytes(16); @@ -83,7 +83,7 @@ class Parser /** * Set the caller class. */ - public function setCallerClass(?string $caller = null): void + public function setCallerClass(string $caller = null): void { $this->noRecurseCall(); diff --git a/system/ThirdParty/Kint/Parser/PluginInterface.php b/system/ThirdParty/Kint/Parser/PluginInterface.php index ff169fd1..3afb1a77 100644 --- a/system/ThirdParty/Kint/Parser/PluginInterface.php +++ b/system/ThirdParty/Kint/Parser/PluginInterface.php @@ -38,7 +38,7 @@ interface PluginInterface public function getTriggers(): int; /** - * @param mixed &$var + * @psalm-param mixed &$var */ public function parse(&$var, Value &$o, int $trigger): void; } diff --git a/system/ThirdParty/Kint/Parser/TablePlugin.php b/system/ThirdParty/Kint/Parser/TablePlugin.php index 8d7e155c..5afe7598 100644 --- a/system/ThirdParty/Kint/Parser/TablePlugin.php +++ b/system/ThirdParty/Kint/Parser/TablePlugin.php @@ -30,6 +30,12 @@ namespace Kint\Parser; use Kint\Zval\Representation\Representation; use Kint\Zval\Value; +// Note: Interaction with ArrayLimitPlugin: +// Any array limited children will be shown in tables identically to +// non-array-limited children since the table only shows that it is an array +// and it's size anyway. Because ArrayLimitPlugin halts the parse on finding +// a limit all other plugins including this one are stopped, so you cannot get +// a tabular representation of an array that is longer than the limit. class TablePlugin extends AbstractPlugin { public function getTypes(): array diff --git a/system/ThirdParty/Kint/Renderer/RichRenderer.php b/system/ThirdParty/Kint/Renderer/RichRenderer.php index f206e560..b3d86ea1 100644 --- a/system/ThirdParty/Kint/Renderer/RichRenderer.php +++ b/system/ThirdParty/Kint/Renderer/RichRenderer.php @@ -140,6 +140,13 @@ class RichRenderer extends AbstractRenderer */ public static $sort = self::SORT_NONE; + /** + * Timestamp to print in footer in date() format. + * + * @var ?string + */ + public static $timestamp = null; + public static $needs_pre_render = true; public static $needs_folder_render = true; @@ -465,29 +472,7 @@ class RichRenderer extends AbstractRenderer $output .= ''; } - if (isset($this->call_info['callee']['file'])) { - $output .= 'Called from '.$this->ideLink( - $this->call_info['callee']['file'], - $this->call_info['callee']['line'] - ); - } - - if ( - isset($this->call_info['callee']['function']) && - ( - !empty($this->call_info['callee']['class']) || - !\in_array( - $this->call_info['callee']['function'], - ['include', 'include_once', 'require', 'require_once'], - true - ) - ) - ) { - $output .= ' ['; - $output .= $this->call_info['callee']['class'] ?? ''; - $output .= $this->call_info['callee']['type'] ?? ''; - $output .= $this->call_info['callee']['function'].'()]'; - } + $output .= $this->calledFrom(); if (!empty($this->call_info['trace']) && \count($this->call_info['trace']) > 1) { $output .= '
      '; @@ -497,8 +482,8 @@ class RichRenderer extends AbstractRenderer } $output .= '
    1. '.$this->ideLink($step['file'], $step['line']); // closing tag not required - if (isset($step['function']) - && !\in_array($step['function'], ['include', 'include_once', 'require', 'require_once'], true) + if (isset($step['function']) && + !\in_array($step['function'], ['include', 'include_once', 'require', 'require_once'], true) ) { $output .= ' ['; $output .= $step['class'] ?? ''; @@ -516,8 +501,6 @@ class RichRenderer extends AbstractRenderer /** * @psalm-param Encoding $encoding - * - * @param mixed $encoding */ public function escape(string $string, $encoding = false): string { @@ -559,6 +542,45 @@ class RichRenderer extends AbstractRenderer return ''.$path.''; } + protected function calledFrom(): string + { + $output = ''; + + if (isset($this->call_info['callee']['file'])) { + $output .= ' '.$this->ideLink( + $this->call_info['callee']['file'], + $this->call_info['callee']['line'] + ); + } + + if ( + isset($this->call_info['callee']['function']) && + ( + !empty($this->call_info['callee']['class']) || + !\in_array( + $this->call_info['callee']['function'], + ['include', 'include_once', 'require', 'require_once'], + true + ) + ) + ) { + $output .= ' ['; + $output .= $this->call_info['callee']['class'] ?? ''; + $output .= $this->call_info['callee']['type'] ?? ''; + $output .= $this->call_info['callee']['function'].'()]'; + } + + if ('' !== $output) { + $output = 'Called from'.$output; + } + + if (null !== self::$timestamp) { + $output .= ' '.\date(self::$timestamp); + } + + return $output; + } + protected function renderTab(Value $o, Representation $rep): string { if (($plugin = $this->getPlugin(self::$tab_plugins, $rep->hints)) && $plugin instanceof TabPluginInterface) { diff --git a/system/ThirdParty/Kint/Renderer/Text/AbstractPlugin.php b/system/ThirdParty/Kint/Renderer/Text/AbstractPlugin.php index 4d3c7e2a..16feccaa 100644 --- a/system/ThirdParty/Kint/Renderer/Text/AbstractPlugin.php +++ b/system/ThirdParty/Kint/Renderer/Text/AbstractPlugin.php @@ -42,7 +42,7 @@ abstract class AbstractPlugin implements PluginInterface $this->renderer = $r; } - public function renderLockedHeader(Value $o, ?string $content = null): string + public function renderLockedHeader(Value $o, string $content = null): string { $out = ''; diff --git a/system/ThirdParty/Kint/Renderer/TextRenderer.php b/system/ThirdParty/Kint/Renderer/TextRenderer.php index 833320a6..2538e4da 100644 --- a/system/ThirdParty/Kint/Renderer/TextRenderer.php +++ b/system/ThirdParty/Kint/Renderer/TextRenderer.php @@ -108,6 +108,13 @@ class TextRenderer extends AbstractRenderer */ public static $sort = self::SORT_NONE; + /** + * Timestamp to print in footer in date() format. + * + * @var ?string + */ + public static $timestamp = null; + public $header_width = 80; public $indent_width = 4; @@ -300,7 +307,7 @@ class TextRenderer extends AbstractRenderer { $return = []; - foreach ($plugins as $index => $plugin) { + foreach ($plugins as $plugin) { foreach (self::$parser_plugin_whitelist as $whitelist) { if ($plugin instanceof $whitelist) { $return[] = $plugin; @@ -319,8 +326,6 @@ class TextRenderer extends AbstractRenderer /** * @psalm-param Encoding $encoding - * - * @param mixed $encoding */ public function escape(string $string, $encoding = false): string { @@ -355,6 +360,13 @@ class TextRenderer extends AbstractRenderer $output .= $this->call_info['callee']['function'].'()]'; } + if (null !== self::$timestamp) { + if (\strlen($output)) { + $output .= ' '; + } + $output .= \date(self::$timestamp); + } + return $output; } diff --git a/system/ThirdParty/Kint/Utils.php b/system/ThirdParty/Kint/Utils.php index 78b5a1e8..54570958 100644 --- a/system/ThirdParty/Kint/Utils.php +++ b/system/ThirdParty/Kint/Utils.php @@ -255,8 +255,6 @@ final class Utils /** * @psalm-param Encoding $encoding - * - * @param mixed $encoding */ public static function truncateString(string $input, int $length = PHP_INT_MAX, string $end = '...', $encoding = false): string { diff --git a/system/ThirdParty/Kint/Zval/BlobValue.php b/system/ThirdParty/Kint/Zval/BlobValue.php index ff5a6a24..5e9f129d 100644 --- a/system/ThirdParty/Kint/Zval/BlobValue.php +++ b/system/ThirdParty/Kint/Zval/BlobValue.php @@ -122,8 +122,6 @@ class BlobValue extends Value /** * @psalm-param Encoding $encoding - * - * @param mixed $encoding */ public static function strlen(string $string, $encoding = false): int { @@ -142,10 +140,8 @@ class BlobValue extends Value /** * @psalm-param Encoding $encoding - * - * @param mixed $encoding */ - public static function substr(string $string, int $start, ?int $length = null, $encoding = false): string + public static function substr(string $string, int $start, int $length = null, $encoding = false): string { if (\function_exists('mb_substr')) { if (false === $encoding) { diff --git a/system/ThirdParty/Kint/Zval/Representation/ColorRepresentation.php b/system/ThirdParty/Kint/Zval/Representation/ColorRepresentation.php index 806a8006..88536034 100644 --- a/system/ThirdParty/Kint/Zval/Representation/ColorRepresentation.php +++ b/system/ThirdParty/Kint/Zval/Representation/ColorRepresentation.php @@ -211,7 +211,7 @@ class ColorRepresentation extends Representation $this->setValues($value); } - public function getColor(?int $variant = null): ?string + public function getColor(int $variant = null): ?string { if (!$variant) { $variant = $this->variant; @@ -275,7 +275,7 @@ class ColorRepresentation extends Representation return null; } - public function hasAlpha(?int $variant = null): bool + public function hasAlpha(int $variant = null): bool { if (null === $variant) { $variant = $this->variant; diff --git a/system/ThirdParty/Kint/Zval/Representation/MicrotimeRepresentation.php b/system/ThirdParty/Kint/Zval/Representation/MicrotimeRepresentation.php index d25539b1..027b0ce2 100644 --- a/system/ThirdParty/Kint/Zval/Representation/MicrotimeRepresentation.php +++ b/system/ThirdParty/Kint/Zval/Representation/MicrotimeRepresentation.php @@ -44,7 +44,7 @@ class MicrotimeRepresentation extends Representation public $mem_peak_real = 0; public $hints = ['microtime']; - public function __construct(int $seconds, int $microseconds, int $group, ?float $lap = null, ?float $total = null, int $i = 0) + public function __construct(int $seconds, int $microseconds, int $group, float $lap = null, float $total = null, int $i = 0) { parent::__construct('Microtime'); diff --git a/system/ThirdParty/Kint/Zval/Representation/Representation.php b/system/ThirdParty/Kint/Zval/Representation/Representation.php index c4680c3d..76d695b8 100644 --- a/system/ThirdParty/Kint/Zval/Representation/Representation.php +++ b/system/ThirdParty/Kint/Zval/Representation/Representation.php @@ -36,7 +36,7 @@ class Representation protected $name; - public function __construct(string $label, ?string $name = null) + public function __construct(string $label, string $name = null) { $this->label = $label; diff --git a/system/ThirdParty/Kint/Zval/Representation/SourceRepresentation.php b/system/ThirdParty/Kint/Zval/Representation/SourceRepresentation.php index d05bf088..2fefc582 100644 --- a/system/ThirdParty/Kint/Zval/Representation/SourceRepresentation.php +++ b/system/ThirdParty/Kint/Zval/Representation/SourceRepresentation.php @@ -57,7 +57,7 @@ class SourceRepresentation extends Representation * @param int $start_line The first line to display (1 based) * @param null|int $length Amount of lines to show */ - public static function getSource(string $filename, int $start_line = 1, ?int $length = null): ?array + public static function getSource(string $filename, int $start_line = 1, int $length = null): ?array { if (!$filename || !\file_exists($filename) || !\is_readable($filename)) { return null; diff --git a/system/ThirdParty/Kint/Zval/Value.php b/system/ThirdParty/Kint/Zval/Value.php index facb6cd8..01a206d4 100644 --- a/system/ThirdParty/Kint/Zval/Value.php +++ b/system/ThirdParty/Kint/Zval/Value.php @@ -53,8 +53,8 @@ class Value public $reference = false; public $depth = 0; public $size; - public $value; public $hints = []; + public $value; protected $representations = []; @@ -62,7 +62,7 @@ class Value { } - public function addRepresentation(Representation $rep, ?int $pos = null): bool + public function addRepresentation(Representation $rep, int $pos = null): bool { if (isset($this->representations[$rep->getName()])) { return false; @@ -81,7 +81,7 @@ class Value return true; } - public function replaceRepresentation(Representation $rep, ?int $pos = null): void + public function replaceRepresentation(Representation $rep, int $pos = null): void { if (null === $pos) { $this->representations[$rep->getName()] = $rep; @@ -234,7 +234,7 @@ class Value /** * Creates a new basic object with a name and access path. */ - public static function blank(?string $name = null, ?string $access_path = null): self + public static function blank(string $name = null, string $access_path = null): self { $o = new self(); $o->name = $name; diff --git a/system/ThirdParty/Kint/init.php b/system/ThirdParty/Kint/init.php index 7605415b..039489ab 100644 --- a/system/ThirdParty/Kint/init.php +++ b/system/ThirdParty/Kint/init.php @@ -45,6 +45,7 @@ if (\version_compare(PHP_VERSION, '7.1') < 0) { \define('KINT_PHP81', \version_compare(PHP_VERSION, '8.1') >= 0); \define('KINT_PHP82', \version_compare(PHP_VERSION, '8.2') >= 0); \define('KINT_PHP83', \version_compare(PHP_VERSION, '8.3') >= 0); +\define('KINT_PHP84', \version_compare(PHP_VERSION, '8.4') >= 0); // Dynamic default settings if (false !== \ini_get('xdebug.file_link_format')) { diff --git a/system/ThirdParty/Kint/init_helpers.php b/system/ThirdParty/Kint/init_helpers.php index 561425b2..b4d5f4d9 100644 --- a/system/ThirdParty/Kint/init_helpers.php +++ b/system/ThirdParty/Kint/init_helpers.php @@ -32,7 +32,7 @@ if (!\function_exists('d')) { /** * Alias of Kint::dump(). * - * @param mixed ...$args + * @psalm-param mixed ...$args * * @return int|string */ @@ -53,7 +53,7 @@ if (!\function_exists('s')) { * * If run in CLI colors are disabled * - * @param mixed ...$args + * @psalm-param mixed ...$args * * @return int|string */ diff --git a/system/ThirdParty/Kint/resources/compiled/aante-dark.css b/system/ThirdParty/Kint/resources/compiled/aante-dark.css new file mode 100644 index 00000000..6aa8b702 --- /dev/null +++ b/system/ThirdParty/Kint/resources/compiled/aante-dark.css @@ -0,0 +1 @@ +.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:rgba(0,0,0,.9)}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:8px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#555;color:#e2e1e1}.kint-rich .kint-focused{box-shadow:0 0 3px 2px aqua}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#e2e1e1;float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:8px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#070707;border:1px solid #282828;color:#e2e1e1;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:4px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#555}.kint-rich>dl dl{padding:0 0 0 12px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiNBQUEiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjQUFBIi8+PC9zdmc+") no-repeat scroll 0 0/15px 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #282828}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#f90;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:aqua}.kint-rich dfn{font-style:normal;font-family:monospace;color:#e2e1e1}.kint-rich pre{color:#e2e1e1;margin:0 0 0 12px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #282828;background:#070707;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(226,225,225,.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#070707;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#e2e1e1;background:#070707}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #282828;border-top-width:0;border-bottom-width:0;padding:4px;float:right !important;margin:-4px 0;color:#e2e1e1;background:#070707;height:24px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#070707;opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#070707}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#070707;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-popup-trigger{background:rgba(0,0,0,0);color:#e2e1e1}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#e2e1e1;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#e2e1e1;border-bottom:1px dotted #e2e1e1}.kint-rich ul{list-style:none;padding-left:12px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #282828}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 12px;padding-left:0;background:#070707;border:1px solid #282828;border-top:0}.kint-rich ul.kint-tabs>li{background:#070707;border:1px solid #282828;cursor:pointer;display:inline-block;height:24px;margin:2px;padding:0 12px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#555;color:aqua}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#070707;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:20px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#555;color:aqua}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #282828;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#555}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #282828;padding:2px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#070707;color:#e2e1e1}.kint-rich table td{background:#070707;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #555 inset}.kint-rich table tr:hover var{color:aqua}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:4px;padding-bottom:4px;border-bottom:1px solid #070707}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #555;padding-right:8px;margin-right:8px}.kint-rich pre.kint-source>div.kint-highlight{background:#070707}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #555,0 1px #555,1px 0 #555,0 -1px #555;color:#070707;font-weight:bold}input.kint-note-input{width:100%}.kint-rich .kint-focused{box-shadow:0 0 3px 2px aqua}.kint-rich dt{font-weight:normal}.kint-rich dt.kint-parent{margin-top:4px}.kint-rich dl dl{margin-top:4px;padding-left:25px;border-left:none}.kint-rich>dl>dt{background:#070707}.kint-rich ul{margin:0;padding-left:0}.kint-rich ul:not(.kint-tabs)>li{border-left:0}.kint-rich ul.kint-tabs{background:#070707;border:1px solid #282828;border-width:0 1px 1px 1px;padding:4px 0 0 12px;margin-left:-1px;margin-top:-1px}.kint-rich ul.kint-tabs li,.kint-rich ul.kint-tabs li+li{margin:0 0 0 4px}.kint-rich ul.kint-tabs li{border-bottom-width:0;height:25px}.kint-rich ul.kint-tabs li:first-child{margin-left:0}.kint-rich ul.kint-tabs li.kint-active-tab{border-top:1px solid #282828;background:#000;font-weight:bold;padding-top:0;border-bottom:1px solid #000 !important;margin-bottom:-1px}.kint-rich ul.kint-tabs li.kint-active-tab:hover{border-bottom:1px solid #000}.kint-rich ul>li>pre{border:1px solid #282828}.kint-rich dt:hover+dd>ul{border-color:#555}.kint-rich pre{background:#000;margin-top:4px;margin-left:25px}.kint-rich .kint-source{margin-left:-1px}.kint-rich .kint-source .kint-highlight{background:#303}.kint-rich .kint-parent.kint-show>.kint-search{border-bottom-width:1px}.kint-rich table td{background:#000}.kint-rich table td>dl{padding:0;margin:0}.kint-rich table td>dl>dt.kint-parent{margin:0}.kint-rich table td:first-child,.kint-rich table td,.kint-rich table th{padding:2px 4px}.kint-rich table dd,.kint-rich table dt{background:#000}.kint-rich table tr:hover>td{box-shadow:none;background:#303} diff --git a/system/ThirdParty/Kint/resources/compiled/aante-light.css b/system/ThirdParty/Kint/resources/compiled/aante-light.css index 832dee71..16194736 100644 --- a/system/ThirdParty/Kint/resources/compiled/aante-light.css +++ b/system/ThirdParty/Kint/resources/compiled/aante-light.css @@ -1 +1 @@ -.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:rgba(255,255,255,0.9)}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:8px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#aaa;color:#1d1e1e}.kint-rich .kint-focused{box-shadow:0 0 3px 2px red}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#1d1e1e;float:none !important;font-family:Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:8px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#f8f8f8;border:1px solid #d7d7d7;color:#1d1e1e;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:4px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#aaa}.kint-rich>dl dl{padding:0 0 0 12px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") no-repeat scroll 0 0/15px 75px transparent;cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #d7d7d7}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#06f;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:red}.kint-rich dfn{font-style:normal;font-family:monospace;color:#1d1e1e}.kint-rich pre{color:#1d1e1e;margin:0 0 0 12px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #d7d7d7;background:#f8f8f8;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(29,30,30,0.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#f8f8f8;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#1d1e1e;background:#f8f8f8}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #d7d7d7;border-top-width:0;border-bottom-width:0;padding:4px;float:right !important;margin:-4px 0;color:#1d1e1e;background:#f8f8f8;height:24px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#f8f8f8;opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#f8f8f8}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#f8f8f8;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:transparent}.kint-rich footer>.kint-popup-trigger{background:transparent;color:#1d1e1e}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#1d1e1e;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#1d1e1e;border-bottom:1px dotted #1d1e1e}.kint-rich ul{list-style:none;padding-left:12px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #d7d7d7}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 12px;padding-left:0;background:#f8f8f8;border:1px solid #d7d7d7;border-top:0}.kint-rich ul.kint-tabs>li{background:#f8f8f8;border:1px solid #d7d7d7;cursor:pointer;display:inline-block;height:24px;margin:2px;padding:0 12px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#aaa;color:red}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#f8f8f8;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:20px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#aaa;color:red}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #d7d7d7;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#aaa}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #d7d7d7;padding:2px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#f8f8f8;color:#1d1e1e}.kint-rich table td{background:#f8f8f8;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #aaa inset}.kint-rich table tr:hover var{color:red}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:4px;padding-bottom:4px;border-bottom:1px solid #f8f8f8}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #aaa;padding-right:8px;margin-right:8px}.kint-rich pre.kint-source>div.kint-highlight{background:#f8f8f8}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #aaa,0 1px #aaa,1px 0 #aaa,0 -1px #aaa;color:#f8f8f8;font-weight:bold}input.kint-note-input{width:100%}.kint-rich .kint-focused{box-shadow:0 0 3px 2px red}.kint-rich dt{font-weight:normal}.kint-rich dt.kint-parent{margin-top:4px}.kint-rich dl dl{margin-top:4px;padding-left:25px;border-left:none}.kint-rich>dl>dt{background:#f8f8f8}.kint-rich ul{margin:0;padding-left:0}.kint-rich ul:not(.kint-tabs)>li{border-left:0}.kint-rich ul.kint-tabs{background:#f8f8f8;border:1px solid #d7d7d7;border-width:0 1px 1px 1px;padding:4px 0 0 12px;margin-left:-1px;margin-top:-1px}.kint-rich ul.kint-tabs li,.kint-rich ul.kint-tabs li+li{margin:0 0 0 4px}.kint-rich ul.kint-tabs li{border-bottom-width:0;height:25px}.kint-rich ul.kint-tabs li:first-child{margin-left:0}.kint-rich ul.kint-tabs li.kint-active-tab{border-top:1px solid #d7d7d7;background:#fff;font-weight:bold;padding-top:0;border-bottom:1px solid #fff !important;margin-bottom:-1px}.kint-rich ul.kint-tabs li.kint-active-tab:hover{border-bottom:1px solid #fff}.kint-rich ul>li>pre{border:1px solid #d7d7d7}.kint-rich dt:hover+dd>ul{border-color:#aaa}.kint-rich pre{background:#fff;margin-top:4px;margin-left:25px}.kint-rich .kint-source{margin-left:-1px}.kint-rich .kint-source .kint-highlight{background:#cfc}.kint-rich .kint-parent.kint-show>.kint-search{border-bottom-width:1px}.kint-rich table td{background:#fff}.kint-rich table td>dl{padding:0;margin:0}.kint-rich table td>dl>dt.kint-parent{margin:0}.kint-rich table td:first-child,.kint-rich table td,.kint-rich table th{padding:2px 4px}.kint-rich table dd,.kint-rich table dt{background:#fff}.kint-rich table tr:hover>td{box-shadow:none;background:#cfc} +.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:rgba(255,255,255,.9)}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:8px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#aaa;color:#1d1e1e}.kint-rich .kint-focused{box-shadow:0 0 3px 2px red}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#1d1e1e;float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:8px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#f8f8f8;border:1px solid #d7d7d7;color:#1d1e1e;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:4px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#aaa}.kint-rich>dl dl{padding:0 0 0 12px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") no-repeat scroll 0 0/15px 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #d7d7d7}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#06f;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:red}.kint-rich dfn{font-style:normal;font-family:monospace;color:#1d1e1e}.kint-rich pre{color:#1d1e1e;margin:0 0 0 12px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #d7d7d7;background:#f8f8f8;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(29,30,30,.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#f8f8f8;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#1d1e1e;background:#f8f8f8}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #d7d7d7;border-top-width:0;border-bottom-width:0;padding:4px;float:right !important;margin:-4px 0;color:#1d1e1e;background:#f8f8f8;height:24px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#f8f8f8;opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#f8f8f8}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#f8f8f8;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-popup-trigger{background:rgba(0,0,0,0);color:#1d1e1e}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#1d1e1e;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#1d1e1e;border-bottom:1px dotted #1d1e1e}.kint-rich ul{list-style:none;padding-left:12px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #d7d7d7}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 12px;padding-left:0;background:#f8f8f8;border:1px solid #d7d7d7;border-top:0}.kint-rich ul.kint-tabs>li{background:#f8f8f8;border:1px solid #d7d7d7;cursor:pointer;display:inline-block;height:24px;margin:2px;padding:0 12px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#aaa;color:red}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#f8f8f8;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:20px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#aaa;color:red}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #d7d7d7;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#aaa}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #d7d7d7;padding:2px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#f8f8f8;color:#1d1e1e}.kint-rich table td{background:#f8f8f8;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #aaa inset}.kint-rich table tr:hover var{color:red}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:4px;padding-bottom:4px;border-bottom:1px solid #f8f8f8}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #aaa;padding-right:8px;margin-right:8px}.kint-rich pre.kint-source>div.kint-highlight{background:#f8f8f8}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #aaa,0 1px #aaa,1px 0 #aaa,0 -1px #aaa;color:#f8f8f8;font-weight:bold}input.kint-note-input{width:100%}.kint-rich .kint-focused{box-shadow:0 0 3px 2px red}.kint-rich dt{font-weight:normal}.kint-rich dt.kint-parent{margin-top:4px}.kint-rich dl dl{margin-top:4px;padding-left:25px;border-left:none}.kint-rich>dl>dt{background:#f8f8f8}.kint-rich ul{margin:0;padding-left:0}.kint-rich ul:not(.kint-tabs)>li{border-left:0}.kint-rich ul.kint-tabs{background:#f8f8f8;border:1px solid #d7d7d7;border-width:0 1px 1px 1px;padding:4px 0 0 12px;margin-left:-1px;margin-top:-1px}.kint-rich ul.kint-tabs li,.kint-rich ul.kint-tabs li+li{margin:0 0 0 4px}.kint-rich ul.kint-tabs li{border-bottom-width:0;height:25px}.kint-rich ul.kint-tabs li:first-child{margin-left:0}.kint-rich ul.kint-tabs li.kint-active-tab{border-top:1px solid #d7d7d7;background:#fff;font-weight:bold;padding-top:0;border-bottom:1px solid #fff !important;margin-bottom:-1px}.kint-rich ul.kint-tabs li.kint-active-tab:hover{border-bottom:1px solid #fff}.kint-rich ul>li>pre{border:1px solid #d7d7d7}.kint-rich dt:hover+dd>ul{border-color:#aaa}.kint-rich pre{background:#fff;margin-top:4px;margin-left:25px}.kint-rich .kint-source{margin-left:-1px}.kint-rich .kint-source .kint-highlight{background:#cfc}.kint-rich .kint-parent.kint-show>.kint-search{border-bottom-width:1px}.kint-rich table td{background:#fff}.kint-rich table td>dl{padding:0;margin:0}.kint-rich table td>dl>dt.kint-parent{margin:0}.kint-rich table td:first-child,.kint-rich table td,.kint-rich table th{padding:2px 4px}.kint-rich table dd,.kint-rich table dt{background:#fff}.kint-rich table tr:hover>td{box-shadow:none;background:#cfc} diff --git a/system/ThirdParty/Kint/resources/compiled/original.css b/system/ThirdParty/Kint/resources/compiled/original.css index e3c2bf41..331b2c38 100644 --- a/system/ThirdParty/Kint/resources/compiled/original.css +++ b/system/ThirdParty/Kint/resources/compiled/original.css @@ -1 +1 @@ -.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:rgba(255,255,255,0.9)}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:8px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#0092db;color:#1d1e1e}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #5cb730}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#1d1e1e;float:none !important;font-family:Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:8px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#e0eaef;border:1px solid #b6cedb;color:#1d1e1e;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:4px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#0092db}.kint-rich>dl dl{padding:0 0 0 12px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxnIHN0cm9rZS13aWR0aD0iMiIgZmlsbD0iI0ZGRiI+PHBhdGggZD0iTTEgMWgyOHYyOEgxem01IDE0aDE4bS05IDlWNk0xIDYxaDI4djI4SDF6bTUgMTRoMTgiIHN0cm9rZT0iIzM3OSIvPjxwYXRoIGQ9Ik0xIDMxaDI4djI4SDF6bTUgMTRoMThtLTkgOVYzNk0xIDkxaDI4djI4SDF6bTUgMTRoMTgiIHN0cm9rZT0iIzVBMyIvPjxwYXRoIGQ9Ik0xIDEyMWgyOHYyOEgxem01IDVsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZT0iI0NDQyIvPjwvZz48L3N2Zz4=") no-repeat scroll 0 0/15px 75px transparent;cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #b6cedb}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#0092db;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:#5cb730}.kint-rich dfn{font-style:normal;font-family:monospace;color:#1d1e1e}.kint-rich pre{color:#1d1e1e;margin:0 0 0 12px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #b6cedb;background:#e0eaef;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(29,30,30,0.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#e0eaef;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#1d1e1e;background:#e0eaef}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #b6cedb;border-top-width:0;border-bottom-width:0;padding:4px;float:right !important;margin:-4px 0;color:#1d1e1e;background:#c1d4df;height:24px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#d0d0d0;opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#e8e8e8}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#c1d4df;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:transparent}.kint-rich footer>.kint-popup-trigger{background:transparent;color:#1d1e1e}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#1d1e1e;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#1d1e1e;border-bottom:1px dotted #1d1e1e}.kint-rich ul{list-style:none;padding-left:12px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #b6cedb}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 12px;padding-left:0;background:#e0eaef;border:1px solid #b6cedb;border-top:0}.kint-rich ul.kint-tabs>li{background:#c1d4df;border:1px solid #b6cedb;cursor:pointer;display:inline-block;height:24px;margin:2px;padding:0 12px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#0092db;color:#5cb730}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#e0eaef;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:20px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#0092db;color:#5cb730}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #b6cedb;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#0092db}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #b6cedb;padding:2px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#c1d4df;color:#1d1e1e}.kint-rich table td{background:#e0eaef;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #0092db inset}.kint-rich table tr:hover var{color:#5cb730}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:4px;padding-bottom:4px;border-bottom:1px solid #c1d4df}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #0092db;padding-right:8px;margin-right:8px}.kint-rich pre.kint-source>div.kint-highlight{background:#c1d4df}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #0092db,0 1px #0092db,1px 0 #0092db,0 -1px #0092db;color:#e0eaef;font-weight:bold}input.kint-note-input{width:100%}.kint-rich>dl>dt{background:linear-gradient(to bottom, #e3ecf0 0, #c0d4df 100%)}.kint-rich ul.kint-tabs{background:linear-gradient(to bottom, #9dbed0 0px, #b2ccda 100%)}.kint-rich>dl:not(.kint-trace)>dd>ul.kint-tabs li{background:#e0eaef}.kint-rich>dl:not(.kint-trace)>dd>ul.kint-tabs li.kint-active-tab{background:#c1d4df}.kint-rich>dl.kint-trace>dt{background:linear-gradient(to bottom, #c0d4df 0px, #e3ecf0 100%)}.kint-rich .kint-source .kint-highlight{background:#f0eb96} +.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:rgba(255,255,255,.9)}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:8px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#0092db;color:#1d1e1e}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #5cb730}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#1d1e1e;float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:8px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#e0eaef;border:1px solid #b6cedb;color:#1d1e1e;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:4px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#0092db}.kint-rich>dl dl{padding:0 0 0 12px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxnIHN0cm9rZS13aWR0aD0iMiIgZmlsbD0iI0ZGRiI+PHBhdGggZD0iTTEgMWgyOHYyOEgxem01IDE0aDE4bS05IDlWNk0xIDYxaDI4djI4SDF6bTUgMTRoMTgiIHN0cm9rZT0iIzM3OSIvPjxwYXRoIGQ9Ik0xIDMxaDI4djI4SDF6bTUgMTRoMThtLTkgOVYzNk0xIDkxaDI4djI4SDF6bTUgMTRoMTgiIHN0cm9rZT0iIzVBMyIvPjxwYXRoIGQ9Ik0xIDEyMWgyOHYyOEgxem01IDVsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZT0iI0NDQyIvPjwvZz48L3N2Zz4=") no-repeat scroll 0 0/15px 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #b6cedb}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#0092db;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:#5cb730}.kint-rich dfn{font-style:normal;font-family:monospace;color:#1d1e1e}.kint-rich pre{color:#1d1e1e;margin:0 0 0 12px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #b6cedb;background:#e0eaef;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(29,30,30,.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#e0eaef;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#1d1e1e;background:#e0eaef}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #b6cedb;border-top-width:0;border-bottom-width:0;padding:4px;float:right !important;margin:-4px 0;color:#1d1e1e;background:#c1d4df;height:24px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#d0d0d0;opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#e8e8e8}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#c1d4df;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-popup-trigger{background:rgba(0,0,0,0);color:#1d1e1e}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#1d1e1e;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#1d1e1e;border-bottom:1px dotted #1d1e1e}.kint-rich ul{list-style:none;padding-left:12px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #b6cedb}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 12px;padding-left:0;background:#e0eaef;border:1px solid #b6cedb;border-top:0}.kint-rich ul.kint-tabs>li{background:#c1d4df;border:1px solid #b6cedb;cursor:pointer;display:inline-block;height:24px;margin:2px;padding:0 12px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#0092db;color:#5cb730}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#e0eaef;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:20px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#0092db;color:#5cb730}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #b6cedb;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#0092db}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #b6cedb;padding:2px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#c1d4df;color:#1d1e1e}.kint-rich table td{background:#e0eaef;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #0092db inset}.kint-rich table tr:hover var{color:#5cb730}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:4px;padding-bottom:4px;border-bottom:1px solid #c1d4df}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #0092db;padding-right:8px;margin-right:8px}.kint-rich pre.kint-source>div.kint-highlight{background:#c1d4df}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #0092db,0 1px #0092db,1px 0 #0092db,0 -1px #0092db;color:#e0eaef;font-weight:bold}input.kint-note-input{width:100%}.kint-rich>dl>dt{background:linear-gradient(to bottom, #e3ecf0 0, #c0d4df 100%)}.kint-rich ul.kint-tabs{background:linear-gradient(to bottom, #9dbed0 0px, #b2ccda 100%)}.kint-rich>dl:not(.kint-trace)>dd>ul.kint-tabs li{background:#e0eaef}.kint-rich>dl:not(.kint-trace)>dd>ul.kint-tabs li.kint-active-tab{background:#c1d4df}.kint-rich>dl.kint-trace>dt{background:linear-gradient(to bottom, #c0d4df 0px, #e3ecf0 100%)}.kint-rich .kint-source .kint-highlight{background:#f0eb96} diff --git a/system/ThirdParty/Kint/resources/compiled/plain.css b/system/ThirdParty/Kint/resources/compiled/plain.css index ba1eba0a..85a4e739 100644 --- a/system/ThirdParty/Kint/resources/compiled/plain.css +++ b/system/ThirdParty/Kint/resources/compiled/plain.css @@ -1 +1 @@ -.kint-plain{background:rgba(255,255,255,0.9);white-space:pre;display:block;font-family:monospace;color:#222}.kint-plain i{color:#d00;font-style:normal}.kint-plain u{color:#030;text-decoration:none;font-weight:bold}.kint-plain .kint-microtime-lap{font-weight:bold;text-shadow:1px 0 #fff, 0 1px #fff, -1px 0 #fff, 0 -1px #fff} +.kint-plain{background:rgba(255,255,255,.9);white-space:pre;display:block;font-family:monospace;color:#222}.kint-plain i{color:#d00;font-style:normal}.kint-plain u{color:#030;text-decoration:none;font-weight:bold}.kint-plain .kint-microtime-lap{font-weight:bold;text-shadow:1px 0 #fff,0 1px #fff,-1px 0 #fff,0 -1px #fff} diff --git a/system/ThirdParty/Kint/resources/compiled/solarized-dark.css b/system/ThirdParty/Kint/resources/compiled/solarized-dark.css index 4ab5e287..2cc125ed 100644 --- a/system/ThirdParty/Kint/resources/compiled/solarized-dark.css +++ b/system/ThirdParty/Kint/resources/compiled/solarized-dark.css @@ -1 +1 @@ -.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:#073642}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:10px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#268bd2;color:#839496}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #2aa198}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#839496;float:none !important;font-family:Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:10px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#002b36;border:1px solid #586e75;color:#839496;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:5px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#268bd2}.kint-rich>dl dl{padding:0 0 0 15px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMzAgMTUwIj48ZGVmcz48cGF0aCBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNNCAzYTI0IDMyIDAgMCAxIDAgMjQgNDAgMjAtMTAgMCAxIDIzLTEyQTQwIDIwIDEwIDAgMSA0IDN6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSIjOTNhMWExIiBzdHJva2U9IiM5M2ExYTEiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxnIGZpbGw9IiM1ODZlNzUiIHN0cm9rZT0iIzU4NmU3NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAzMCkiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxwYXRoIGQ9Ik02IDEyNmwxOCAxOG0tMTggMGwxOC0xOCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM1ODZlNzUiLz48L3N2Zz4=") no-repeat scroll 0 0/15px 75px transparent;cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #586e75}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#268bd2;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:#2aa198}.kint-rich dfn{font-style:normal;font-family:monospace;color:#93a1a1}.kint-rich pre{color:#839496;margin:0 0 0 15px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #586e75;background:#002b36;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(131,148,150,0.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#002b36;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#839496;background:#002b36}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #586e75;border-top-width:0;border-bottom-width:0;padding:5px;float:right !important;margin:-5px 0;color:#93a1a1;background:#073642;height:26px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#252525;opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#1b1b1b}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#073642;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:transparent}.kint-rich footer>.kint-popup-trigger{background:transparent;color:#839496}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#839496;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#93a1a1;border-bottom:1px dotted #93a1a1}.kint-rich ul{list-style:none;padding-left:15px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #586e75}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 15px;padding-left:0;background:#002b36;border:1px solid #586e75;border-top:0}.kint-rich ul.kint-tabs>li{background:#073642;border:1px solid #586e75;cursor:pointer;display:inline-block;height:30px;margin:3px;padding:0 15px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#268bd2;color:#2aa198}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#002b36;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:25px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#268bd2;color:#2aa198}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #586e75;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#268bd2}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2.5px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #586e75;padding:2.5px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#073642;color:#93a1a1}.kint-rich table td{background:#002b36;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #268bd2 inset}.kint-rich table tr:hover var{color:#2aa198}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:5px;padding-bottom:5px;border-bottom:1px solid #073642}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #268bd2;padding-right:10px;margin-right:10px}.kint-rich pre.kint-source>div.kint-highlight{background:#073642}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #268bd2,0 1px #268bd2,1px 0 #268bd2,0 -1px #268bd2;color:#002b36;font-weight:bold}input.kint-note-input{width:100%}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px #268bd2 inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px}.kint-rich footer li{color:#ddd} +.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:#073642}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:10px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#268bd2;color:#839496}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #2aa198}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#839496;float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:10px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#002b36;border:1px solid #586e75;color:#839496;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:5px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#268bd2}.kint-rich>dl dl{padding:0 0 0 15px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMzAgMTUwIj48ZGVmcz48cGF0aCBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNNCAzYTI0IDMyIDAgMCAxIDAgMjQgNDAgMjAtMTAgMCAxIDIzLTEyQTQwIDIwIDEwIDAgMSA0IDN6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSIjOTNhMWExIiBzdHJva2U9IiM5M2ExYTEiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxnIGZpbGw9IiM1ODZlNzUiIHN0cm9rZT0iIzU4NmU3NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAzMCkiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxwYXRoIGQ9Ik02IDEyNmwxOCAxOG0tMTggMGwxOC0xOCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM1ODZlNzUiLz48L3N2Zz4=") no-repeat scroll 0 0/15px 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #586e75}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#268bd2;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:#2aa198}.kint-rich dfn{font-style:normal;font-family:monospace;color:#93a1a1}.kint-rich pre{color:#839496;margin:0 0 0 15px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #586e75;background:#002b36;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(131,148,150,.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#002b36;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#839496;background:#002b36}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #586e75;border-top-width:0;border-bottom-width:0;padding:5px;float:right !important;margin:-5px 0;color:#93a1a1;background:#073642;height:26px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#252525;opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#1b1b1b}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#073642;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-popup-trigger{background:rgba(0,0,0,0);color:#839496}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#839496;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#93a1a1;border-bottom:1px dotted #93a1a1}.kint-rich ul{list-style:none;padding-left:15px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #586e75}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 15px;padding-left:0;background:#002b36;border:1px solid #586e75;border-top:0}.kint-rich ul.kint-tabs>li{background:#073642;border:1px solid #586e75;cursor:pointer;display:inline-block;height:30px;margin:3px;padding:0 15px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#268bd2;color:#2aa198}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#002b36;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:25px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#268bd2;color:#2aa198}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #586e75;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#268bd2}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2.5px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #586e75;padding:2.5px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#073642;color:#93a1a1}.kint-rich table td{background:#002b36;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #268bd2 inset}.kint-rich table tr:hover var{color:#2aa198}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:5px;padding-bottom:5px;border-bottom:1px solid #073642}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #268bd2;padding-right:10px;margin-right:10px}.kint-rich pre.kint-source>div.kint-highlight{background:#073642}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #268bd2,0 1px #268bd2,1px 0 #268bd2,0 -1px #268bd2;color:#002b36;font-weight:bold}input.kint-note-input{width:100%}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px #268bd2 inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px}.kint-rich footer li{color:#ddd} diff --git a/system/ThirdParty/Kint/resources/compiled/solarized.css b/system/ThirdParty/Kint/resources/compiled/solarized.css index 952453ed..8c1a67f0 100644 --- a/system/ThirdParty/Kint/resources/compiled/solarized.css +++ b/system/ThirdParty/Kint/resources/compiled/solarized.css @@ -1 +1 @@ -.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:rgba(255,255,255,0.9)}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:10px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#268bd2;color:#657b83}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #2aa198}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#657b83;float:none !important;font-family:Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:10px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#fdf6e3;border:1px solid #93a1a1;color:#657b83;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:5px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#268bd2}.kint-rich>dl dl{padding:0 0 0 15px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMzAgMTUwIj48ZGVmcz48cGF0aCBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNNCAzYTI0IDMyIDAgMCAxIDAgMjQgNDAgMjAtMTAgMCAxIDIzLTEyQTQwIDIwIDEwIDAgMSA0IDN6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSIjOTNhMWExIiBzdHJva2U9IiM5M2ExYTEiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxnIGZpbGw9IiM1ODZlNzUiIHN0cm9rZT0iIzU4NmU3NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAzMCkiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxwYXRoIGQ9Ik02IDEyNmwxOCAxOG0tMTggMGwxOC0xOCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM1ODZlNzUiLz48L3N2Zz4=") no-repeat scroll 0 0/15px 75px transparent;cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #93a1a1}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#268bd2;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:#2aa198}.kint-rich dfn{font-style:normal;font-family:monospace;color:#586e75}.kint-rich pre{color:#657b83;margin:0 0 0 15px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #93a1a1;background:#fdf6e3;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(101,123,131,0.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#fdf6e3;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#657b83;background:#fdf6e3}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #93a1a1;border-top-width:0;border-bottom-width:0;padding:5px;float:right !important;margin:-5px 0;color:#586e75;background:#eee8d5;height:26px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#e2e2e2;opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:0.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#f0f0f0}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#eee8d5;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:transparent}.kint-rich footer>.kint-popup-trigger{background:transparent;color:#657b83}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#657b83;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#586e75;border-bottom:1px dotted #586e75}.kint-rich ul{list-style:none;padding-left:15px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #93a1a1}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 15px;padding-left:0;background:#fdf6e3;border:1px solid #93a1a1;border-top:0}.kint-rich ul.kint-tabs>li{background:#eee8d5;border:1px solid #93a1a1;cursor:pointer;display:inline-block;height:30px;margin:3px;padding:0 15px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#268bd2;color:#2aa198}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#fdf6e3;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:25px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#268bd2;color:#2aa198}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #93a1a1;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#268bd2}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2.5px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #93a1a1;padding:2.5px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#eee8d5;color:#586e75}.kint-rich table td{background:#fdf6e3;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #268bd2 inset}.kint-rich table tr:hover var{color:#2aa198}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:5px;padding-bottom:5px;border-bottom:1px solid #eee8d5}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #268bd2;padding-right:10px;margin-right:10px}.kint-rich pre.kint-source>div.kint-highlight{background:#eee8d5}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #268bd2,0 1px #268bd2,1px 0 #268bd2,0 -1px #268bd2;color:#fdf6e3;font-weight:bold}input.kint-note-input{width:100%}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px #268bd2 inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px} +.kint-rich{font-size:13px;overflow-x:auto;white-space:nowrap;background:rgba(255,255,255,.9)}.kint-rich.kint-folder{position:fixed;bottom:0;left:0;right:0;z-index:999999;width:100%;margin:0;display:block}.kint-rich.kint-folder dd.kint-foldout{max-height:calc(100vh - 100px);padding-right:10px;overflow-y:scroll;display:none}.kint-rich.kint-folder dd.kint-foldout.kint-show{display:block}.kint-rich::selection,.kint-rich::-moz-selection,.kint-rich::-webkit-selection{background:#268bd2;color:#657b83}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #2aa198}.kint-rich,.kint-rich::before,.kint-rich::after,.kint-rich *,.kint-rich *::before,.kint-rich *::after{box-sizing:border-box;border-radius:0;color:#657b83;float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint-rich{margin:10px 0}.kint-rich dt,.kint-rich dl{width:auto}.kint-rich dt,.kint-rich div.access-path{background:#fdf6e3;border:1px solid #93a1a1;color:#657b83;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:5px}.kint-rich dt:hover,.kint-rich div.access-path:hover{border-color:#268bd2}.kint-rich>dl dl{padding:0 0 0 15px}.kint-rich dt.kint-parent>nav,.kint-rich>footer>nav{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMzAgMTUwIj48ZGVmcz48cGF0aCBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNNCAzYTI0IDMyIDAgMCAxIDAgMjQgNDAgMjAtMTAgMCAxIDIzLTEyQTQwIDIwIDEwIDAgMSA0IDN6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSIjOTNhMWExIiBzdHJva2U9IiM5M2ExYTEiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxnIGZpbGw9IiM1ODZlNzUiIHN0cm9rZT0iIzU4NmU3NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAzMCkiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCAtMTUgNDUpIi8+PC9nPjxwYXRoIGQ9Ik02IDEyNmwxOCAxOG0tMTggMGwxOC0xOCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM1ODZlNzUiLz48L3N2Zz4=") no-repeat scroll 0 0/15px 75px rgba(0,0,0,0);cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint-rich dt.kint-parent:hover>nav,.kint-rich>footer>nav:hover{background-position:0 25%}.kint-rich dt.kint-parent.kint-show>nav,.kint-rich>footer.kint-show>nav{background-position:0 50%}.kint-rich dt.kint-parent.kint-show:hover>nav,.kint-rich>footer.kint-show>nav:hover{background-position:0 75%}.kint-rich dt.kint-parent.kint-locked>nav{background-position:0 100%}.kint-rich dt.kint-parent+dd{display:none;border-left:1px dashed #93a1a1}.kint-rich dt.kint-parent.kint-show+dd{display:block}.kint-rich var,.kint-rich var a{color:#268bd2;font-style:normal}.kint-rich dt:hover var,.kint-rich dt:hover var a{color:#2aa198}.kint-rich dfn{font-style:normal;font-family:monospace;color:#586e75}.kint-rich pre{color:#657b83;margin:0 0 0 15px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #93a1a1;background:#fdf6e3;display:block;word-break:normal}.kint-rich .kint-popup-trigger,.kint-rich .kint-access-path-trigger,.kint-rich .kint-search-trigger{background:rgba(101,123,131,.8);border-radius:3px;height:16px;font-size:16px;margin-left:5px;font-weight:bold;width:16px;text-align:center;float:right !important;cursor:pointer;color:#fdf6e3;position:relative;overflow:hidden;line-height:17.6px}.kint-rich .kint-popup-trigger:hover,.kint-rich .kint-access-path-trigger:hover,.kint-rich .kint-search-trigger:hover{color:#657b83;background:#fdf6e3}.kint-rich dt.kint-parent>.kint-popup-trigger{line-height:19.2px}.kint-rich .kint-search-trigger{font-size:20px}.kint-rich input.kint-search{display:none;border:1px solid #93a1a1;border-top-width:0;border-bottom-width:0;padding:5px;float:right !important;margin:-5px 0;color:#586e75;background:#eee8d5;height:26px;width:160px;position:relative;z-index:100}.kint-rich input.kint-search.kint-show{display:block}.kint-rich .kint-search-root ul.kint-tabs>li:not(.kint-search-match){background:#e2e2e2;opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match){opacity:.5}.kint-rich .kint-search-root dl:not(.kint-search-match)>dt{background:#f0f0f0}.kint-rich .kint-search-root dl:not(.kint-search-match) dl,.kint-rich .kint-search-root dl:not(.kint-search-match) ul.kint-tabs>li:not(.kint-search-match){opacity:1}.kint-rich div.access-path{background:#eee8d5;display:none;margin-top:5px;padding:4px;white-space:pre}.kint-rich div.access-path.kint-show{display:block}.kint-rich footer{padding:0 3px 3px;font-size:9px;background:rgba(0,0,0,0)}.kint-rich footer>.kint-popup-trigger{background:rgba(0,0,0,0);color:#657b83}.kint-rich footer nav{height:10px;width:10px;background-size:10px 50px}.kint-rich footer>ol{display:none;margin-left:32px}.kint-rich footer.kint-show>ol{display:block}.kint-rich a{color:#657b83;text-shadow:none;text-decoration:underline}.kint-rich a:hover{color:#586e75;border-bottom:1px dotted #586e75}.kint-rich ul{list-style:none;padding-left:15px}.kint-rich ul:not(.kint-tabs) li{border-left:1px dashed #93a1a1}.kint-rich ul:not(.kint-tabs) li>dl{border-left:none}.kint-rich ul.kint-tabs{margin:0 0 0 15px;padding-left:0;background:#fdf6e3;border:1px solid #93a1a1;border-top:0}.kint-rich ul.kint-tabs>li{background:#eee8d5;border:1px solid #93a1a1;cursor:pointer;display:inline-block;height:30px;margin:3px;padding:0 15px;vertical-align:top}.kint-rich ul.kint-tabs>li:hover,.kint-rich ul.kint-tabs>li.kint-active-tab:hover{border-color:#268bd2;color:#2aa198}.kint-rich ul.kint-tabs>li.kint-active-tab{background:#fdf6e3;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint-rich ul.kint-tabs>li:not(.kint-active-tab){line-height:25px}.kint-rich ul.kint-tabs li+li{margin-left:0}.kint-rich ul.kint-tab-contents>li{display:none}.kint-rich ul.kint-tab-contents>li.kint-show{display:block}.kint-rich dt:hover+dd>ul>li.kint-active-tab{border-color:#268bd2;color:#2aa198}.kint-rich dt>.kint-color-preview{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-left:10px;border:1px solid #93a1a1;background-color:#ccc;background-image:url('data:image/svg+xml;utf8,');background-size:100%}.kint-rich dt>.kint-color-preview:hover{border-color:#268bd2}.kint-rich dt>.kint-color-preview>div{width:100%;height:100%}.kint-rich table{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-rich table *{font-size:12px}.kint-rich table dt{background:none;padding:2.5px}.kint-rich table dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-rich table td,.kint-rich table th{border:1px solid #93a1a1;padding:2.5px;vertical-align:center}.kint-rich table th{cursor:alias}.kint-rich table td:first-child,.kint-rich table th{font-weight:bold;background:#eee8d5;color:#586e75}.kint-rich table td{background:#fdf6e3;white-space:pre}.kint-rich table td>dl{padding:0}.kint-rich table pre{border-top:0;border-right:0}.kint-rich table thead th:first-child{background:none;border:0}.kint-rich table tr:hover>td{box-shadow:0 0 1px 0 #268bd2 inset}.kint-rich table tr:hover var{color:#2aa198}.kint-rich table ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-rich pre.kint-source{margin-left:-1px}.kint-rich pre.kint-source[data-kint-filename]:before{display:block;content:attr(data-kint-filename);margin-bottom:5px;padding-bottom:5px;border-bottom:1px solid #eee8d5}.kint-rich pre.kint-source>div:before{display:inline-block;content:counter(kint-l);counter-increment:kint-l;border-right:1px solid #268bd2;padding-right:10px;margin-right:10px}.kint-rich pre.kint-source>div.kint-highlight{background:#eee8d5}.kint-rich .kint-microtime-lap{text-shadow:-1px 0 #268bd2,0 1px #268bd2,1px 0 #268bd2,0 -1px #268bd2;color:#fdf6e3;font-weight:bold}input.kint-note-input{width:100%}.kint-rich .kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-rich>dl>dt,.kint-rich ul.kint-tabs{box-shadow:4px 0 2px -3px #268bd2 inset}.kint-rich ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px} diff --git a/system/Validation/CreditCardRules.php b/system/Validation/CreditCardRules.php index 61f59d7b..8e17b75d 100644 --- a/system/Validation/CreditCardRules.php +++ b/system/Validation/CreditCardRules.php @@ -185,7 +185,7 @@ class CreditCardRules } // If empty, it's not a card type we recognize, or invalid type. - if (empty($info)) { + if ($info === null) { return false; } diff --git a/system/Validation/DotArrayFilter.php b/system/Validation/DotArrayFilter.php index 43cb1937..1ba9b92f 100644 --- a/system/Validation/DotArrayFilter.php +++ b/system/Validation/DotArrayFilter.php @@ -44,7 +44,7 @@ final class DotArrayFilter $segments ); - $result = array_merge_recursive($result, self::filter($segments, $array)); + $result = array_replace_recursive($result, self::filter($segments, $array)); } return $result; @@ -93,7 +93,7 @@ final class DotArrayFilter // If this is the last index, make sure to return it now, // and not try to recurse through things. - if (empty($indexes)) { + if ($indexes === []) { return [$currentIndex => $array[$currentIndex]]; } diff --git a/system/Validation/FormatRules.php b/system/Validation/FormatRules.php index 5089f5a0..88775d14 100644 --- a/system/Validation/FormatRules.php +++ b/system/Validation/FormatRules.php @@ -252,7 +252,7 @@ class FormatRules */ public function valid_ip(?string $ip = null, ?string $which = null): bool { - if (empty($ip)) { + if ($ip === null || $ip === '') { return false; } @@ -281,7 +281,7 @@ class FormatRules */ public function valid_url(?string $str = null): bool { - if (empty($str)) { + if ($str === null || $str === '') { return false; } @@ -305,7 +305,7 @@ class FormatRules */ public function valid_url_strict(?string $str = null, ?string $validSchemes = null): bool { - if (empty($str)) { + if ($str === null || $str === '' || $str === '0') { return false; } @@ -322,6 +322,8 @@ class FormatRules /** * Checks for a valid date and matches a given date format + * + * @param non-empty-string|null $format */ public function valid_date(?string $str = null, ?string $format = null): bool { @@ -329,7 +331,7 @@ class FormatRules return false; } - if (empty($format)) { + if ($format === null || $format === '') { return strtotime($str) !== false; } diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index b7c585f5..7a051f90 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -24,9 +24,10 @@ class Rules /** * The value does not match another field in $data. * - * @param array $data Other field/value pairs + * @param string|null $str + * @param array $data Other field/value pairs */ - public function differs(?string $str, string $field, array $data): bool + public function differs($str, string $field, array $data): bool { if (strpos($field, '.') !== false) { return $str !== dot_array_search($field, $data); @@ -104,7 +105,8 @@ class Rules ->limit(1); if ( - ! empty($whereField) && ! empty($whereValue) + $whereField !== null && $whereField !== '' + && $whereValue !== null && $whereValue !== '' && ! preg_match('/^\{(\w+)\}$/', $whereValue) ) { $row = $row->where($whereField, $whereValue); @@ -149,7 +151,8 @@ class Rules ->limit(1); if ( - ! empty($ignoreField) && ! empty($ignoreValue) + $ignoreField !== null && $ignoreField !== '' + && $ignoreValue !== null && $ignoreValue !== '' && ! preg_match('/^\{(\w+)\}$/', $ignoreValue) ) { $row = $row->where("{$ignoreField} !=", $ignoreValue); @@ -177,15 +180,16 @@ class Rules /** * Matches the value of another field in $data. * - * @param array $data Other field/value pairs + * @param string|null $str + * @param array $data Other field/value pairs */ - public function matches(?string $str, string $field, array $data): bool + public function matches($str, string $field, array $data): bool { if (strpos($field, '.') !== false) { return $str === dot_array_search($field, $data); } - return array_key_exists($field, $data) && $str === $data[$field]; + return isset($data[$field]) && $str === $data[$field]; } /** @@ -254,7 +258,7 @@ class Rules */ public function required_with($str = null, ?string $fields = null, array $data = []): bool { - if ($fields === null || empty($data)) { + if ($fields === null || $data === []) { throw new InvalidArgumentException('You must supply the parameters: fields, data.'); } @@ -274,14 +278,16 @@ class Rules foreach (explode(',', $fields) as $field) { if ( - (array_key_exists($field, $data) && ! empty($data[$field])) - || (strpos($field, '.') !== false && ! empty(dot_array_search($field, $data))) + (array_key_exists($field, $data) + && ! empty($data[$field])) // @phpstan-ignore-line Use empty() + || (strpos($field, '.') !== false + && ! empty(dot_array_search($field, $data))) // @phpstan-ignore-line Use empty() ) { $requiredFields[] = $field; } } - return empty($requiredFields); + return $requiredFields === []; } /** @@ -303,7 +309,7 @@ class Rules ?string $error = null, ?string $field = null ): bool { - if ($otherFields === null || empty($data)) { + if ($otherFields === null || $data === []) { throw new InvalidArgumentException('You must supply the parameters: otherFields, data.'); } @@ -321,7 +327,8 @@ class Rules foreach (explode(',', $otherFields) as $otherField) { if ( (strpos($otherField, '.') === false) - && (! array_key_exists($otherField, $data) || empty($data[$otherField])) + && (! array_key_exists($otherField, $data) + || empty($data[$otherField])) // @phpstan-ignore-line Use empty() ) { return false; } @@ -336,7 +343,7 @@ class Rules $fieldKey = $fieldSplitArray[1]; if (is_array($fieldData)) { - return ! empty(dot_array_search($otherField, $data)[$fieldKey]); + return ! empty(dot_array_search($otherField, $data)[$fieldKey]); // @phpstan-ignore-line Use empty() } $nowField = str_replace('*', $fieldKey, $otherField); $nowFieldVaule = dot_array_search($nowField, $data); diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index 0887eff2..3086ca5a 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -36,13 +36,26 @@ class Rules * @param array|bool|float|int|object|string|null $str * @param array $data Other field/value pairs */ - public function differs($str, string $field, array $data): bool - { - if (! is_string($str)) { + public function differs( + $str, + string $otherField, + array $data, + ?string $error = null, + ?string $field = null + ): bool { + if (strpos($otherField, '.') !== false) { + return $str !== dot_array_search($otherField, $data); + } + + if (! array_key_exists($field, $data)) { return false; } - return $this->nonStrictRules->differs($str, $field, $data); + if (! array_key_exists($otherField, $data)) { + return false; + } + + return $str !== ($data[$otherField] ?? null); } /** @@ -144,7 +157,8 @@ class Rules ->limit(1); if ( - ! empty($whereField) && ! empty($whereValue) + $whereField !== null && $whereField !== '' + && $whereValue !== null && $whereValue !== '' && ! preg_match('/^\{(\w+)\}$/', $whereValue) ) { $row = $row->where($whereField, $whereValue); @@ -203,7 +217,8 @@ class Rules ->limit(1); if ( - ! empty($ignoreField) && ! empty($ignoreValue) + $ignoreField !== null && $ignoreField !== '' + && $ignoreValue !== null && $ignoreValue !== '' && ! preg_match('/^\{(\w+)\}$/', $ignoreValue) ) { $row = $row->where("{$ignoreField} !=", $ignoreValue); @@ -254,9 +269,26 @@ class Rules * @param array|bool|float|int|object|string|null $str * @param array $data Other field/value pairs */ - public function matches($str, string $field, array $data): bool - { - return $this->nonStrictRules->matches($str, $field, $data); + public function matches( + $str, + string $otherField, + array $data, + ?string $error = null, + ?string $field = null + ): bool { + if (strpos($otherField, '.') !== false) { + return $str === dot_array_search($otherField, $data); + } + + if (! array_key_exists($field, $data)) { + return false; + } + + if (! array_key_exists($otherField, $data)) { + return false; + } + + return $str === ($data[$otherField] ?? null); } /** diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index ad95cde7..83bb361e 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Validation; use Closure; +use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\Validation\Exceptions\ValidationException; @@ -147,7 +148,7 @@ class Validation implements ValidationInterface // If no rules exist, we return false to ensure // the developer didn't forget to set the rules. - if (empty($this->rules)) { + if ($this->rules === []) { return false; } @@ -168,12 +169,14 @@ class Validation implements ValidationInterface } if (strpos($field, '*') !== false) { - $values = array_filter(array_flatten_with_dots($data), static fn ($key) => preg_match( - '/^' - . str_replace(['\.\*', '\*\.'], ['\..+', '.+\.'], preg_quote($field, '/')) - . '$/', - $key - ), ARRAY_FILTER_USE_KEY); + $flattenedArray = array_flatten_with_dots($data); + + $values = array_filter( + $flattenedArray, + static fn ($key) => preg_match(self::getRegex($field), $key), + ARRAY_FILTER_USE_KEY + ); + // if keys not found $values = $values ?: [$field => null]; } else { @@ -211,6 +214,20 @@ class Validation implements ValidationInterface return false; } + /** + * Returns regex pattern for key with dot array syntax. + */ + private static function getRegex(string $field): string + { + return '/\A' + . str_replace( + ['\.\*', '\*\.'], + ['\.[^.]+', '[^.]+\.'], + preg_quote($field, '/') + ) + . '\z/'; + } + /** * Runs the validation process, returning true or false determining whether * validation was successful or not. @@ -480,6 +497,10 @@ class Validation implements ValidationInterface if (strpos($request->getHeaderLine('Content-Type'), 'application/json') !== false) { $this->data = $request->getJSON(true); + if (! is_array($this->data)) { + throw HTTPException::forUnsupportedJSONFormat(); + } + return $this; } @@ -686,7 +707,7 @@ class Validation implements ValidationInterface */ protected function loadRuleSets() { - if (empty($this->ruleSetFiles)) { + if ($this->ruleSetFiles === [] || $this->ruleSetFiles === null) { throw ValidationException::forNoRuleSets(); } @@ -703,13 +724,15 @@ class Validation implements ValidationInterface * same format used with setRules(). Additionally, check * for {group}_errors for an array of custom error messages. * + * @param non-empty-string|null $group + * * @return array [rules, customErrors] * * @throws ValidationException */ public function loadRuleGroup(?string $group = null) { - if (empty($group)) { + if ($group === null || $group === '') { return []; } @@ -814,9 +837,7 @@ class Validation implements ValidationInterface */ public function hasError(string $field): bool { - $pattern = '/^' . str_replace('\.\*', '\..+', preg_quote($field, '/')) . '$/'; - - return (bool) preg_grep($pattern, array_keys($this->getErrors())); + return (bool) preg_grep(self::getRegex($field), array_keys($this->getErrors())); } /** @@ -829,10 +850,11 @@ class Validation implements ValidationInterface $field = array_key_first($this->rules); } - $errors = array_filter($this->getErrors(), static fn ($key) => preg_match( - '/^' . str_replace(['\.\*', '\*\.'], ['\..+', '.+\.'], preg_quote($field, '/')) . '$/', - $key - ), ARRAY_FILTER_USE_KEY); + $errors = array_filter( + $this->getErrors(), + static fn ($key) => preg_match(self::getRegex($field), $key), + ARRAY_FILTER_USE_KEY + ); return $errors === [] ? '' : implode("\n", $errors); } @@ -868,7 +890,8 @@ class Validation implements ValidationInterface /** * Attempts to find the appropriate error message * - * @param string|null $value The value that caused the validation to fail. + * @param non-empty-string|null $label + * @param string|null $value The value that caused the validation to fail. */ protected function getErrorMessage( string $rule, @@ -892,10 +915,10 @@ class Validation implements ValidationInterface $message = lang('Validation.' . $rule); } - $message = str_replace('{field}', empty($label) ? $field : lang($label), $message); + $message = str_replace('{field}', ($label === null || $label === '') ? $field : lang($label), $message); $message = str_replace( '{param}', - empty($this->rules[$param]['label']) ? $param : lang($this->rules[$param]['label']), + (! isset($this->rules[$param]['label'])) ? $param : lang($this->rules[$param]['label']), $message ); diff --git a/system/Validation/ValidationInterface.php b/system/Validation/ValidationInterface.php index 78c38468..c289afc4 100644 --- a/system/Validation/ValidationInterface.php +++ b/system/Validation/ValidationInterface.php @@ -134,6 +134,8 @@ interface ValidationInterface * same format used with setRules(). Additionally, check * for {group}_errors for an array of custom error messages. * + * @param non-empty-string|null $group + * * @return array */ public function loadRuleGroup(?string $group = null); diff --git a/system/Validation/Views/list.php b/system/Validation/Views/list.php index cc95156c..3669f038 100644 --- a/system/Validation/Views/list.php +++ b/system/Validation/Views/list.php @@ -1,4 +1,4 @@ - +