diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 81ddccc3..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 CodeIgniter 4 web framework - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 03626c76..275f0153 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ This repository holds the distributable version of the framework, including the user guide. It has been built from the [development repository](https://github.com/codeigniter4/CodeIgniter4). -**This is pre-release code and should not be used in production sites.** - More information about the plans for version 4 can be found in [the announcement](http://forum.codeigniter.com/thread-62615.html) on the forums. The user guide corresponding to this version of the framework can be found diff --git a/app/Config/Kint.php b/app/Config/Kint.php index 9562447e..09db83dd 100644 --- a/app/Config/Kint.php +++ b/app/Config/Kint.php @@ -1,7 +1,7 @@ <?php namespace Config; -use Kint\Renderer\Renderer; use CodeIgniter\Config\BaseConfig; +use Kint\Renderer\Renderer; class Kint extends BaseConfig { diff --git a/app/Config/Logger.php b/app/Config/Logger.php index 64e4aa19..93d38f6f 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -34,15 +34,6 @@ class Logger extends BaseConfig */ public $threshold = 3; - /* - |-------------------------------------------------------------------------- - | Error Logging Directory Path - |-------------------------------------------------------------------------- - | By default, logs are written to WRITEPATH . 'logs/' - | Specify a different destination here, if desired. - */ - public $path = ''; - /* |-------------------------------------------------------------------------- | Date Format for Logs @@ -116,6 +107,14 @@ class Logger extends BaseConfig * integer notation (i.e. 0700, 0644, etc.) */ 'filePermissions' => 0644, + + /* + * Logging Directory Path + * + * By default, logs are written to WRITEPATH . 'logs/' + * Specify a different destination here, if desired. + */ + 'path' => '', ], /** diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php index a570ef54..41014d40 100644 --- a/app/Config/Mimes.php +++ b/app/Config/Mimes.php @@ -516,11 +516,7 @@ public static function guessExtensionFromType(string $type, ?string $proposed_ex foreach (static::$mimes as $ext => $types) { - if (is_string($types) && $types === $type) - { - return $ext; - } - else if (is_array($types) && in_array($type, $types)) + if ((is_string($types) && $types === $type) || (is_array($types) && in_array($type, $types))) { return $ext; } diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 48327365..a2a9654b 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -1,7 +1,7 @@ <?php namespace Config; // Create a new instance of our RouteCollection class. -$routes = Services::routes(true); +$routes = Services::routes(); // Load the system's routing file first, so that the app and ENVIRONMENT // can override as needed. diff --git a/app/Config/Services.php b/app/Config/Services.php index 79a2afba..fb85a734 100644 --- a/app/Config/Services.php +++ b/app/Config/Services.php @@ -1,7 +1,6 @@ <?php namespace Config; use CodeIgniter\Config\Services as CoreServices; -use CodeIgniter\Config\BaseConfig; require_once SYSTEMPATH . 'Config/Services.php'; diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php index 7052e53b..09fddcbd 100644 --- a/app/Views/errors/html/error_exception.php +++ b/app/Views/errors/html/error_exception.php @@ -132,7 +132,7 @@ <?php if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) { continue; -} ?> + } ?> <h3>$<?= $var ?></h3> @@ -235,7 +235,7 @@ <?php if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) { continue; -} ?> + } ?> <?php $empty = false; ?> @@ -287,15 +287,15 @@ </tr> </thead> <tbody> - <?php foreach ($headers as $name => $value) : ?> + <?php foreach ($headers as $value) : ?> <?php if (empty($value)) { continue; -} ?> + } ?> <?php if (! is_array($value)) { $value = [$value]; -} ?> + } ?> <?php foreach ($value as $h) : ?> <tr> <td><?= esc($h->getName(), 'html') ?></td> diff --git a/composer.json b/composer.json index d8adb485..a8045d37 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "ext-curl": "*", "ext-intl": "*", "ext-json": "*", + "ext-mbstring": "*", "kint-php/kint": "^3.3", "psr/log": "^1.1", "laminas/laminas-escaper": "^2.6" diff --git a/contributing.md b/contributing.md deleted file mode 100644 index f0cf482f..00000000 --- a/contributing.md +++ /dev/null @@ -1,94 +0,0 @@ -# Contributing to CodeIgniter4 - - -## Contributions - -We expect all contributions to conform to our [style guide](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/styleguide.rst), be commented (inside the PHP source files), -be documented (in the [user guide](https://codeigniter4.github.io/userguide/)), and unit tested (in the [test folder](https://github.com/codeigniter4/CodeIgniter4/tree/develop/tests)). -There is a [Contributing to CodeIgniter](./contributing/README.rst) section in the repository which describes the contribution process; this page is an overview. - -Note, we expect all code changes or bug-fixes to be accompanied by one or more tests added to our test suite to prove the code works. If pull requests are not accompanied by relevant tests, they will likely be closed. Since we are a team of volunteers, we don't have any more time to work on the framework than you do. Please make it as painless for your contributions to be included as possible. If you need help with getting tests running on your local machines, ask for help on the forums. We would be happy to help out. - -The [Open Source Guide](https://opensource.guide/) is a good first read for those new to contributing to open source! -## Issues - -Issues are a quick way to point out a bug. If you find a bug or documentation error in CodeIgniter then please make sure that: - -1. There is not already an open [Issue](https://github.com/codeigniter4/CodeIgniter4/issues) -2. The Issue has not already been fixed (check the develop branch or look for [closed Issues](https://github.com/codeigniter4/CodeIgniter4/issues?q=is%3Aissue+is%3Aclosed)) -3. It's not something really obvious that you can fix yourself - -Reporting Issues is helpful, but an even [better approach](./contributing/workflow.rst) is to send a [Pull Request](https://help.github.com/en/articles/creating-a-pull-request), which is done by [Forking](https://help.github.com/en/articles/fork-a-repo) the main repository and making a [Commit](https://help.github.com/en/desktop/contributing-to-projects/committing-and-reviewing-changes-to-your-project) to your own copy of the project. This will require you to use the version control system called [Git](https://git-scm.com/). - -## Guidelines - -Before we look into how to contribute to CodeIgniter4, here are some guidelines. If your Pull Requests fail -to pass these guidelines, they will be declined, and you will need to re-submit -when you’ve made the changes. This might sound a bit tough, but it is required -for us to maintain the quality of the codebase. - -### PHP Style - -All code must meet the [Style Guide](./contributing/styleguide.rst). -This makes certain that all submitted code is of the same format as the existing code and ensures that the codebase will be as readable as possible. - -### Documentation - -If you change anything that requires a change to documentation, then you will need to add to the documentation. New classes, methods, parameters, changing default values, etc. are all changes that require a change to documentation. Also, the [changelog](https://codeigniter4.github.io/CodeIgniter4/changelogs/index.html) must be updated for every change, and [PHPDoc](https://github.com/codeigniter4/CodeIgniter4/blob/develop/phpdoc.dist.xml) blocks must be maintained. - -### Compatibility - -CodeIgniter4 requires [PHP 7.2](https://php.net/releases/7_2_0.php). - -### Branching - -CodeIgniter4 uses the [Git-Flow](http://nvie.com/posts/a-successful-git-branching-model/) branching model which requires all -Pull Requests to be sent to the "develop" branch; this is where the next planned version will be developed. -The "master" branch will always contain the latest stable version and is kept clean so a "hotfix" (e.g. an -emergency security patch) can be applied to the "master" branch to create a new version, without worrying -about other features holding it up. For this reason, all commits need to be made to the "develop" branch, -and any sent to the "master" branch will be closed automatically. If you have multiple changes to submit, -please place all changes into their own branch on your fork. - -**One thing at a time:** A pull request should only contain one change. That does not mean only one commit, -but one change - however many commits it took. The reason for this is that if you change X and Y, -but send a pull request for both at the same time, we might really want X but disagree with Y, -meaning we cannot merge the request. Using the Git-Flow branching model you can create new -branches for both of these features and send two requests. - -A reminder: **please use separate branches for each of your PRs** - it will make it easier for you to keep changes separate from -each other and from whatever else you are doing with your repository! - -### Signing - -You must [GPG-sign](./contributing/signing.rst) your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open-source project. This is *not* just a "signed-off-by" commit, but instead, a digitally signed one. - -## How-to Guide - -The best way to contribute is to fork the CodeIgniter4 repository, and "clone" that to your development area. That sounds like some jargon, but "forking" on GitHub means "making a copy of that repo to your account" and "cloning" means "copying that code to your environment so you can work on it". - -1. Set up Git ([Windows](https://git-scm.com/download/win), [Mac](https://git-scm.com/download/mac), & [Linux](https://git-scm.com/download/linux)). -2. Go to the [CodeIgniter4 repository](https://github.com/codeigniter4/CodeIgniter4). -3. [Fork](https://help.github.com/en/articles/fork-a-repo) it (to your Github account). -4. [Clone](https://help.github.com/en/articles/cloning-a-repository) your CodeIgniter repository: `git@github.com:\<your-name>/CodeIgniter4.git` -5. Create a new [branch](https://help.github.com/en/articles/about-branches) in your project for each set of changes you want to make. -6. Fix existing bugs on the [Issue tracker](https://github.com/codeigniter4/CodeIgniter4/issues) after confirming that no one else is working on them. -7. [Commit](https://help.github.com/en/desktop/contributing-to-projects/committing-and-reviewing-changes-to-your-project) the changed files in your contribution branch. -8. [Push](https://help.github.com/en/articles/pushing-to-a-remote) your contribution branch to your fork. -9. Send a [pull request](http://help.github.com/send-pull-requests/). - -The codebase maintainers will now be alerted to the submission and someone from the team will respond. If your change fails to meet the guidelines, it will be rejected or feedback will be provided to help you improve it. - -Once the maintainer handling your pull request is satisfied with it they will approve the pull request and merge it into the "develop" branch; your patch will now be part of the next release! - -### Keeping your fork up-to-date - -Unlike systems like Subversion, Git can have multiple remotes. A remote is the name for the URL of a Git repository. By default, your fork will have a remote named "origin", which points to your fork, but you can add another remote named "codeigniter", which points to `git://github.com/codeigniter4/CodeIgniter4.git`. This is a read-only remote, but you can pull from this develop branch to update your own. - -If you are using the command-line, you can do the following to update your fork to the latest changes: - -1. `git remote add codeigniter git://github.com/codeigniter4/CodeIgniter4.git` -2. `git pull codeigniter develop` -3. `git push origin develop` - -Your fork is now up to date. This should be done regularly and, at the least, before you submit a pull request. diff --git a/public/index.php b/public/index.php index 5b9e912f..3eaa592a 100644 --- a/public/index.php +++ b/public/index.php @@ -13,7 +13,7 @@ // Location of the Paths config file. // This is the line that might need to be changed, depending on your folder structure. -$pathsPath = FCPATH . '../app/Config/Paths.php'; +$pathsPath = realpath(FCPATH . '../app/Config/Paths.php'); // ^^^ Change this if you move your application folder /* diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index f16b2846..49248eb1 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -39,8 +39,8 @@ namespace CodeIgniter\API; -use Config\Format; use CodeIgniter\HTTP\Response; +use Config\Format; /** * Response trait. @@ -56,7 +56,6 @@ */ trait ResponseTrait { - /** * Allows child classes to override the * status code that is used in their API. @@ -66,6 +65,7 @@ trait ResponseTrait protected $codes = [ 'created' => 201, 'deleted' => 200, + 'updated' => 200, 'no_content' => 204, 'invalid_request' => 400, 'unsupported_response_type' => 400, @@ -93,6 +93,15 @@ trait ResponseTrait 'not_implemented' => 501, ]; + /** + * How to format the response data. + * Either 'json' or 'xml'. If blank will be + * determine through content negotiation. + * + * @var string + */ + protected $format = 'json'; + //-------------------------------------------------------------------- /** @@ -190,6 +199,19 @@ public function respondDeleted($data = null, string $message = '') return $this->respond($data, $this->codes['deleted'], $message); } + /** + * Used after a resource has been successfully updated. + * + * @param mixed $data Data. + * @param string $message Message. + * + * @return mixed + */ + public function respondUpdated($data = null, string $message = '') + { + return $this->respond($data, $this->codes['updated'], $message); + } + //-------------------------------------------------------------------- /** @@ -364,9 +386,14 @@ protected function format($data = null) return $data; } - // Determine correct response type through content negotiation $config = new Format(); - $format = $this->request->negotiate('media', $config->supportedResponseFormats, false); + $format = "application/$this->format"; + + // Determine correct response type through content negotiation if not explicitly declared + if (empty($this->format) || ! in_array($this->format, ['json', 'xml'])) + { + $format = $this->request->negotiate('media', $config->supportedResponseFormats, false); + } $this->response->setContentType($format); @@ -387,4 +414,17 @@ protected function format($data = null) return $this->formatter->format($data); } + /** + * Sets the format the response should be in. + * + * @param string $format + * + * @return $this + */ + public function setResponseFormat(string $format = null) + { + $this->format = strtolower($format); + + return $this; + } } diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 46631074..fa5c9a2f 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -392,7 +392,7 @@ public function sanitizeFilename(string $filename): string // be a path. // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_278 // Modified to allow backslash and colons for on Windows machines. - $filename = preg_replace('/[^a-zA-Z0-9\s\/\-\_\.\:\\\\]/', '', $filename); + $filename = preg_replace('/[^0-9\p{L}\s\/\-\_\.\:\\\\]/u', '', $filename); // Clean up our filename edges. $filename = trim($filename, '.-_'); diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 0b78e9c0..5aa217e1 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -353,7 +353,11 @@ public function findQualifiedNameFromPath(string $path) // Remove the file extension (.php) $className = mb_substr($className, 0, -4); - return $className; + // Check if this exists + if (class_exists($className)) + { + return $className; + } } } diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index e60036c7..476c08d1 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -437,7 +437,7 @@ public static function newLine(int $num = 1) // Do it once or more, write with empty string gives us a new line for ($i = 0; $i < $num; $i ++) { - static::write(''); + static::write(); } } @@ -504,9 +504,7 @@ public static function color(string $text, string $foreground, string $backgroun $string .= "\033[4m"; } - $string .= $text . "\033[0m"; - - return $string; + return $string . ($text . "\033[0m"); } //-------------------------------------------------------------------- @@ -702,24 +700,17 @@ public static function wrap(string $string = null, int $max = 0, int $pad_left = */ protected static function parseCommandLine() { - $optionsFound = false; - // start picking segments off from #1, ignoring the invoking program for ($i = 1; $i < $_SERVER['argc']; $i ++) { // If there's no '-' at the beginning of the argument // then add it to our segments. - if (! $optionsFound && mb_strpos($_SERVER['argv'][$i], '-') === false) + if (mb_strpos($_SERVER['argv'][$i], '-') === false) { static::$segments[] = $_SERVER['argv'][$i]; continue; } - // We set $optionsFound here so that we know to - // skip the next argument since it's likely the - // value belonging to this option. - $optionsFound = true; - $arg = str_replace('-', '', $_SERVER['argv'][$i]); $value = null; @@ -731,10 +722,6 @@ protected static function parseCommandLine() } static::$options[$arg] = $value; - - // Reset $optionsFound so it can collect segments - // past any options. - $optionsFound = false; } } @@ -958,7 +945,7 @@ public static function table(array $tbody, array $thead = []) } } - fwrite(STDOUT, $table); + static::write($table); } //-------------------------------------------------------------------- diff --git a/system/CLI/CommandRunner.php b/system/CLI/CommandRunner.php index b1e6103e..2e1a8e11 100644 --- a/system/CLI/CommandRunner.php +++ b/system/CLI/CommandRunner.php @@ -40,8 +40,8 @@ namespace CodeIgniter\CLI; -use Config\Services; use CodeIgniter\Controller; +use Config\Services; /** * Command runner @@ -104,7 +104,7 @@ public function index(array $params) if (is_null($command)) { - $command = 'help'; + $command = 'list'; } return $this->runCommand($command, $params); diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index 737db09e..86e70903 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -154,7 +154,7 @@ public function delete(string $key) { $key = $this->prefix . $key; - return is_file($this->path . $key) ? unlink($this->path . $key) : false; + return is_file($this->path . $key) && unlink($this->path . $key); } //-------------------------------------------------------------------- diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 26a68038..c708a279 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -39,20 +39,20 @@ namespace CodeIgniter; use Closure; +use CodeIgniter\Debug\Timer; +use CodeIgniter\Events\Events; +use CodeIgniter\Exceptions\PageNotFoundException; +use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\Request; +use CodeIgniter\HTTP\Response; use CodeIgniter\HTTP\ResponseInterface; -use Config\Services; -use Config\Cache; use CodeIgniter\HTTP\URI; -use CodeIgniter\Debug\Timer; -use CodeIgniter\Events\Events; -use CodeIgniter\HTTP\Response; -use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\Router\Exceptions\RedirectException; use CodeIgniter\Router\RouteCollectionInterface; -use CodeIgniter\Exceptions\PageNotFoundException; +use Config\Cache; +use Config\Services; use Exception; /** @@ -66,7 +66,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - const CI_VERSION = '4.0.2'; + const CI_VERSION = '4.0.3'; /** * App startup time. @@ -194,7 +194,9 @@ public function initialize() if (! CI_DEBUG) { + // @codeCoverageIgnoreStart \Kint::$enabled_mode = false; + // @codeCoverageIgnoreEnd } } @@ -496,9 +498,11 @@ protected function bootstrapEnvironment() } else { + // @codeCoverageIgnoreStart header('HTTP/1.1 503 Service Unavailable.', true, 503); echo 'The application environment is not set correctly.'; exit(1); // EXIT_ERROR + // @codeCoverageIgnoreEnd } } @@ -550,9 +554,11 @@ protected function getRequestObject() return; } - if (is_cli() && ! (ENVIRONMENT === 'testing')) + if (is_cli() && ENVIRONMENT !== 'testing') { + // @codeCoverageIgnoreStart $this->request = Services::clirequest($this->config); + // @codeCoverageIgnoreEnd } else { @@ -747,9 +753,7 @@ public function displayPerformanceMetrics(string $output): string { $this->totalTime = $this->benchmark->getElapsedTime('total_execution'); - $output = str_replace('{elapsed_time}', $this->totalTime, $output); - - return $output; + return str_replace('{elapsed_time}', $this->totalTime, $output); } //-------------------------------------------------------------------- @@ -958,10 +962,12 @@ protected function display404errors(PageNotFoundException $e) if (ENVIRONMENT !== 'testing') { + // @codeCoverageIgnoreStart if (ob_get_level() > 0) { ob_end_flush(); } + // @codeCoverageIgnoreEnd } else { @@ -972,7 +978,7 @@ protected function display404errors(PageNotFoundException $e) } } - throw PageNotFoundException::forPageNotFound($e->getMessage()); + throw PageNotFoundException::forPageNotFound(ENVIRONMENT !== 'production' || is_cli() ? $e->getMessage() : ''); } //-------------------------------------------------------------------- @@ -1110,7 +1116,9 @@ protected function sendResponse() */ protected function callExit($code) { + // @codeCoverageIgnoreStart exit($code); + // @codeCoverageIgnoreEnd } //-------------------------------------------------------------------- diff --git a/system/Commands/Database/CreateMigration.php b/system/Commands/Database/CreateMigration.php index 19d87a44..3983d677 100644 --- a/system/Commands/Database/CreateMigration.php +++ b/system/Commands/Database/CreateMigration.php @@ -40,8 +40,7 @@ use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; -use Config\Autoload; -use Config\Migrations; +use Config\Services; /** * Creates a new migration file. @@ -124,15 +123,14 @@ public function run(array $params = []) if (! empty($ns)) { - // Get all namespaces from PSR4 paths. - $config = new Autoload(); - $namespaces = $config->psr4; + // Get all namespaces + $namespaces = Services::autoloader()->getNamespace(); foreach ($namespaces as $namespace => $path) { if ($namespace === $ns) { - $homepath = realpath($path); + $homepath = realpath(reset($path)); break; } } @@ -178,7 +176,7 @@ public function down() helper('filesystem'); if (! write_file($path, $template)) { - CLI::error(lang('Migrations.writeError')); + CLI::error(lang('Migrations.writeError', [$path])); return; } diff --git a/system/Commands/Database/MigrateRefresh.php b/system/Commands/Database/MigrateRefresh.php index e0ec6696..bf6b4f8b 100644 --- a/system/Commands/Database/MigrateRefresh.php +++ b/system/Commands/Database/MigrateRefresh.php @@ -40,6 +40,7 @@ namespace CodeIgniter\Commands\Database; use CodeIgniter\CLI\BaseCommand; +use CodeIgniter\CLI\CLI; /** * Does a rollback followed by a latest to refresh the current state @@ -95,6 +96,7 @@ class MigrateRefresh extends BaseCommand '-n' => 'Set migration namespace', '-g' => 'Set database group', '-all' => 'Set latest for all namespace, will ignore (-n) option', + '-f' => 'Force command - this option allows you to bypass the confirmation question when running this command in a production environment', ]; /** @@ -105,7 +107,20 @@ class MigrateRefresh extends BaseCommand */ public function run(array $params = []) { - $this->call('migrate:rollback', ['-b' => 0]); + $params = ['-b' => 0]; + + if (ENVIRONMENT === 'production') + { + $force = $params['-f'] ?? CLI::getOption('f'); + if (is_null($force) && CLI::prompt(lang('Migrations.refreshConfirm'), ['y', 'n']) === 'n') + { + return; + } + + $params['-f'] = ''; + } + + $this->call('migrate:rollback', $params); $this->call('migrate'); } diff --git a/system/Commands/Database/MigrateRollback.php b/system/Commands/Database/MigrateRollback.php index ba259897..b2dca1c8 100644 --- a/system/Commands/Database/MigrateRollback.php +++ b/system/Commands/Database/MigrateRollback.php @@ -42,7 +42,6 @@ use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; use Config\Services; -use Config\Autoload; /** * Runs all of the migrations in reverse order, until they have @@ -97,6 +96,7 @@ class MigrateRollback extends BaseCommand protected $options = [ '-b' => 'Specify a batch to roll back to; e.g. "3" to return to batch #3 or "-2" to roll back twice', '-g' => 'Set database group', + '-f' => 'Force command - this option allows you to bypass the confirmation question when running this command in a production environment', ]; /** @@ -107,6 +107,15 @@ class MigrateRollback extends BaseCommand */ public function run(array $params = []) { + if (ENVIRONMENT === 'production') + { + $force = $params['-f'] ?? CLI::getOption('f'); + if (is_null($force) && CLI::prompt(lang('Migrations.rollBackConfirm'), ['y', 'n']) === 'n') + { + return; + } + } + $runner = Services::migrations(); $group = $params['-g'] ?? CLI::getOption('g'); diff --git a/system/Commands/Database/MigrateStatus.php b/system/Commands/Database/MigrateStatus.php index 8e714369..94ad3ea9 100644 --- a/system/Commands/Database/MigrateStatus.php +++ b/system/Commands/Database/MigrateStatus.php @@ -42,7 +42,6 @@ use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; use Config\Services; -use Config\Autoload; /** * Displays a list of all migrations and whether they've been run or not. @@ -106,6 +105,10 @@ class MigrateStatus extends BaseCommand 'CodeIgniter', 'Config', 'Tests\Support', + 'Kint', + 'Laminas\ZendFrameworkBridge', + 'Laminas\Escaper', + 'Psr\Log', ]; /** @@ -124,9 +127,11 @@ public function run(array $params = []) $runner->setGroup($group); } - // Get all namespaces from PSR4 paths. - $config = new Autoload(); - $namespaces = $config->psr4; + // Get all namespaces + $namespaces = Services::autoloader()->getNamespace(); + + // Determines whether any migrations were found + $found = false; // Loop for all $namespaces foreach ($namespaces as $namespace => $path) @@ -138,16 +143,17 @@ public function run(array $params = []) $runner->setNamespace($namespace); $migrations = $runner->findMigrations(); - $history = $runner->getHistory(); - - CLI::write($namespace); if (empty($migrations)) { - CLI::error(lang('Migrations.noneFound')); continue; } + $found = true; + $history = $runner->getHistory(); + + CLI::write($namespace); + ksort($migrations); $max = 0; @@ -176,6 +182,11 @@ public function run(array $params = []) CLI::write(str_pad(' ' . $migration->name, $max + 6) . ($date ? $date : '---')); } } + + if (! $found) + { + CLI::error(lang('Migrations.noneFound')); + } } } diff --git a/system/Commands/ListCommands.php b/system/Commands/ListCommands.php index bae8e73d..c20fe322 100644 --- a/system/Commands/ListCommands.php +++ b/system/Commands/ListCommands.php @@ -188,9 +188,8 @@ protected function padTitle(string $item, int $max, int $extra = 2, int $indent $max += $extra + $indent; $item = str_repeat(' ', $indent) . $item; - $item = str_pad($item, $max); - return $item; + return str_pad($item, $max); } //-------------------------------------------------------------------- diff --git a/system/Commands/Server/Serve.php b/system/Commands/Server/Serve.php index 9fb68fe8..9e658629 100644 --- a/system/Commands/Server/Serve.php +++ b/system/Commands/Server/Serve.php @@ -97,14 +97,14 @@ class Serve extends BaseCommand /** * The current port offset. * - * @var int + * @var integer */ protected $portOffset = 0; /** * The max number of ports to attempt to serve from * - * @var int + * @var integer */ protected $tries = 10; @@ -131,12 +131,14 @@ public function run(array $params) // Valid PHP Version? if (phpversion() < $this->minPHPVersion) { + // @codeCoverageIgnoreStart die('Your PHP version must be ' . $this->minPHPVersion . ' or higher to run CodeIgniter. Current version: ' . phpversion()); + // @codeCoverageIgnoreEnd } // Collect any user-supplied options and apply them. - $php = CLI::getOption('php') ?? PHP_BINARY; + $php = escapeshellarg(CLI::getOption('php') ?? PHP_BINARY); $host = CLI::getOption('host') ?? 'localhost'; $port = (int) (CLI::getOption('port') ?? '8080') + $this->portOffset; @@ -155,7 +157,8 @@ public function run(array $params) // to ensure our environment is set and it simulates basic mod_rewrite. passthru($php . ' -S ' . $host . ':' . $port . ' -t ' . $docroot . ' ' . $rewrite, $status); - if ($status && $this->portOffset < $this->tries) { + if ($status && $this->portOffset < $this->tries) + { $this->portOffset += 1; $this->run($params); diff --git a/system/Commands/Sessions/CreateMigration.php b/system/Commands/Sessions/CreateMigration.php index 15c7476a..ca26bfaa 100644 --- a/system/Commands/Sessions/CreateMigration.php +++ b/system/Commands/Sessions/CreateMigration.php @@ -126,7 +126,7 @@ public function run(array $params = []) helper('filesystem'); if (! write_file($path, $template)) { - CLI::error(lang('Migrations.migWriteError')); + CLI::error(lang('Migrations.writeError', [$path])); return; } diff --git a/system/Common.php b/system/Common.php index a14bc66e..2e3fcd7d 100644 --- a/system/Common.php +++ b/system/Common.php @@ -37,19 +37,20 @@ * @filesource */ -use Config\App; -use Config\Logger; -use Config\Database; -use Config\Services; -use CodeIgniter\HTTP\URI; -use Laminas\Escaper\Escaper; use CodeIgniter\Config\Config; -use CodeIgniter\Test\TestLogger; +use CodeIgniter\Database\ConnectionInterface; +use CodeIgniter\Files\Exceptions\FileNotFoundException; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; -use CodeIgniter\Database\ConnectionInterface; -use CodeIgniter\Files\Exceptions\FileNotFoundException; +use CodeIgniter\HTTP\URI; +use CodeIgniter\Test\TestLogger; +use Config\App; +use Config\Database; +use Config\Logger; +use Config\Services; +use Config\View; +use Laminas\Escaper\Escaper; /** * Common Functions @@ -246,9 +247,11 @@ function db_connect($db = null, bool $getShared = true) */ function dd(...$vars) { + // @codeCoverageIgnoreStart Kint::$aliases[] = 'dd'; Kint::dump(...$vars); exit; + // @codeCoverageIgnoreEnd } } @@ -319,7 +322,7 @@ function esc($data, string $context = 'html', string $encoding = null) { if (is_array($data)) { - foreach ($data as $key => &$value) + foreach ($data as &$value) { $value = esc($value, $context); } @@ -384,10 +387,7 @@ function esc($data, string $context = 'html', string $encoding = null) * @param RequestInterface $request * @param ResponseInterface $response * - * Not testable, as it will exit! - * - * @throws \CodeIgniter\HTTP\Exceptions\HTTPException - * @codeCoverageIgnore + * @throws \CodeIgniter\HTTP\Exceptions\HTTPException */ function force_https(int $duration = 31536000, RequestInterface $request = null, ResponseInterface $response = null) { @@ -400,25 +400,33 @@ function force_https(int $duration = 31536000, RequestInterface $request = null, $response = Services::response(null, true); } - if (is_cli() || $request->isSecure()) + if (ENVIRONMENT !== 'testing' && (is_cli() || $request->isSecure())) { + // @codeCoverageIgnoreStart return; + // @codeCoverageIgnoreEnd } - // If the session library is loaded, we should regenerate + // If the session status is active, we should regenerate // the session ID for safety sake. - if (class_exists('Session', false)) + if (ENVIRONMENT !== 'testing' && session_status() === PHP_SESSION_ACTIVE) { + // @codeCoverageIgnoreStart Services::session(null, true) ->regenerate(); + // @codeCoverageIgnoreEnd } - $uri = $request->uri; - $uri->setScheme('https'); + $baseURL = config(App::class)->baseURL; + + if (strpos($baseURL, 'http://') === 0) + { + $baseURL = (string) substr($baseURL, strlen('http://')); + } $uri = URI::createURIString( - $uri->getScheme(), $uri->getAuthority(true), $uri->getPath(), // Absolute URIs should use a "/" for an empty path - $uri->getQuery(), $uri->getFragment() + 'https', $baseURL, $request->uri->getPath(), // Absolute URIs should use a "/" for an empty path + $request->uri->getQuery(), $request->uri->getFragment() ); // Set an HSTS header @@ -426,7 +434,12 @@ function force_https(int $duration = 31536000, RequestInterface $request = null, $response->redirect($uri); $response->sendHeaders(); - exit(); + if (ENVIRONMENT !== 'testing') + { + // @codeCoverageIgnoreStart + exit(); + // @codeCoverageIgnoreEnd + } } } @@ -747,7 +760,9 @@ function old(string $key, $default = null, $escape = 'html') // Ensure the session is loaded if (session_status() === PHP_SESSION_NONE && ENVIRONMENT !== 'testing') { + // @codeCoverageIgnoreStart session(); + // @codeCoverageIgnoreEnd } $request = Services::request(); @@ -1060,8 +1075,9 @@ function view(string $name, array $data = [], array $options = []): string */ $renderer = Services::renderer(); - $saveData = true; - if (array_key_exists('saveData', $options) && $options['saveData'] === true) + $saveData = config(View::class)->saveData; + + if (array_key_exists('saveData', $options)) { $saveData = (bool) $options['saveData']; unset($options['saveData']); diff --git a/system/ComposerScripts.php b/system/ComposerScripts.php index d53108f4..6ebb1227 100644 --- a/system/ComposerScripts.php +++ b/system/ComposerScripts.php @@ -90,7 +90,9 @@ protected static function moveFile(string $source, string $destination): bool if (empty($source)) { + // @codeCoverageIgnoreStart die('Cannot move file. Source path invalid.'); + // @codeCoverageIgnoreEnd } if (! is_file($source)) @@ -203,7 +205,9 @@ public static function moveEscaper() { if (! static::moveFile($source, $dest)) { + // @codeCoverageIgnoreStart die('Error moving: ' . $source); + // @codeCoverageIgnoreEnd } } } diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index 9a0100f1..ceec8baa 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -164,16 +164,12 @@ protected function getEnvValue(string $property, string $prefix, string $shortPr { case array_key_exists("{$shortPrefix}.{$property}", $_ENV): return $_ENV["{$shortPrefix}.{$property}"]; - break; case array_key_exists("{$shortPrefix}.{$property}", $_SERVER): return $_SERVER["{$shortPrefix}.{$property}"]; - break; case array_key_exists("{$prefix}.{$property}", $_ENV): return $_ENV["{$prefix}.{$property}"]; - break; case array_key_exists("{$prefix}.{$property}", $_SERVER): return $_SERVER["{$prefix}.{$property}"]; - break; default: $value = getenv($property); return $value === false ? null : $value; diff --git a/system/Config/DotEnv.php b/system/Config/DotEnv.php index 541334a4..4220d77f 100644 --- a/system/Config/DotEnv.php +++ b/system/Config/DotEnv.php @@ -128,7 +128,7 @@ public function parse(): ?array if (strpos($line, '=') !== false) { list($name, $value) = $this->normaliseVariable($line); - $vars[$name] = $value; + $vars[$name] = $value; } } @@ -314,10 +314,8 @@ protected function getVariable(string $name) { case array_key_exists($name, $_ENV): return $_ENV[$name]; - break; case array_key_exists($name, $_SERVER): return $_SERVER[$name]; - break; default: $value = getenv($name); diff --git a/system/Config/Services.php b/system/Config/Services.php index 6f166a64..afb65123 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -39,6 +39,8 @@ namespace CodeIgniter\Config; use CodeIgniter\Cache\CacheFactory; +use CodeIgniter\Database\ConnectionInterface; +use CodeIgniter\Database\MigrationRunner; use CodeIgniter\Debug\Exceptions; use CodeIgniter\Debug\Iterator; use CodeIgniter\Debug\Timer; @@ -70,10 +72,8 @@ use CodeIgniter\Validation\Validation; use CodeIgniter\View\Cell; use CodeIgniter\View\Parser; -use Config\App; -use CodeIgniter\Database\ConnectionInterface; -use CodeIgniter\Database\MigrationRunner; use CodeIgniter\View\RendererInterface; +use Config\App; use Config\Cache; use Config\Images; use Config\Logger; @@ -207,8 +207,7 @@ public static function email($config = null, bool $getShared = true) { $config = new \Config\Email(); } - $email = new \CodeIgniter\Email\Email($config); - return $email; + return new \CodeIgniter\Email\Email($config); } /** @@ -232,8 +231,7 @@ public static function encrypter($config = null, $getShared = false) } $encryption = new Encryption($config); - $encrypter = $encryption->initialize($config); - return $encrypter; + return $encryption->initialize($config); } //-------------------------------------------------------------------- @@ -502,7 +500,7 @@ public static function pager($config = null, RendererInterface $view = null, boo if (empty($config)) { - $config = new \Config\Pager(); + $config = config('Pager'); } if (! $view instanceof RendererInterface) @@ -542,7 +540,7 @@ public static function parser(string $viewPath = null, $config = null, bool $get $viewPath = $paths->viewDirectory; } - return new Parser($config, $viewPath, static::locator(true), CI_DEBUG, static::logger(true)); + return new Parser($config, $viewPath, static::locator(), CI_DEBUG, static::logger()); } //-------------------------------------------------------------------- @@ -577,7 +575,7 @@ public static function renderer(string $viewPath = null, $config = null, bool $g $viewPath = $paths->viewDirectory; } - return new \CodeIgniter\View\View($config, $viewPath, static::locator(true), CI_DEBUG, static::logger(true)); + return new \CodeIgniter\View\View($config, $viewPath, static::locator(), CI_DEBUG, static::logger()); } //-------------------------------------------------------------------- @@ -705,7 +703,7 @@ public static function router(RouteCollectionInterface $routes = null, Request $ if (empty($routes)) { - $routes = static::routes(true); + $routes = static::routes(); } return new Router($routes, $request); @@ -759,7 +757,7 @@ public static function session(App $config = null, bool $getShared = true) $config = config(App::class); } - $logger = static::logger(true); + $logger = static::logger(); $driverName = $config->sessionDriver; $driver = new $driverName($config, static::request()->getIpAddress()); diff --git a/system/Controller.php b/system/Controller.php index 172104e0..985d828d 100644 --- a/system/Controller.php +++ b/system/Controller.php @@ -39,11 +39,11 @@ namespace CodeIgniter; -use Config\Services; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; -use CodeIgniter\Validation\Validation; use CodeIgniter\Validation\Exceptions\ValidationException; +use CodeIgniter\Validation\Validation; +use Config\Services; use Psr\Log\LoggerInterface; /** @@ -117,7 +117,6 @@ public function initController(RequestInterface $request, ResponseInterface $res $this->request = $request; $this->response = $response; $this->logger = $logger; - $this->logger->info('Controller "' . get_class($this) . '" loaded.'); if ($this->forceHTTPS > 0) { @@ -195,7 +194,7 @@ protected function validate($rules, array $messages = []): bool // If you replace the $rules array with the name of the group if (is_string($rules)) { - $validation = new \Config\Validation(); + $validation = config('Validation'); // If the rule wasn't found in the \Config\Validation, we // should throw an exception so the developer can find it. @@ -214,12 +213,10 @@ protected function validate($rules, array $messages = []): bool $rules = $validation->$rules; } - $success = $this->validator + return $this->validator ->withRequest($this->request) ->setRules($rules, $messages) ->run(); - - return $success; } //-------------------------------------------------------------------- diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 800247f8..33e70045 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Database; +use Closure; use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Exceptions\DataException; -use Closure; /** * Class BaseBuilder @@ -103,7 +103,7 @@ class BaseBuilder * * @var array */ - protected $QBGroupBy = []; + public $QBGroupBy = []; /** * QB HAVING data @@ -213,6 +213,14 @@ class BaseBuilder */ protected $binds = []; + /** + * Collects the key count for named parameters + * in the Query object. + * + * @var array + */ + protected $bindsKeyCount = []; + /** * Some databases, like SQLite, do not by default * allow limiting of delete clauses. @@ -388,7 +396,7 @@ public function select($select = '*', bool $escape = null) */ public function selectMax(string $select = '', string $alias = '') { - return $this->maxMinAvgSum($select, $alias, 'MAX'); + return $this->maxMinAvgSum($select, $alias); } //-------------------------------------------------------------------- @@ -973,20 +981,38 @@ public function orHavingNotIn(string $key = null, $values = null, bool $escape = * @used-by whereNotIn() * @used-by orWhereNotIn() * - * @param string $key The field to search - * @param array|Closure $values The values searched on, or anonymous function with subquery - * @param boolean $not If the statement would be IN or NOT IN - * @param string $type - * @param boolean $escape - * @param string $clause (Internal use only) + * @param string $key The field to search + * @param array|Closure $values The values searched on, or anonymous function with subquery + * @param boolean $not If the statement would be IN or NOT IN + * @param string $type + * @param boolean $escape + * @param string $clause (Internal use only) + * @throws InvalidArgumentException * * @return BaseBuilder */ protected function _whereIn(string $key = null, $values = null, bool $not = false, string $type = 'AND ', bool $escape = null, string $clause = 'QBWhere') { - if ($key === null || $values === null || (! is_array($values) && ! ($values instanceof Closure))) + if (empty($key) || ! is_string($key)) { + if (CI_DEBUG) + { + throw new \InvalidArgumentException(sprintf('%s() expects $key to be a non-empty string', debug_backtrace(0, 2)[1]['function'])); + } + // @codeCoverageIgnoreStart return $this; + // @codeCoverageIgnoreEnd + } + + if ($values === null || (! is_array($values) && ! ($values instanceof Closure))) + { + if (CI_DEBUG) + { + throw new \InvalidArgumentException(sprintf('%s() expects $values to be of type array or closure', debug_backtrace(0, 2)[1]['function'])); + } + // @codeCoverageIgnoreStart + return $this; + // @codeCoverageIgnoreEnd } is_bool($escape) || $escape = $this->db->protectIdentifiers; @@ -1301,7 +1327,7 @@ protected function _like_statement(string $prefix = null, string $column, string */ public function groupStart() { - return $this->groupStartPrepare('', 'AND ', 'QBWhere'); + return $this->groupStartPrepare(); } //-------------------------------------------------------------------- @@ -1313,7 +1339,7 @@ public function groupStart() */ public function orGroupStart() { - return $this->groupStartPrepare('', 'OR ', 'QBWhere'); + return $this->groupStartPrepare('', 'OR '); } //-------------------------------------------------------------------- @@ -1325,7 +1351,7 @@ public function orGroupStart() */ public function notGroupStart() { - return $this->groupStartPrepare('NOT ', 'AND ', 'QBWhere'); + return $this->groupStartPrepare('NOT '); } //-------------------------------------------------------------------- @@ -1337,7 +1363,7 @@ public function notGroupStart() */ public function orNotGroupStart() { - return $this->groupStartPrepare('NOT ', 'OR ', 'QBWhere'); + return $this->groupStartPrepare('NOT ', 'OR '); } //-------------------------------------------------------------------- @@ -1349,7 +1375,7 @@ public function orNotGroupStart() */ public function groupEnd() { - return $this->groupEndPrepare('QBWhere'); + return $this->groupEndPrepare(); } // -------------------------------------------------------------------- @@ -1682,9 +1708,9 @@ public function offset(int $offset) * * @return string */ - protected function _limit(string $sql): string + protected function _limit(string $sql, bool $offsetIgnore = false): string { - return $sql . ' LIMIT ' . ($this->QBOffset ? $this->QBOffset . ', ' : '') . $this->QBLimit; + return $sql . ' LIMIT ' . (false === $offsetIgnore && $this->QBOffset ? $this->QBOffset . ', ' : '') . $this->QBLimit; } //-------------------------------------------------------------------- @@ -1897,7 +1923,7 @@ public function countAllResults(bool $reset = true) $limit = $this->QBLimit; $this->QBLimit = false; - $sql = ($this->QBDistinct === true) + $sql = ($this->QBDistinct === true || ! empty($this->QBGroupBy)) ? $this->countString . $this->db->protectIdentifiers('numrows') . "\nFROM (\n" . $this->compileSelect() . "\n) CI_count_all_results" : @@ -2012,8 +2038,9 @@ public function insertBatch(array $set = null, bool $escape = null, int $batchSi { throw new DatabaseException('You must use the "set" method to update an entry.'); } - + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } } else @@ -2024,8 +2051,9 @@ public function insertBatch(array $set = null, bool $escape = null, int $batchSi { throw new DatabaseException('insertBatch() called with no data'); } - + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } $this->setInsertBatch($set, '', $escape); @@ -2234,8 +2262,9 @@ protected function validateInsert(): bool { throw new DatabaseException('You must use the "set" method to update an entry.'); } - + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } return true; @@ -2284,7 +2313,9 @@ public function replace(array $set = null) { throw new DatabaseException('You must use the "set" method to update an entry.'); } + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } $table = $this->QBFrom[0]; @@ -2445,7 +2476,7 @@ protected function _update(string $table, array $values): string return 'UPDATE ' . $this->compileIgnore('update') . $table . ' SET ' . implode(', ', $valStr) . $this->compileWhereHaving('QBWhere') . $this->compileOrderBy() - . ($this->QBLimit ? $this->_limit(' ') : ''); + . ($this->QBLimit ? $this->_limit(' ', true) : ''); } //-------------------------------------------------------------------- @@ -2468,8 +2499,9 @@ protected function validateUpdate(): bool { throw new DatabaseException('You must use the "set" method to update an entry.'); } - + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } return true; @@ -2497,8 +2529,9 @@ public function updateBatch(array $set = null, string $index = null, int $batchS { throw new DatabaseException('You must specify an index to match on for batch updates.'); } - + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } if ($set === null) @@ -2509,7 +2542,9 @@ public function updateBatch(array $set = null, string $index = null, int $batchS { throw new DatabaseException('You must use the "set" method to update an entry.'); } + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } } else @@ -2520,7 +2555,9 @@ public function updateBatch(array $set = null, string $index = null, int $batchS { throw new DatabaseException('updateBatch() called with no data'); } + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } $this->setUpdateBatch($set, $index); @@ -2573,7 +2610,7 @@ protected function _updateBatch(string $table, array $values, string $index): st $ids = []; $final = []; - foreach ($values as $key => $val) + foreach ($values as $val) { $ids[] = $val[$index]; @@ -2622,7 +2659,7 @@ public function setUpdateBatch($key, string $index = '', bool $escape = null) is_bool($escape) || $escape = $this->db->protectIdentifiers; - foreach ($key as $k => $v) + foreach ($key as $v) { $index_set = false; $clean = []; @@ -2769,8 +2806,9 @@ public function delete($where = '', int $limit = null, bool $reset_data = true) { throw new DatabaseException('Deletes are not allowed unless they contain a "where" or "like" clause.'); } - + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } $sql = $this->_delete($table); @@ -2787,7 +2825,7 @@ public function delete($where = '', int $limit = null, bool $reset_data = true) throw new DatabaseException('SQLite3 does not allow LIMITs on DELETE queries.'); } - $sql = $this->_limit($sql); + $sql = $this->_limit($sql, true); } if ($reset_data) @@ -2849,8 +2887,7 @@ public function decrement(string $column, int $value = 1) */ protected function _delete(string $table): string { - return 'DELETE ' . $this->compileIgnore('delete') . 'FROM ' . $table . $this->compileWhereHaving('QBWhere') - . ($this->QBLimit ? ' LIMIT ' . $this->QBLimit : ''); + return 'DELETE ' . $this->compileIgnore('delete') . 'FROM ' . $table . $this->compileWhereHaving('QBWhere'); } //-------------------------------------------------------------------- @@ -3304,6 +3341,12 @@ protected function resetSelect() { $this->db->setAliasedTables([]); } + + // Reset QBFrom part + if (! empty($this->QBFrom)) + { + $this->from(array_shift($this->QBFrom), true); + } } //-------------------------------------------------------------------- @@ -3402,12 +3445,11 @@ protected function setBind(string $key, $value = null, bool $escape = true): str return $key; } - $count = 0; - - while (array_key_exists($key . $count, $this->binds)) + if (! array_key_exists($key, $this->bindsKeyCount)) { - ++$count; + $this->bindsKeyCount[$key] = 0; } + $count = $this->bindsKeyCount[$key]++; $this->binds[$key . $count] = [ $value, diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index de9d510b..844cf7ba 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Database; -use CodeIgniter\Events\Events; use CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Events\Events; /** * Class BaseConnection @@ -1002,7 +1002,7 @@ public function prepare(\Closure $func, array $options = []) $this->initialize(); } - $this->pretend(true); + $this->pretend(); $sql = $func($this); @@ -1381,9 +1381,7 @@ public function escape($str) { if (is_array($str)) { - $str = array_map([&$this, 'escape'], $str); - - return $str; + return array_map([&$this, 'escape'], $str); } else if (is_string($str) || ( is_object($str) && method_exists($str, '__toString'))) { diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index b9d586a3..5e6abe35 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -39,6 +39,8 @@ namespace CodeIgniter\Database; +use CodeIgniter\Entity; + /** * Class BaseResult */ @@ -187,12 +189,12 @@ public function getCustomResultObject(string $className) return $this->customResultObject[$className]; } - is_null($this->rowData) || $this->dataSeek(0); + is_null($this->rowData) || $this->dataSeek(); $this->customResultObject[$className] = []; while ($row = $this->fetchObject($className)) { - if (method_exists($row, 'syncOriginal')) + if (! is_subclass_of($row, Entity::class) && method_exists($row, 'syncOriginal')) { $row->syncOriginal(); } @@ -237,7 +239,7 @@ public function getResultArray(): array return $this->resultArray; } - is_null($this->rowData) || $this->dataSeek(0); + is_null($this->rowData) || $this->dataSeek(); while ($row = $this->fetchAssoc()) { $this->resultArray[] = $row; @@ -280,10 +282,10 @@ public function getResultObject(): array return $this->resultObject; } - is_null($this->rowData) || $this->dataSeek(0); + is_null($this->rowData) || $this->dataSeek(); while ($row = $this->fetchObject()) { - if (method_exists($row, 'syncOriginal')) + if (! is_subclass_of($row, Entity::class) && method_exists($row, 'syncOriginal')) { $row->syncOriginal(); } @@ -312,7 +314,7 @@ public function getRow($n = 0, string $type = 'object') if (! is_numeric($n)) { // We cache the row data for subsequent uses - is_array($this->rowData) || $this->rowData = $this->getRowArray(0); + is_array($this->rowData) || $this->rowData = $this->getRowArray(); // array_key_exists() instead of isset() to allow for NULL values if (empty($this->rowData) || ! array_key_exists($n, $this->rowData)) @@ -433,7 +435,7 @@ public function setRow($key, $value = null) // We cache the row data for subsequent uses if (! is_array($this->rowData)) { - $this->rowData = $this->getRowArray(0); + $this->rowData = $this->getRowArray(); } if (is_array($key)) diff --git a/system/Database/BaseUtils.php b/system/Database/BaseUtils.php index a2ba2b0d..e9a4dd63 100644 --- a/system/Database/BaseUtils.php +++ b/system/Database/BaseUtils.php @@ -356,7 +356,7 @@ public function backup($params = []) 'tables' => [], 'ignore' => [], 'filename' => '', - 'format' => 'gzip', // gzip, zip, txt + 'format' => 'gzip', // gzip, txt 'add_drop' => true, 'add_insert' => true, 'newline' => "\n", @@ -383,15 +383,14 @@ public function backup($params = []) } // Validate the format - if (! in_array($prefs['format'], ['gzip', 'zip', 'txt'], true)) + if (! in_array($prefs['format'], ['gzip', 'txt'], true)) { $prefs['format'] = 'txt'; } // Is the encoder supported? If not, we'll either issue an // error or use plain text depending on the debug settings - if (($prefs['format'] === 'gzip' && ! function_exists('gzencode')) - || ( $prefs['format'] === 'zip' && ! function_exists('gzcompress'))) + if ($prefs['format'] === 'gzip' && ! function_exists('gzencode')) { if ($this->db->DBDebug) { @@ -401,46 +400,12 @@ public function backup($params = []) $prefs['format'] = 'txt'; } - // Was a Zip file requested? - if ($prefs['format'] === 'zip') - { - // Set the filename if not provided (only needed with Zip files) - if ($prefs['filename'] === '') - { - $prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database) - . date('Y-m-d_H-i', time()) . '.sql'; - } - else - { - // If they included the .zip file extension we'll remove it - if (preg_match('|.+?\.zip$|', $prefs['filename'])) - { - $prefs['filename'] = str_replace('.zip', '', $prefs['filename']); - } - - // Tack on the ".sql" file extension if needed - if (! preg_match('|.+?\.sql$|', $prefs['filename'])) - { - $prefs['filename'] .= '.sql'; - } - } - - // Load the Zip class and output it - // $CI =& get_instance(); - // $CI->load->library('zip'); - // $CI->zip->add_data($prefs['filename'], $this->_backup($prefs)); - // return $CI->zip->get_zip(); - } - elseif ($prefs['format'] === 'txt') // Was a text file requested? + if ($prefs['format'] === 'txt') // Was a text file requested? { return $this->_backup($prefs); } - elseif ($prefs['format'] === 'gzip') // Was a Gzip file requested? - { - return gzencode($this->_backup($prefs)); - } - return; + return gzencode($this->_backup($prefs)); } //-------------------------------------------------------------------- diff --git a/system/Database/Database.php b/system/Database/Database.php index a8d26b10..adb0063c 100644 --- a/system/Database/Database.php +++ b/system/Database/Database.php @@ -109,9 +109,7 @@ public function loadForge(ConnectionInterface $db) $db->initialize(); } - $class = new $className($db); - - return $class; + return new $className($db); } //-------------------------------------------------------------------- @@ -133,9 +131,7 @@ public function loadUtils(ConnectionInterface $db) $db->initialize(); } - $class = new $className($db); - - return $class; + return new $className($db); } //-------------------------------------------------------------------- diff --git a/system/Database/Forge.php b/system/Database/Forge.php index b40dc5eb..e38d505c 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -110,14 +110,14 @@ class Forge * * @var string */ - protected $createDatabaseIfStr = null; + protected $createDatabaseIfStr; /** * CHECK DATABASE EXIST statement * * @var string */ - protected $checkDatabaseExistStr = null; + protected $checkDatabaseExistStr; /** * DROP DATABASE statement @@ -717,9 +717,7 @@ protected function _dropTable(string $table, bool $if_exists, bool $cascade): st } } - $sql = $sql . ' ' . $this->db->escapeIdentifiers($table); - - return $sql; + return $sql . ' ' . $this->db->escapeIdentifiers($table); } //-------------------------------------------------------------------- diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index 6d4f0e2b..55350c86 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -38,10 +38,10 @@ namespace CodeIgniter\Database; -use Config\Services; use CodeIgniter\CLI\CLI; use CodeIgniter\Config\BaseConfig; use CodeIgniter\Exceptions\ConfigException; +use Config\Services; /** * Class MigrationRunner @@ -1027,6 +1027,15 @@ protected function migrate($direction, $migration): bool // Determine DBGroup to use $group = $instance->getDBGroup() ?? config('Database')->defaultGroup; + // Skip tests db group when not running in testing environment + if (ENVIRONMENT !== 'testing' && $group === 'tests' && $this->groupFilter !== 'tests') + { + // @codeCoverageIgnoreStart + $this->groupSkip = true; + return true; + // @codeCoverageIgnoreEnd + } + // Skip migration if group filtering was set if ($direction === 'up' && ! is_null($this->groupFilter) && $this->groupFilter !== $group) { diff --git a/system/Database/ModelFactory.php b/system/Database/ModelFactory.php index 6b7d514e..b2b43620 100644 --- a/system/Database/ModelFactory.php +++ b/system/Database/ModelFactory.php @@ -12,14 +12,6 @@ class ModelFactory */ static private $instances = []; - /** - * The Database connection to use, - * if other than default. - * - * @var ConnectionInterface - */ - static private $connection = null; - /** * Create new configuration instances or return * a shared instance diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 26b213ce..9bda5b06 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -326,8 +326,19 @@ public function execute(string $sql) $res->free(); } } - - return $this->connID->query($this->prepQuery($sql)); + try + { + return $this->connID->query($this->prepQuery($sql)); + } + catch (\mysqli_sql_exception $e) + { + log_message('error', $e); + if ($this->DBDebug) + { + throw $e; + } + } + return false; } //-------------------------------------------------------------------- @@ -424,8 +435,6 @@ public function escapeLikeStringDirect($str) '\\' . '_', ], $str ); - - return $str; } //-------------------------------------------------------------------- diff --git a/system/Database/MySQLi/PreparedQuery.php b/system/Database/MySQLi/PreparedQuery.php index 35d87b12..3ef41047 100644 --- a/system/Database/MySQLi/PreparedQuery.php +++ b/system/Database/MySQLi/PreparedQuery.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Database\MySQLi; -use CodeIgniter\Database\PreparedQueryInterface; use CodeIgniter\Database\BasePreparedQuery; +use CodeIgniter\Database\PreparedQueryInterface; /** * Prepared query for MySQLi @@ -116,9 +116,7 @@ public function _execute(array $data): bool // Bind it $this->statement->bind_param($bindTypes, ...$data); - $success = $this->statement->execute(); - - return $success; + return $this->statement->execute(); } //-------------------------------------------------------------------- diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index bf20a3d1..fa45c45d 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -40,7 +40,6 @@ use CodeIgniter\Database\BaseBuilder; use CodeIgniter\Database\Exceptions\DatabaseException; -use http\Encoding\Stream\Inflate; /** * Builder for Postgre @@ -196,7 +195,9 @@ public function replace(array $set = null) { throw new DatabaseException('You must use the "set" method to update an entry.'); } + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } $table = $this->QBFrom[0]; @@ -269,7 +270,7 @@ public function delete($where = '', int $limit = null, bool $reset_data = true) * * @return string */ - protected function _limit(string $sql): string + protected function _limit(string $sql, bool $offsetIgnore = false): string { return $sql . ' LIMIT ' . $this->QBLimit . ($this->QBOffset ? " OFFSET {$this->QBOffset}" : ''); } @@ -316,7 +317,7 @@ protected function _update(string $table, array $values): string protected function _updateBatch(string $table, array $values, string $index): string { $ids = []; - foreach ($values as $key => $val) + foreach ($values as $val) { $ids[] = $val[$index]; diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index b239232b..c3d6db6b 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -187,11 +187,23 @@ public function getVersion(): string * * @param string $sql * - * @return resource + * @return mixed */ public function execute(string $sql) { - return pg_query($this->connID, $sql); + try + { + return pg_query($this->connID, $sql); + } + catch (\ErrorException $e) + { + log_message('error', $e); + if ($this->DBDebug) + { + throw $e; + } + } + return false; } //-------------------------------------------------------------------- diff --git a/system/Database/Postgre/PreparedQuery.php b/system/Database/Postgre/PreparedQuery.php index 7b3bff33..27e3da2c 100644 --- a/system/Database/Postgre/PreparedQuery.php +++ b/system/Database/Postgre/PreparedQuery.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Database\Postgre; -use CodeIgniter\Database\PreparedQueryInterface; use CodeIgniter\Database\BasePreparedQuery; +use CodeIgniter\Database\PreparedQueryInterface; /** * Prepared query for Postgre @@ -148,12 +148,10 @@ public function parameterize(string $sql): string // Track our current value $count = 0; - $sql = preg_replace_callback('/\?/', function ($matches) use (&$count) { + return preg_replace_callback('/\?/', function ($matches) use (&$count) { $count ++; return "\${$count}"; }, $sql); - - return $sql; } //-------------------------------------------------------------------- diff --git a/system/Database/Query.php b/system/Database/Query.php index 301185b4..dae2eeb1 100644 --- a/system/Database/Query.php +++ b/system/Database/Query.php @@ -368,7 +368,7 @@ protected function compileBinds() { $sql = $this->finalQueryString; - $hasNamedBinds = strpos($sql, ':') !== false; + $hasNamedBinds = strpos($sql, ':') !== false && strpos($sql, ':=') === false; if (empty($this->binds) || empty($this->bindMarker) || (strpos($sql, $this->bindMarker) === false && @@ -440,9 +440,7 @@ protected function matchNamedBinds(string $sql, array $binds): string $replacers[":{$placeholder}:"] = $escapedValue; } - $sql = strtr($sql, $replacers); - - return $sql; + return strtr($sql, $replacers); } //-------------------------------------------------------------------- diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index ae83cfc1..d3ac1cb6 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -165,9 +165,21 @@ public function getVersion(): string */ public function execute(string $sql) { - return $this->isWriteType($sql) - ? $this->connID->exec($sql) - : $this->connID->query($sql); + try + { + return $this->isWriteType($sql) + ? $this->connID->exec($sql) + : $this->connID->query($sql); + } + catch (\ErrorException $e) + { + log_message('error', $e); + if ($this->DBDebug) + { + throw $e; + } + } + return false; } //-------------------------------------------------------------------- @@ -323,8 +335,8 @@ public function _fieldData(string $table): array $retVal[$i]->type = $query[$i]->type; $retVal[$i]->max_length = null; $retVal[$i]->default = $query[$i]->dflt_value; - $retVal[$i]->primary_key = isset($query[$i]->pk) ? (bool)$query[$i]->pk : false; - $retVal[$i]->nullable = isset($query[$i]->notnull) ? ! (bool)$query[$i]->notnull : false; + $retVal[$i]->primary_key = isset($query[$i]->pk) && (bool)$query[$i]->pk; + $retVal[$i]->nullable = isset($query[$i]->notnull) && ! (bool)$query[$i]->notnull; } return $retVal; diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index 338c6e4e..bb6f4826 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -167,7 +167,6 @@ protected function _alterTable(string $alter_type, string $table, $field) ->run(); return ''; - break; case 'CHANGE': $sqlTable = new Table($this->db, $this); @@ -176,7 +175,6 @@ protected function _alterTable(string $alter_type, string $table, $field) ->run(); return null; - break; default: return parent::_alterTable($alter_type, $table, $field); } diff --git a/system/Database/SQLite3/PreparedQuery.php b/system/Database/SQLite3/PreparedQuery.php index fbd6feac..22202c3b 100644 --- a/system/Database/SQLite3/PreparedQuery.php +++ b/system/Database/SQLite3/PreparedQuery.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Database\SQLite3; -use CodeIgniter\Database\PreparedQueryInterface; use CodeIgniter\Database\BasePreparedQuery; +use CodeIgniter\Database\PreparedQueryInterface; /** * Prepared query for SQLite3 diff --git a/system/Database/Seeder.php b/system/Database/Seeder.php index 682b7cec..97eb414f 100644 --- a/system/Database/Seeder.php +++ b/system/Database/Seeder.php @@ -122,6 +122,8 @@ public function __construct(BaseConfig $config, BaseConnection $db = null) } $this->db = & $db; + + $this->forge = \Config\Database::forge($this->DBGroup); } //-------------------------------------------------------------------- diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index 7cc3043b..7f7ca2cb 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -142,6 +142,7 @@ public function initialize() */ public function exceptionHandler(Throwable $exception) { + // @codeCoverageIgnoreStart $codes = $this->determineCodes($exception); $statusCode = $codes[0]; $exitCode = $codes[1]; @@ -171,6 +172,7 @@ public function exceptionHandler(Throwable $exception) $this->render($exception, $statusCode); exit($exitCode); + // @codeCoverageIgnoreEnd } //-------------------------------------------------------------------- @@ -186,11 +188,10 @@ public function exceptionHandler(Throwable $exception) * @param string $message * @param string|null $file * @param integer|null $line - * @param null $context * * @throws \ErrorException */ - public function errorHandler(int $severity, string $message, string $file = null, int $line = null, $context = null) + public function errorHandler(int $severity, string $message, string $file = null, int $line = null) { if (! (error_reporting() & $severity)) { @@ -371,17 +372,20 @@ protected function determineCodes(Throwable $exception): array */ public static function cleanPath(string $file): string { - if (strpos($file, APPPATH) === 0) + switch (true) { - $file = 'APPPATH/' . substr($file, strlen(APPPATH)); - } - elseif (strpos($file, SYSTEMPATH) === 0) - { - $file = 'SYSTEMPATH/' . substr($file, strlen(SYSTEMPATH)); - } - elseif (strpos($file, FCPATH) === 0) - { - $file = 'FCPATH/' . substr($file, strlen(FCPATH)); + case strpos($file, APPPATH) === 0: + $file = 'APPPATH' . DIRECTORY_SEPARATOR . substr($file, strlen(APPPATH)); + break; + case strpos($file, SYSTEMPATH) === 0: + $file = 'SYSTEMPATH' . DIRECTORY_SEPARATOR . substr($file, strlen(SYSTEMPATH)); + break; + case strpos($file, FCPATH) === 0: + $file = 'FCPATH' . DIRECTORY_SEPARATOR . substr($file, strlen(FCPATH)); + break; + case defined('VENDORPATH') && strpos($file, VENDORPATH) === 0; + $file = 'VENDORPATH' . DIRECTORY_SEPARATOR . substr($file, strlen(VENDORPATH)); + break; } return $file; diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 02e50fc8..ff2be35c 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -118,7 +118,7 @@ public function run(float $startTime, float $totalTime, RequestInterface $reques $data['startTime'] = $startTime; $data['totalTime'] = $totalTime * 1000; $data['totalMemory'] = number_format((memory_get_peak_usage()) / 1024 / 1024, 3); - $data['segmentDuration'] = $this->roundTo($data['totalTime'] / 7, 5); + $data['segmentDuration'] = $this->roundTo($data['totalTime'] / 7); $data['segmentCount'] = (int) ceil($data['totalTime'] / $data['segmentDuration']); $data['CI_VERSION'] = \CodeIgniter\CodeIgniter::CI_VERSION; $data['collectors'] = []; @@ -167,7 +167,7 @@ public function run(float $startTime, float $totalTime, RequestInterface $reques $data['vars']['post'][esc($name)] = is_array($value) ? '<pre>' . esc(print_r($value, true)) . '</pre>' : esc($value); } - foreach ($request->getHeaders() as $header => $value) + foreach ($request->getHeaders() as $value) { if (empty($value)) { @@ -415,6 +415,7 @@ public function respond() return; } + // @codeCoverageIgnoreStart $request = Services::request(); // If the request contains '?debugbar then we're @@ -459,6 +460,7 @@ public function respond() http_response_code(404); exit; // Exit here is needed to avoid load the index page } + // @codeCoverageIgnoreEnd } /** diff --git a/system/Debug/Toolbar/Collectors/Config.php b/system/Debug/Toolbar/Collectors/Config.php index 1ae809cd..82ef6347 100644 --- a/system/Debug/Toolbar/Collectors/Config.php +++ b/system/Debug/Toolbar/Collectors/Config.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; +use CodeIgniter\CodeIgniter; use Config\App; use Config\Services; -use CodeIgniter\CodeIgniter; /** * Debug toolbar configuration diff --git a/system/Debug/Toolbar/Collectors/Events.php b/system/Debug/Toolbar/Collectors/Events.php index 285d87d8..41c9f008 100644 --- a/system/Debug/Toolbar/Collectors/Events.php +++ b/system/Debug/Toolbar/Collectors/Events.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; -use Config\Services; use CodeIgniter\View\RendererInterface; +use Config\Services; /** * Views collector @@ -111,7 +111,7 @@ protected function formatTimelineData(): array $rows = $this->viewer->getPerformanceData(); - foreach ($rows as $name => $info) + foreach ($rows as $info) { $data[] = [ 'name' => 'View: ' . $info['view'], diff --git a/system/Debug/Toolbar/Collectors/Views.php b/system/Debug/Toolbar/Collectors/Views.php index 8bcec569..bf0c1108 100644 --- a/system/Debug/Toolbar/Collectors/Views.php +++ b/system/Debug/Toolbar/Collectors/Views.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; -use Config\Services; use CodeIgniter\View\RendererInterface; +use Config\Services; /** * Views collector @@ -126,7 +126,7 @@ protected function formatTimelineData(): array $rows = $this->viewer->getPerformanceData(); - foreach ($rows as $name => $info) + foreach ($rows as $info) { $data[] = [ 'name' => 'View: ' . $info['view'], diff --git a/system/Debug/Toolbar/Views/toolbarloader.js.php b/system/Debug/Toolbar/Views/toolbarloader.js.php index 4ecdeeb1..af693381 100644 --- a/system/Debug/Toolbar/Views/toolbarloader.js.php +++ b/system/Debug/Toolbar/Views/toolbarloader.js.php @@ -75,9 +75,11 @@ function newXHR() { var debugbarTime = realXHR.getResponseHeader('Debugbar-Time'); if (debugbarTime) { var h2 = document.querySelector('#ci-history > h2'); - h2.innerHTML = 'History <small>You have new debug data.</small> <button onclick="loadDoc(' + debugbarTime + ')">Update</button>'; - var badge = document.querySelector('a[data-tab="ci-history"] > span > .badge'); - badge.className += ' active'; + if(h2) { + h2.innerHTML = 'History <small>You have new debug data.</small> <button onclick="loadDoc(' + debugbarTime + ')">Update</button>'; + var badge = document.querySelector('a[data-tab="ci-history"] > span > .badge'); + badge.className += ' active'; + } } } }, false); diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index 1f047f6e..4c2d3337 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -38,10 +38,8 @@ namespace CodeIgniter\Encryption; -use Config\Encryption as EncryptionConfig; -use CodeIgniter\Encryption\Exceptions\EncryptionException; use CodeIgniter\Config\BaseConfig; -use Config\Services; +use CodeIgniter\Encryption\Exceptions\EncryptionException; /** * CodeIgniter Encryption Manager diff --git a/system/Encryption/Handlers/OpenSSLHandler.php b/system/Encryption/Handlers/OpenSSLHandler.php index 72b60236..50622d1f 100644 --- a/system/Encryption/Handlers/OpenSSLHandler.php +++ b/system/Encryption/Handlers/OpenSSLHandler.php @@ -64,7 +64,7 @@ class OpenSSLHandler extends BaseHandler * * @param BaseConfig $config * - * @throws \CodeIgniter\Encryption\EncryptionException + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException */ public function __construct(BaseConfig $config = null) { @@ -77,7 +77,7 @@ public function __construct(BaseConfig $config = null) * @param string $data Input data * @param array $params Over-ridden parameters, specifically the key * @return string - * @throws \CodeIgniter\Encryption\EncryptionException + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException */ public function encrypt($data, $params = null) { @@ -114,9 +114,8 @@ public function encrypt($data, $params = null) $result = $iv . $data; $hmacKey = \hash_hmac($this->digest, $result, $secret, true); - $result = $hmacKey . $result; - return $result; + return $hmacKey . $result; } // -------------------------------------------------------------------- @@ -127,7 +126,7 @@ public function encrypt($data, $params = null) * @param string $data Encrypted data * @param array $params Over-ridden parameters, specifically the key * @return string - * @throws \CodeIgniter\Encryption\EncryptionException + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException */ public function decrypt($data, $params = null) { @@ -145,7 +144,7 @@ public function decrypt($data, $params = null) } if (empty($this->key)) { - throw EncryptionException::forStarterKeyNeeded(); + throw EncryptionException::forNeedsStarterKey(); } // derive a secret key diff --git a/system/Entity.php b/system/Entity.php index 94153d0b..1ccd6d29 100644 --- a/system/Entity.php +++ b/system/Entity.php @@ -39,14 +39,13 @@ namespace CodeIgniter; -use CodeIgniter\Exceptions\EntityException; -use CodeIgniter\I18n\Time; use CodeIgniter\Exceptions\CastException; +use CodeIgniter\I18n\Time; /** * Entity encapsulation, for use with CodeIgniter\Model */ -class Entity +class Entity implements \JsonSerializable { /** * Maps names used in sets and gets against unique @@ -165,7 +164,7 @@ public function toArray(bool $onlyChanged = false, bool $cast = true): array // allow our magic methods a chance to do their thing. foreach ($this->attributes as $key => $value) { - if (substr($key, 0, 1) === '_') + if (strpos($key, '_') === 0) { continue; } @@ -353,7 +352,7 @@ public function __set(string $key, $value = null) if (array_key_exists($key, $this->casts)) { - $isNullable = substr($this->casts[$key], 0, 1) === '?'; + $isNullable = strpos($this->casts[$key], '?') === 0; $castTo = $isNullable ? substr($this->casts[$key], 1) : $this->casts[$key]; } @@ -530,7 +529,7 @@ protected function mutateDate($value) protected function castAs($value, string $type) { - if (substr($type, 0, 1) === '?') + if (strpos($type, '?') === 0) { if ($value === null) { @@ -570,17 +569,15 @@ protected function castAs($value, string $type) $value = (array)$value; break; case 'json': - $value = $this->castAsJson($value, false); + $value = $this->castAsJson($value); break; case 'json-array': $value = $this->castAsJson($value, true); break; case 'datetime': - return new \DateTime($value); - break; + return $this->mutateDate($value); case 'timestamp': return strtotime($value); - break; } return $value; @@ -614,4 +611,15 @@ private function castAsJson($value, bool $asArray = false) } return $tmp; } + + /** + * Support for json_encode() + * + * @return array|mixed + * @throws \Exception + */ + public function jsonSerialize() + { + return $this->toArray(); + } } diff --git a/system/Exceptions/CastException.php b/system/Exceptions/CastException.php index a5bb0659..ddcb7178 100644 --- a/system/Exceptions/CastException.php +++ b/system/Exceptions/CastException.php @@ -19,22 +19,17 @@ public static function forInvalidJsonFormatException(int $error) switch($error) { case JSON_ERROR_DEPTH: - throw new static(lang('Cast.jsonErrorDepth')); - break; + return new static(lang('Cast.jsonErrorDepth')); case JSON_ERROR_STATE_MISMATCH: - throw new static(lang('Cast.jsonErrorStateMismatch')); - break; + return new static(lang('Cast.jsonErrorStateMismatch')); case JSON_ERROR_CTRL_CHAR: - throw new static(lang('Cast.jsonErrorCtrlChar')); - break; + return new static(lang('Cast.jsonErrorCtrlChar')); case JSON_ERROR_SYNTAX: - throw new static(lang('Cast.jsonErrorSyntax')); - break; + return new static(lang('Cast.jsonErrorSyntax')); case JSON_ERROR_UTF8: - throw new static(lang('Cast.jsonErrorUtf8')); - break; + return new static(lang('Cast.jsonErrorUtf8')); default: - throw new static(lang('Cast.jsonErrorUnknown')); + return new static(lang('Cast.jsonErrorUnknown')); } } diff --git a/system/Exceptions/ConfigException.php b/system/Exceptions/ConfigException.php index dc36377e..964c5212 100644 --- a/system/Exceptions/ConfigException.php +++ b/system/Exceptions/ConfigException.php @@ -16,6 +16,6 @@ class ConfigException extends CriticalError public static function forDisabledMigrations() { - throw new static(lang('Migrations.disabled')); + return new static(lang('Migrations.disabled')); } } diff --git a/system/Files/File.php b/system/Files/File.php index 8b5fc2ed..1f597958 100644 --- a/system/Files/File.php +++ b/system/Files/File.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Files; -use SplFileInfo; use CodeIgniter\Files\Exceptions\FileException; use CodeIgniter\Files\Exceptions\FileNotFoundException; +use SplFileInfo; /** * Wrapper for PHP's built-in SplFileInfo, with goodies. diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 853e135a..451a2dab 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Filters; use CodeIgniter\Config\BaseConfig; +use CodeIgniter\Filters\Exceptions\FilterException; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; -use CodeIgniter\Filters\Exceptions\FilterException; /** * Filters @@ -150,47 +150,59 @@ public function run(string $uri, string $position = 'before') throw FilterException::forNoAlias($alias); } - $class = new $this->config->aliases[$alias](); - - if (! $class instanceof FilterInterface) + if (is_array($this->config->aliases[$alias])) { - throw FilterException::forIncorrectInterface(get_class($class)); + $classNames = $this->config->aliases[$alias]; + } + else + { + $classNames = [$this->config->aliases[$alias]]; } - if ($position === 'before') + foreach ($classNames as $className) { - $result = $class->before($this->request, $this->arguments[$alias] ?? null); + $class = new $className(); - if ($result instanceof RequestInterface) + if (! $class instanceof FilterInterface) { - $this->request = $result; - continue; + throw FilterException::forIncorrectInterface(get_class($class)); } - // If the response object was sent back, - // then send it and quit. - if ($result instanceof ResponseInterface) + if ($position === 'before') { - // short circuit - bypass any other filters - return $result; - } + $result = $class->before($this->request, $this->arguments[$alias] ?? null); - // Ignore an empty result - if (empty($result)) - { - continue; - } + if ($result instanceof RequestInterface) + { + $this->request = $result; + continue; + } - return $result; - } - elseif ($position === 'after') - { - $result = $class->after($this->request, $this->response); + // If the response object was sent back, + // then send it and quit. + if ($result instanceof ResponseInterface) + { + // short circuit - bypass any other filters + return $result; + } + + // Ignore an empty result + if (empty($result)) + { + continue; + } - if ($result instanceof ResponseInterface) + return $result; + } + elseif ($position === 'after') { - $this->response = $result; - continue; + $result = $class->after($this->request, $this->response); + + if ($result instanceof ResponseInterface) + { + $this->response = $result; + continue; + } } } } diff --git a/system/Filters/Honeypot.php b/system/Filters/Honeypot.php index aea08c3c..a5807e42 100644 --- a/system/Filters/Honeypot.php +++ b/system/Filters/Honeypot.php @@ -38,10 +38,10 @@ namespace CodeIgniter\Filters; +use CodeIgniter\Honeypot\Exceptions\HoneypotException; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Config\Services; -use CodeIgniter\Honeypot\Exceptions\HoneypotException; /** * Honeypot filter diff --git a/system/Format/JSONFormatter.php b/system/Format/JSONFormatter.php index 50caf393..823f3ca2 100644 --- a/system/Format/JSONFormatter.php +++ b/system/Format/JSONFormatter.php @@ -56,13 +56,13 @@ class JSONFormatter implements FormatterInterface */ public function format($data) { - $options = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES; + $options = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR; $options = ENVIRONMENT === 'production' ? $options : $options | JSON_PRETTY_PRINT; $result = json_encode($data, $options, 512); - if (json_last_error() !== JSON_ERROR_NONE) + if ( ! in_array(json_last_error(), [JSON_ERROR_NONE, JSON_ERROR_RECURSION])) { throw FormatException::forInvalidJSON(json_last_error_msg()); } diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index c5506acc..d1a8e944 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -536,9 +536,7 @@ protected function applyMethod(string $method, array $curl_options): array // Have content? if ($size === null || $size > 0) { - $curl_options = $this->applyBody($curl_options); - - return $curl_options; + return $this->applyBody($curl_options); } if ($method === 'PUT' || $method === 'POST') diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 713312f9..910f7002 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -136,7 +136,7 @@ class ContentSecurityPolicy * * @var string */ - protected $reportURI = null; + protected $reportURI; /** * Used for security enforcement diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index 0b363cdb..b7c04dfb 100755 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -427,7 +427,7 @@ public function getPostGet($index = null, $filter = null, $flags = null) // Use $_POST directly here, since filter_has_var only // checks the initial POST data, not anything that might // have been added since. - return isset($_POST[$index]) ? $this->getPost($index, $filter, $flags) : $this->getGet($index, $filter, $flags); + return isset($_POST[$index]) ? $this->getPost($index, $filter, $flags) : (isset($_GET[$index]) ? $this->getGet($index, $filter, $flags) : $this->getPost()); } //-------------------------------------------------------------------- @@ -446,7 +446,7 @@ public function getGetPost($index = null, $filter = null, $flags = null) // Use $_GET directly here, since filter_has_var only // checks the initial GET data, not anything that might // have been added since. - return isset($_GET[$index]) ? $this->getGet($index, $filter, $flags) : $this->getPost($index, $filter, $flags); + return isset($_GET[$index]) ? $this->getGet($index, $filter, $flags) : (isset($_POST[$index]) ? $this->getPost($index, $filter, $flags) : $this->getGet()); } //-------------------------------------------------------------------- @@ -616,7 +616,6 @@ protected function detectURI(string $protocol, string $baseURL) $this->uri->setScheme(parse_url($baseURL, PHP_URL_SCHEME)); $this->uri->setHost(parse_url($baseURL, PHP_URL_HOST)); $this->uri->setPort(parse_url($baseURL, PHP_URL_PORT)); - $this->uri->resolveRelativeURI(parse_url($baseURL, PHP_URL_PATH)); // Ensure we have any query vars $this->uri->setQuery($_SERVER['QUERY_STRING'] ?? ''); @@ -722,7 +721,7 @@ protected function parseRequestURI(): string $query = $parts['query'] ?? ''; $uri = $parts['path'] ?? ''; - if (isset($_SERVER['SCRIPT_NAME'][0])) + if (isset($_SERVER['SCRIPT_NAME'][0]) && pathinfo($_SERVER['SCRIPT_NAME'], PATHINFO_EXTENSION) === 'php') { // strip the script name from the beginning of the URI if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 1afb1d52..6d7a5cce 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -316,7 +316,9 @@ public function appendHeader(string $name, string $value) { $orig_name = $this->getHeaderName($name); - $this->headers[$orig_name]->appendValue($value); + array_key_exists($orig_name, $this->headers) + ? $this->headers[$orig_name]->appendValue($value) + : $this->setHeader($name, $value); return $this; } diff --git a/system/HTTP/Negotiate.php b/system/HTTP/Negotiate.php index 6ac2d06b..74aa4706 100644 --- a/system/HTTP/Negotiate.php +++ b/system/HTTP/Negotiate.php @@ -179,7 +179,7 @@ public function encoding(array $supported = []): string */ public function language(array $supported): string { - return $this->getBestMatch($supported, $this->request->getHeaderLine('accept-language')); + return $this->getBestMatch($supported, $this->request->getHeaderLine('accept-language'), false, false, true); } //-------------------------------------------------------------------- @@ -198,10 +198,11 @@ public function language(array $supported): string * @param boolean $enforceTypes If TRUE, will compare media types and sub-types. * @param boolean $strictMatch If TRUE, will return empty string on no match. * If FALSE, will return the first supported element. + * @param boolean $matchLocales If TRUE, will match locale sub-types to a broad type (fr-FR = fr) * * @return string Best match */ - protected function getBestMatch(array $supported, string $header = null, bool $enforceTypes = false, bool $strictMatch = false): string + protected function getBestMatch(array $supported, string $header = null, bool $enforceTypes = false, bool $strictMatch = false, bool $matchLocales = false): string { if (empty($supported)) { @@ -232,7 +233,7 @@ protected function getBestMatch(array $supported, string $header = null, bool $e // If an acceptable value is supported, return it foreach ($supported as $available) { - if ($this->match($accept, $available, $enforceTypes)) + if ($this->match($accept, $available, $enforceTypes, $matchLocales)) { return $available; } @@ -337,12 +338,14 @@ public function parseHeader(string $header): array /** * Match-maker * - * @param array $acceptable - * @param string $supported - * @param boolean $enforceTypes + * @param array $acceptable + * @param string $supported + * @param boolean $enforceTypes + * @param boolean $matchLocales + * * @return boolean */ - protected function match(array $acceptable, string $supported, bool $enforceTypes = false): bool + protected function match(array $acceptable, string $supported, bool $enforceTypes = false, $matchLocales = false): bool { $supported = $this->parseHeader($supported); if (is_array($supported) && count($supported) === 1) @@ -363,6 +366,12 @@ protected function match(array $acceptable, string $supported, bool $enforceType return $this->matchTypes($acceptable, $supported); } + // Do we need to match locales against broader locales? + if ($matchLocales) + { + return $this->matchLocales($acceptable, $supported); + } + return false; } @@ -409,8 +418,14 @@ protected function matchParameters(array $acceptable, array $supported): bool */ public function matchTypes(array $acceptable, array $supported): bool { - list($aType, $aSubType) = explode('/', $acceptable['value']); - list($sType, $sSubType) = explode('/', $supported['value']); + [ + $aType, + $aSubType, + ] = explode('/', $acceptable['value']); + [ + $sType, + $sSubType, + ] = explode('/', $supported['value']); // If the types don't match, we're done. if ($aType !== $sType) @@ -429,4 +444,25 @@ public function matchTypes(array $acceptable, array $supported): bool } //-------------------------------------------------------------------- + + /** + * Will match locales against their broader pairs, so that fr-FR would + * match a supported localed of fr + * + * @param array $acceptable + * @param array $supported + * + * @return boolean + */ + public function matchLocales(array $acceptable, array $supported): bool + { + $aBroad = mb_strpos($acceptable['value'], '-') > 0 + ? mb_substr($acceptable['value'], 0, mb_strpos($acceptable['value'], '-')) + : $acceptable['value']; + $sBroad = mb_strpos($supported['value'], '-') > 0 + ? mb_substr($supported['value'], 0, mb_strpos($supported['value'], '-')) + : $supported['value']; + + return strtolower($aBroad) === strtolower($sBroad); + } } diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index a6d3c5ec..0d8320bb 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -40,10 +40,10 @@ namespace CodeIgniter\HTTP; -use Config\App; -use Config\Format; use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\Pager\PagerInterface; +use Config\App; +use Config\Format; /** * Representation of an outgoing, getServer-side response. @@ -986,6 +986,7 @@ public function deleteCookie(string $name = '', string $domain = '', string $pat $name = $prefix . $name; + $cookieHasFlag = false; foreach ($this->cookies as &$cookie) { if ($cookie['name'] === $name) @@ -1000,11 +1001,16 @@ public function deleteCookie(string $name = '', string $domain = '', string $pat } $cookie['value'] = ''; $cookie['expires'] = ''; - + $cookieHasFlag = true; break; } } + if (! $cookieHasFlag) + { + $this->setCookie($name, '', '', $domain, $path, $prefix); + } + return $this; } diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 001b0a96..09aa3cdc 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -566,7 +566,7 @@ public static function createURIString(string $scheme = null, string $authority $uri .= $authority; } - if ($path) + if ($path !== '') { $uri .= substr($uri, -1, 1) !== '/' ? '/' . ltrim($path, '/') : $path; } @@ -597,7 +597,12 @@ public function setAuthority(string $str) { $parts = parse_url($str); - if (empty($parts['host']) && ! empty($parts['path'])) + if (! isset($parts['path'])) + { + $parts['path'] = $this->getPath(); + } + + if (empty($parts['host']) && $parts['path'] !== '') { $parts['host'] = $parts['path']; unset($parts['path']); @@ -705,7 +710,9 @@ public function setPath(string $path) { $this->path = $this->filterPath($path); - $this->segments = explode('/', $this->path); + $tempPath = trim($this->path, '/'); + + $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); return $this; } @@ -721,7 +728,9 @@ public function refreshPath() { $this->path = $this->filterPath(implode('/', $this->segments)); - $this->segments = explode('/', $this->path); + $tempPath = trim($this->path, '/'); + + $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); return $this; } @@ -913,7 +922,7 @@ protected function applyParts(array $parts) { $this->user = $parts['user']; } - if (! empty($parts['path'])) + if (isset($parts['path']) && $parts['path'] !== '') { $this->path = $this->filterPath($parts['path']); } @@ -953,9 +962,11 @@ protected function applyParts(array $parts) } // Populate our segments array - if (! empty($parts['path'])) + if (isset($parts['path']) && $parts['path'] !== '') { - $this->segments = explode('/', trim($parts['path'], '/')); + $tempPath = trim($parts['path'], '/'); + + $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); } } @@ -1048,14 +1059,14 @@ public function resolveRelativeURI(string $uri) */ protected function mergePaths(URI $base, URI $reference): string { - if (! empty($base->getAuthority()) && empty($base->getPath())) + if (! empty($base->getAuthority()) && $base->getPath() === '') { return '/' . ltrim($reference->getPath(), '/ '); } $path = explode('/', $base->getPath()); - if (empty($path[0])) + if ($path[0] === '') { unset($path[0]); } @@ -1082,7 +1093,7 @@ protected function mergePaths(URI $base, URI $reference): string */ public function removeDotSegments(string $path): string { - if (empty($path) || $path === '/') + if ($path === '' || $path === '/') { return $path; } @@ -1091,7 +1102,7 @@ public function removeDotSegments(string $path): string $input = explode('/', $path); - if (empty($input[0])) + if ($input[0] === '') { unset($input[0]); $input = array_values($input); diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 6e5b5965..c6873fd5 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -51,7 +51,7 @@ class UserAgent * * @var string */ - protected $agent = null; + protected $agent; /** * Flag for if the user-agent belongs to a browser diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index 9c5d8f6e..c114df9f 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -79,7 +79,7 @@ function _array_search_dot(array $indexes, array $array) ? array_shift($indexes) : null; - if (empty($currentIndex) || (! isset($array[$currentIndex]) && $currentIndex !== '*')) + if ((empty($currentIndex) && intval($currentIndex) !== 0) || (! isset($array[$currentIndex]) && $currentIndex !== '*')) { return null; } @@ -90,7 +90,7 @@ function _array_search_dot(array $indexes, array $array) // If $array has more than 1 item, we have to loop over each. if (is_array($array)) { - foreach ($array as $key => $value) + foreach ($array as $value) { $answer = _array_search_dot($indexes, $value); diff --git a/system/Helpers/cookie_helper.php b/system/Helpers/cookie_helper.php index d85f644d..50ab3927 100755 --- a/system/Helpers/cookie_helper.php +++ b/system/Helpers/cookie_helper.php @@ -96,9 +96,8 @@ function get_cookie($index, bool $xssClean = false) $request = \Config\Services::request(); $filter = true === $xssClean ? FILTER_SANITIZE_STRING : null; - $cookie = $request->getCookie($prefix . $index, $filter); - return $cookie; + return $request->getCookie($prefix . $index, $filter); } } diff --git a/system/Helpers/date_helper.php b/system/Helpers/date_helper.php index 6cf362ad..1951d253 100644 --- a/system/Helpers/date_helper.php +++ b/system/Helpers/date_helper.php @@ -96,8 +96,7 @@ function timezone_select(string $class = '', string $default = '', int $what = \ $selected = ($timezone === $default) ? 'selected' : ''; $buffer .= "<option value='{$timezone}' {$selected}>{$timezone}</option>" . PHP_EOL; } - $buffer .= '</select>' . PHP_EOL; - return $buffer; + return $buffer . ('</select>' . PHP_EOL); } } diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 1ead233b..2556e86a 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -209,45 +209,54 @@ function delete_files(string $path, bool $del_dir = false, bool $htdocs = false, * Reads the specified directory and builds an array containing the filenames. * Any sub-folders contained within the specified path are read as well. * - * @param string $source_dir Path to source - * @param boolean $include_path Whether to include the path as part of the filename - * @param boolean $recursion Internal variable to determine recursion status - do not use in calls + * @param string $source_dir Path to source + * @param boolean|null $include_path Whether to include the path as part of the filename; false for no path, null for a relative path, true for full path + * @param boolean $hidden Whether to include hidden files (files beginning with a period) * * @return array */ - function get_filenames(string $source_dir, bool $include_path = false, bool $recursion = false): array + function get_filenames(string $source_dir, ?bool $include_path = false, bool $hidden = false): array { - static $fileData = []; + $files = []; + + $source_dir = realpath($source_dir) ?: $source_dir; + $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; try { - $fp = opendir($source_dir); - // reset the array and make sure $source_dir has a trailing slash on the initial call - if ($recursion === false) + foreach (new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($source_dir, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::SELF_FIRST + ) as $name => $object) { - $fileData = []; - $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - } + $basename = pathinfo($name, PATHINFO_BASENAME); - while (false !== ($file = readdir($fp))) - { - if (is_dir($source_dir . $file) && $file[0] !== '.') + if (! $hidden && $basename[0] === '.') { - get_filenames($source_dir . $file . DIRECTORY_SEPARATOR, $include_path, true); + continue; } - elseif ($file[0] !== '.') + elseif ($include_path === false) { - $fileData[] = ($include_path === true) ? $source_dir . $file : $file; + $files[] = $basename; + } + elseif (is_null($include_path)) + { + $files[] = str_replace($source_dir, '', $name); + } + else + { + $files[] = $name; } } - - closedir($fp); - return $fileData; } - catch (\Exception $fe) + catch (\Throwable $e) { return []; } + + sort($files); + + return $files; } } diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 82c809ce..e505e65b 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -69,6 +69,12 @@ function form_open(string $action = '', $attributes = [], array $hidden = []): s } // If an action is not a full URL then turn it into one elseif (strpos($action, '://') === false) { + // If an action has {locale} + if (strpos($action, '{locale}') !== false) + { + $action = str_replace('{locale}', Services::request()->getLocale(), $action); + } + $action = site_url($action); } @@ -180,7 +186,7 @@ function form_hidden($name, $value = '', bool $recursing = false): string if (! is_array($value)) { - $form .= '<input type="hidden" name="' . $name . '" value="' . esc($value, 'html') . "\" style=\"display:none;\" />\n"; + $form .= '<input type="hidden" name="' . $name . '" value="' . esc($value) . "\" style=\"display:none;\" />\n"; } else { @@ -408,7 +414,7 @@ function form_dropdown($data = '', $options = [], $selected = [], $extra = ''): { $sel = in_array($optgroup_key, $selected) ? ' selected="selected"' : ''; $form .= '<option value="' . htmlspecialchars($optgroup_key) . '"' . $sel . '>' - . (string) $optgroup_val . "</option>\n"; + . $optgroup_val . "</option>\n"; } $form .= "</optgroup>\n"; } @@ -416,7 +422,7 @@ function form_dropdown($data = '', $options = [], $selected = [], $extra = ''): { $form .= '<option value="' . htmlspecialchars($key) . '"' . (in_array($key, $selected) ? ' selected="selected"' : '') . '>' - . (string) $val . "</option>\n"; + . $val . "</option>\n"; } } @@ -645,9 +651,7 @@ function form_datalist(string $name, string $value, array $options): string $out .= "<option value='$option'>" . "\n"; } - $out .= '</datalist>' . "\n"; - - return $out; + return $out . ('</datalist>' . "\n"); } } @@ -741,7 +745,7 @@ function set_value(string $field, string $default = '', bool $html_escape = true $value = $request->getPost($field) ?? $default; } - return ($html_escape) ? esc($value, 'html') : $value; + return ($html_escape) ? esc($value) : $value; } } @@ -839,7 +843,7 @@ function set_checkbox(string $field, string $value = '', bool $default = false): } // Unchecked checkbox and radio inputs are not even submitted by browsers ... - if (! empty($request->getPost()) || ! empty(old($field))) + if (intval($input) === 0 || ! empty($request->getPost()) || ! empty(old($field))) { return ($input === $value) ? ' checked="checked"' : ''; } @@ -891,7 +895,7 @@ function set_radio(string $field, string $value = '', bool $default = false): st // Unchecked checkbox and radio inputs are not even submitted by browsers ... $result = ''; - if (! empty($input = $request->getPost($field)) || ! empty($input = old($field))) + if (intval($input) === 0 || ! empty($input = $request->getPost($field)) || ! empty($input = old($field))) { $result = ($input === $value) ? ' checked="checked"' : ''; } @@ -944,7 +948,7 @@ function parse_form_attributes($attributes, array $default): string { if ($key === 'value') { - $val = esc($val, 'html'); + $val = esc($val); } elseif ($key === 'name' && ! strlen($default['name'])) { diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 3084771c..373cec33 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -382,9 +382,7 @@ function video($src, string $unsupportedMessage = '', string $attributes = '', a . "\n"; } - $video .= "</video>\n"; - - return $video; + return $video . "</video>\n"; } } @@ -447,9 +445,7 @@ function audio($src, string $unsupportedMessage = '', string $attributes = '', a $audio .= "\n" . _space_indent() . $unsupportedMessage . "\n"; } - $audio .= "</audio>\n"; - - return $audio; + return $audio . "</audio>\n"; } } @@ -501,9 +497,7 @@ function _media(string $name, array $types = [], string $unsupportedMessage = '' $media .= _space_indent() . $unsupportedMessage . "\n"; } - $media .= '</' . $name . ">\n"; - - return $media; + return $media . ('</' . $name . ">\n"); } } @@ -545,9 +539,7 @@ function source(string $src, string $type = 'unknown', string $attributes = '', $source .= ' ' . $attributes; } - $source .= ' />'; - - return $source; + return $source . ' />'; } } @@ -623,9 +615,7 @@ function object(string $data, string $type = 'unknown', string $attributes = '', $object .= _space_indent() . $param . "\n"; } - $object .= "</object>\n"; - - return $object; + return $object . "</object>\n"; } } diff --git a/system/Helpers/inflector_helper.php b/system/Helpers/inflector_helper.php index cca140d2..bf869dfd 100755 --- a/system/Helpers/inflector_helper.php +++ b/system/Helpers/inflector_helper.php @@ -180,10 +180,9 @@ function plural(string $string): string */ function counted(int $count, string $string): string { - $result = "{$count} "; - $result .= $count === 1 ? singular($string) : plural($string); + $result = "{$count} "; - return $result; + return $result . ($count === 1 ? singular($string) : plural($string)); } } @@ -263,12 +262,11 @@ function underscore(string $string): string function humanize(string $string, string $separator = '_'): string { $replacement = trim($string); - $upperCased = ucwords + + return ucwords ( preg_replace('/[' . $separator . ']+/', ' ', $replacement) ); - - return $upperCased; } } diff --git a/system/Helpers/number_helper.php b/system/Helpers/number_helper.php index d1baf765..f5351980 100644 --- a/system/Helpers/number_helper.php +++ b/system/Helpers/number_helper.php @@ -179,17 +179,19 @@ function number_to_amount($num, int $precision = 0, string $locale = null) if (! function_exists('number_to_currency')) { /** - * @param float $num - * @param string $currency - * @param string $locale + * @param float $num + * @param string $currency + * @param string $locale + * @param integer $fraction * * @return string */ - function number_to_currency(float $num, string $currency, string $locale = null): string + function number_to_currency(float $num, string $currency, string $locale = null, int $fraction = null): string { return format_number($num, 1, $locale, [ 'type' => NumberFormatter::CURRENCY, 'currency' => $currency, + 'fraction' => $fraction, ]); } } @@ -217,19 +219,20 @@ function format_number(float $num, int $precision = 1, string $locale = null, ar // Type can be any of the NumberFormatter options, but provide a default. $type = (int) ($options['type'] ?? NumberFormatter::DECIMAL); - // In order to specify a precision, we'll have to modify - // the pattern used by NumberFormatter. - $pattern = '#,##0.' . str_repeat('#', $precision); - $formatter = new NumberFormatter($locale, $type); // Try to format it per the locale if ($type === NumberFormatter::CURRENCY) { + $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $options['fraction']); $output = $formatter->formatCurrency($num, $options['currency']); } else { + // In order to specify a precision, we'll have to modify + // the pattern used by NumberFormatter. + $pattern = '#,##0.' . str_repeat('#', $precision); + $formatter->setPattern($pattern); $output = $formatter->format($num); } diff --git a/system/Helpers/text_helper.php b/system/Helpers/text_helper.php index e4c5e2c6..b0288973 100755 --- a/system/Helpers/text_helper.php +++ b/system/Helpers/text_helper.php @@ -854,7 +854,7 @@ function excerpt(string $text, string $phrase = null, int $radius = 100, string $post = ' '; $count = 0; - foreach (array_reverse($pre) as $pr => $e) + foreach (array_reverse($pre) as $e) { if ((strlen($e) + $count + 1) < $radius) { @@ -865,7 +865,7 @@ function excerpt(string $text, string $phrase = null, int $radius = 100, string $count = 0; - foreach ($pos as $po => $s) + foreach ($pos as $s) { if ((strlen($s) + $count + 1) < $radius) { diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index 2959333c..ece4e38b 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -475,13 +475,11 @@ function safe_mailto(string $email, string $title = '', $attributes = ''): strin $output .= 'l[' . $i . "] = '" . $x[$i] . "';"; } - $output .= 'for (var i = l.length-1; i >= 0; i=i-1) {' + return $output . ('for (var i = l.length-1; i >= 0; i=i-1) {' . "if (l[i].substring(0, 1) === '|') document.write(\"&#\"+unescape(l[i].substring(1))+\";\");" . 'else document.write(unescape(l[i]));' . '}' - . '</script>'; - - return $output; + . '</script>'); } } @@ -603,7 +601,6 @@ function url_title(string $str, string $separator = '-', bool $lowercase = false $str = strip_tags($str); foreach ($trans as $key => $val) { - // $str = preg_replace('#'.$key.'#i'.( UTF8_ENABLED ? 'u' : ''), $val, $str); $str = preg_replace('#' . $key . '#iu', $val, $str); } diff --git a/system/Honeypot/Honeypot.php b/system/Honeypot/Honeypot.php index 406e43cf..a8d908a0 100644 --- a/system/Honeypot/Honeypot.php +++ b/system/Honeypot/Honeypot.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Honeypot; use CodeIgniter\Config\BaseConfig; +use CodeIgniter\Honeypot\Exceptions\HoneypotException; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; -use CodeIgniter\Honeypot\Exceptions\HoneypotException; /** * class Honeypot diff --git a/system/I18n/Time.php b/system/I18n/Time.php index a11d7e16..8645d355 100644 --- a/system/I18n/Time.php +++ b/system/I18n/Time.php @@ -40,12 +40,12 @@ namespace CodeIgniter\I18n; use CodeIgniter\I18n\Exceptions\I18nException; -use IntlCalendar; -use Locale; -use DateTime; use DateInterval; +use DateTime; use DateTimeZone; +use IntlCalendar; use IntlDateFormatter; +use Locale; /** * Class Time @@ -423,7 +423,7 @@ public static function hasTestNow(): bool */ public function getYear(): string { - return $this->toLocalizedString('Y'); + return $this->toLocalizedString('y'); } //-------------------------------------------------------------------- diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 990ad23c..5ec903b1 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -60,12 +60,12 @@ abstract class BaseHandler implements ImageHandlerInterface * * @var \CodeIgniter\Images\Image */ - protected $image = null; + protected $image; /** * Whether the image file has been confirmed. * - * @var bool + * @var boolean */ protected $verified = false; @@ -531,28 +531,21 @@ public function reorient(bool $silent = false) { case 2: return $this->flip('horizontal'); - break; case 3: return $this->rotate(180); - break; case 4: return $this->rotate(180) ->flip('horizontal'); - break; case 5: return $this->rotate(270) ->flip('horizontal'); - break; case 6: return $this->rotate(270); - break; case 7: return $this->rotate(90) ->flip('horizontal'); - break; case 8: return $this->rotate(90); - break; default: return $this; } diff --git a/system/Images/Handlers/GDHandler.php b/system/Images/Handlers/GDHandler.php index 0ef75aac..d57034c9 100644 --- a/system/Images/Handlers/GDHandler.php +++ b/system/Images/Handlers/GDHandler.php @@ -357,7 +357,6 @@ public function save(string $target = null, int $quality = 90): bool break; default: throw ImageException::forInvalidImageCreate(); - break; } imagedestroy($this->resource); @@ -519,7 +518,7 @@ protected function _text(string $text, array $options = []) $this->textOverlay($text, $options, true); } - $this->textOverlay($text, $options, false); + $this->textOverlay($text, $options); } //-------------------------------------------------------------------- diff --git a/system/Language/Language.php b/system/Language/Language.php index 65cf58d0..9235610f 100644 --- a/system/Language/Language.php +++ b/system/Language/Language.php @@ -149,7 +149,19 @@ public function getLine(string $line, array $args = []) $parsedLine, ] = $this->parseLine($line, $this->locale); - $output = $this->language[$this->locale][$file][$parsedLine] ?? null; + foreach (explode('.', $parsedLine) as $row) + { + if (! isset($current)) + { + $current = $this->language[$this->locale][$file] ?? null; + } + + $output = $current[$row] ?? null; + if (is_array($output)) + { + $current = $output; + } + } if ($output === null && strpos($this->locale, '-')) { diff --git a/system/Language/en/Migrations.php b/system/Language/en/Migrations.php index efbac6df..d1489273 100644 --- a/system/Language/en/Migrations.php +++ b/system/Language/en/Migrations.php @@ -35,8 +35,10 @@ 'migCreate' => "\tCreates a new migration named [name]", 'nameMigration' => 'Name the migration file', 'badCreateName' => 'You must provide a migration file name.', - 'writeError' => 'Error trying to create file.', + 'writeError' => 'Error trying to create {0} file, check if the directory is writable.', 'migNumberError' => 'Migration number must be three digits, and there must not be any gaps in the sequence.', + 'rollBackConfirm' => 'Are you sure you want to rollback?', + 'refreshConfirm' => 'Are you sure you want to refresh?', 'latest' => 'Running all new migrations...', 'generalFault' => 'Migration failed!', diff --git a/system/Language/en/Validation.php b/system/Language/en/Validation.php index 3b15241b..73a38c20 100644 --- a/system/Language/en/Validation.php +++ b/system/Language/en/Validation.php @@ -53,6 +53,7 @@ 'required' => 'The {field} field is required.', 'required_with' => 'The {field} field is required when {param} is present.', 'required_without' => 'The {field} field is required when {param} is not present.', + 'string' => 'The {field} field must be a valid string.', 'timezone' => 'The {field} field must be a valid timezone.', 'valid_base64' => 'The {field} field must be a valid base64 string.', 'valid_email' => 'The {field} field must contain a valid email address.', diff --git a/system/Log/Handlers/ChromeLoggerHandler.php b/system/Log/Handlers/ChromeLoggerHandler.php index bdcd3188..9a293e1a 100644 --- a/system/Log/Handlers/ChromeLoggerHandler.php +++ b/system/Log/Handlers/ChromeLoggerHandler.php @@ -39,7 +39,6 @@ namespace CodeIgniter\Log\Handlers; -use CodeIgniter\Events\Events; use CodeIgniter\HTTP\ResponseInterface; use Config\Services; @@ -123,7 +122,6 @@ public function __construct(array $config = []) $this->json['request_uri'] = (string) $request->uri; - Events::on('post_controller', [$this, 'sendLogs'], EVENT_PRIORITY_HIGH); } //-------------------------------------------------------------------- @@ -163,11 +161,13 @@ public function handle($level, $message): bool } $this->json['rows'][] = [ - $message, + [$message], $backtraceMessage, $type, ]; + $this->sendLogs(); + return true; } diff --git a/system/Log/Handlers/FileHandler.php b/system/Log/Handlers/FileHandler.php index 78a32186..41df0966 100644 --- a/system/Log/Handlers/FileHandler.php +++ b/system/Log/Handlers/FileHandler.php @@ -77,7 +77,7 @@ public function __construct(array $config = []) { parent::__construct($config); - $this->path = $config['path'] ?? WRITEPATH . 'logs/'; + $this->path = empty($config['path']) ? WRITEPATH . 'logs/' : $config['path']; $this->fileExtension = empty($config['fileExtension']) ? 'log' : $config['fileExtension']; $this->fileExtension = ltrim($this->fileExtension, '.'); diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 5e95ffd9..45f2eca8 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Log; -use Psr\Log\LoggerInterface; use CodeIgniter\Log\Exceptions\LogException; +use Psr\Log\LoggerInterface; /** * The CodeIgntier Logger @@ -59,13 +59,6 @@ class Logger implements LoggerInterface { - /** - * Path to save log files to. - * - * @var string - */ - protected $logPath; - /** * Used by the logThreshold Config setting to define * which errors to show. @@ -474,37 +467,51 @@ protected function interpolate($message, array $context = []) return strtr($message, $replace); } - //-------------------------------------------------------------------- - /** - * Determines the current file/line that the log method was called from. - * by analyzing the backtrace. + * Determines the file and line that the logging call + * was made from by analyzing the backtrace. + * Find the earliest stack frame that is part of our logging system. * * @return array */ public function determineFile(): array { - // Determine the file and line by finding the first - // backtrace that is not part of our logging system. - $trace = debug_backtrace(); - $file = null; - $line = null; + $logFunctions = [ + 'log_message', + 'log', + 'error', + 'debug', + 'info', + 'warning', + 'critical', + 'emergency', + 'alert', + 'notice', + ]; + + // Generate Backtrace info + $trace = \debug_backtrace(false); - foreach ($trace as $row) + // So we search from the bottom (earliest) of the stack frames + $stackFrames = \array_reverse($trace); + + // Find the first reference to a Logger class method + foreach ($stackFrames as $frame) { - if (in_array($row['function'], ['interpolate', 'determineFile', 'log', 'log_message'])) + if (\in_array($frame['function'], $logFunctions)) { - continue; + $file = isset($frame['file']) ? $this->cleanFileNames($frame['file']) : 'unknown'; + $line = $frame['line'] ?? 'unknown'; + return [ + $file, + $line, + ]; } - - $file = $row['file'] ?? isset($row['object']) ? get_class($row['object']) : 'unknown'; - $line = $row['line'] ?? $row['function'] ?? 'unknown'; - break; } return [ - $file, - $line, + 'unknown', + 'unknown', ]; } @@ -526,9 +533,8 @@ protected function cleanFileNames(string $file): string { $file = str_replace(APPPATH, 'APPPATH/', $file); $file = str_replace(SYSTEMPATH, 'SYSTEMPATH/', $file); - $file = str_replace(FCPATH, 'FCPATH/', $file); - return $file; + return str_replace(FCPATH, 'FCPATH/', $file); } //-------------------------------------------------------------------- diff --git a/system/Model.php b/system/Model.php index 8d02e782..4814217f 100644 --- a/system/Model.php +++ b/system/Model.php @@ -40,16 +40,16 @@ namespace CodeIgniter; use Closure; -use CodeIgniter\Exceptions\ModelException; -use Config\Database; -use CodeIgniter\I18n\Time; -use CodeIgniter\Pager\Pager; use CodeIgniter\Database\BaseBuilder; use CodeIgniter\Database\BaseConnection; use CodeIgniter\Database\ConnectionInterface; -use CodeIgniter\Validation\ValidationInterface; -use CodeIgniter\Database\Exceptions\DataException; use CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\Exceptions\DataException; +use CodeIgniter\Exceptions\ModelException; +use CodeIgniter\I18n\Time; +use CodeIgniter\Pager\Pager; +use CodeIgniter\Validation\ValidationInterface; +use Config\Database; use ReflectionClass; use ReflectionProperty; use stdClass; @@ -430,7 +430,7 @@ public function findColumn(string $columnName) * @param integer $limit * @param integer $offset * - * @return array|null + * @return array */ public function findAll(int $limit = 0, int $offset = 0) { @@ -470,22 +470,30 @@ public function first() { $builder->where($this->table . '.' . $this->deletedField, null); } + else + { + if ($this->useSoftDeletes === true && empty($builder->QBGroupBy) && ! empty($this->primaryKey)) + { + $builder->groupBy($this->table . '.' . $this->primaryKey); + } + } // Some databases, like PostgreSQL, need order // information to consistently return correct results. - if (empty($builder->QBOrderBy) && ! empty($this->primaryKey)) + if (! empty($builder->QBGroupBy) && empty($builder->QBOrderBy) && ! empty($this->primaryKey)) { $builder->orderBy($this->table . '.' . $this->primaryKey, 'asc'); } $row = $builder->limit(1, 0) - ->get(); + ->get(); $row = $row->getFirstRow($this->tempReturnType); $eventData = $this->trigger('afterFind', ['data' => $row]); - $this->tempReturnType = $this->returnType; + $this->tempReturnType = $this->returnType; + $this->tempUseSoftDeletes = $this->useSoftDeletes; return $eventData['data']; } @@ -493,14 +501,13 @@ public function first() //-------------------------------------------------------------------- /** - * Captures the builder's set() method so that we can validate the * data here. This allows it to be used with any of the other * builder methods and still get validated data, like replace. * - * @param mixed $key Field name, or an array of field/value pairs - * @param string $value Field value, if $key is a single field - * @param boolean $escape Whether to escape values and identifiers + * @param mixed $key Field name, or an array of field/value pairs + * @param string $value Field value, if $key is a single field + * @param boolean $escape Whether to escape values and identifiers * * @return $this */ @@ -691,7 +698,7 @@ public function insert($data = null, bool $returnID = true) // Validate data before saving. if ($this->skipValidation === false) { - if ($this->cleanRules(false)->validate($data) === false) + if ($this->cleanRules()->validate($data) === false) { return false; } @@ -759,7 +766,7 @@ public function insertBatch(array $set = null, bool $escape = null, int $batchSi { foreach ($set as $row) { - if ($this->cleanRules(false)->validate($row) === false) + if ($this->cleanRules()->validate($row) === false) { return false; } @@ -818,6 +825,12 @@ public function update($id = null, $data = null): bool $data = (array) $data; } + // If it's still empty here, means $data is no change or is empty object + if (empty($data)) + { + throw DataException::forEmptyDataset('update'); + } + // Validate data before saving. if ($this->skipValidation === false) { @@ -892,15 +905,15 @@ public function updateBatch(array $set = null, string $index = null, int $batchS * Deletes a single record from $this->table where $id matches * the table's primaryKey * - * @param integer|array|null $id The rows primary key(s) - * @param boolean $purge Allows overriding the soft deletes setting. + * @param integer|string|array|null $id The rows primary key(s) + * @param boolean $purge Allows overriding the soft deletes setting. * * @return mixed * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ public function delete($id = null, bool $purge = false) { - if (! empty($id) && is_numeric($id)) + if (! empty($id) && (is_numeric($id) || is_string($id))) { $id = [$id]; } @@ -921,7 +934,9 @@ public function delete($id = null, bool $purge = false) { throw new DatabaseException('Deletes are not allowed unless they contain a "where" or "like" clause.'); } + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } $set[$this->deletedField] = $this->setDate(); @@ -1118,10 +1133,11 @@ public function chunk(int $size, Closure $userFunc) * @param string $group Will be used by the pagination library * to identify a unique pagination set. * @param integer $page Optional page number (useful when the page number is provided in different way) + * @param integer $segment Optional URI segment number (if page number is provided by URI segment) * * @return array|null */ - public function paginate(int $perPage = 20, string $group = 'default', int $page = 0) + public function paginate(int $perPage = null, string $group = 'default', int $page = 0, int $segment = 0) { $pager = \Config\Services::pager(null, null, false); $page = $page >= 1 ? $page : $pager->getCurrentPage($group); @@ -1130,9 +1146,9 @@ public function paginate(int $perPage = 20, string $group = 'default', int $page // Store it in the Pager library so it can be // paginated in the views. - $this->pager = $pager->store($group, $page, $perPage, $total); - - $offset = ($page - 1) * $perPage; + $this->pager = $pager->store($group, $page, $perPage, $total, $segment); + $perPage = $this->pager->getPerPage($group); + $offset = ($page - 1) * $perPage; return $this->findAll($perPage, $offset); } @@ -1258,13 +1274,10 @@ protected function setDate(int $userData = null) { case 'int': return $currentDate; - break; case 'datetime': return date('Y-m-d H:i:s', $currentDate); - break; case 'date': return date('Y-m-d', $currentDate); - break; default: throw ModelException::forNoDateFormat(get_class($this)); } @@ -1426,10 +1439,6 @@ public function validate($data): bool return true; } - // Replace any placeholders (i.e. {id}) in the rules with - // the value found in $data, if exists. - $rules = $this->fillPlaceholders($rules, $data); - $this->validation->setRules($rules, $this->validationMessages); $valid = $this->validation->run($data, null, $this->DBGroup); @@ -1482,6 +1491,10 @@ protected function cleanValidationRules(array $rules, array $data = null): array * * 'required|is_unique[users,email,id,13]' * + * @codeCoverageIgnore + * + * @deprecated use fillPlaceholders($rules, $data) from Validation instead + * * @param array $rules * @param array $data * @@ -1578,8 +1591,9 @@ public function countAllResults(bool $reset = true, bool $test = false) { $this->builder()->where($this->table . '.' . $this->deletedField, null); } + $this->tempUseSoftDeletes = $this->useSoftDeletes; - return $this->builder()->countAllResults($reset, $test); + return $this->builder()->testMode($test)->countAllResults($reset); } /** @@ -1707,6 +1721,11 @@ public function __call(string $name, array $params) // and break intermingling of model and builder methods. if ($name !== 'builder' && empty($result)) { + if (! method_exists($this->builder(), $name)) + { + $className = get_class($this); + throw new \BadMethodCallException("Call to undefined method $className::$name"); + } return $result; } if ($name !== 'builder' && ! $result instanceof BaseBuilder) diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index 8aecd5f2..12a3a3ea 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -40,7 +40,6 @@ namespace CodeIgniter\Pager; use CodeIgniter\Pager\Exceptions\PagerException; -use Config\Services; use CodeIgniter\View\RendererInterface; /** @@ -145,22 +144,22 @@ public function simpleLinks(string $group = 'default', string $template = 'defau * Allows for a simple, manual, form of pagination where all of the data * is provided by the user. The URL is the current URI. * - * @param integer $page - * @param integer $perPage - * @param integer $total - * @param string $template The output template alias to render. - * @param integer $segment (if page number is provided by URI segment) + * @param integer $page + * @param integer $perPage + * @param integer $total + * @param string $template The output template alias to render. + * @param integer $segment (if page number is provided by URI segment) * - * @param string|null $group optional group (i.e. if we'd like to define custom path) + * @param string $group optional group (i.e. if we'd like to define custom path) * @return string */ - public function makeLinks(int $page, int $perPage, int $total, string $template = 'default_full', int $segment = 0, ?string $group = null): string + public function makeLinks(int $page, int $perPage = null, int $total, string $template = 'default_full', int $segment = 0, ?string $group = 'default'): string { - $name = time(); + $group = $group === '' ? 'default' : $group; - $this->store($group ?? $name, $page, $perPage, $total, $segment); + $this->store($group, $page, $perPage ?? $this->config->perPage, $total, $segment); - return $this->displayLinks($group ?? $name, $template); + return $this->displayLinks($group, $template); } //-------------------------------------------------------------------- @@ -201,16 +200,19 @@ protected function displayLinks(string $group, string $template): string * * @return $this */ - public function store(string $group, int $page, int $perPage, int $total, int $segment = 0) + public function store(string $group, int $page, int $perPage = null, int $total, int $segment = 0) { $this->segment[$group] = $segment; - $this->ensureGroup($group); + $this->ensureGroup($group, $perPage); + $perPage = $perPage ?? $this->config->perPage; + $pageCount = (int)ceil($total / $perPage); + $page = $page > $pageCount ? $pageCount : $page; $this->groups[$group]['currentPage'] = $page; $this->groups[$group]['perPage'] = $perPage; $this->groups[$group]['total'] = $total; - $this->groups[$group]['pageCount'] = (int)ceil($total / $perPage); + $this->groups[$group]['pageCount'] = $pageCount; return $this; } @@ -493,9 +495,10 @@ public function only(array $queries):Pager /** * Ensures that an array exists for the group specified. * - * @param string $group + * @param string $group + * @param integer $perPage */ - protected function ensureGroup(string $group) + protected function ensureGroup(string $group, int $perPage = null) { if (array_key_exists($group, $this->groups)) { @@ -506,7 +509,7 @@ protected function ensureGroup(string $group) 'uri' => clone current_url(true), 'hasMore' => false, 'total' => null, - 'perPage' => $this->config->perPage, + 'perPage' => $perPage ?? $this->config->perPage, 'pageCount' => 1, 'pageSelector' => $group === 'default' ? 'page' : 'page_' . $group, ]; diff --git a/system/Pager/PagerRenderer.php b/system/Pager/PagerRenderer.php index 1712ddfd..ed95c1b1 100644 --- a/system/Pager/PagerRenderer.php +++ b/system/Pager/PagerRenderer.php @@ -343,4 +343,78 @@ protected function updatePages(int $count = null) } //-------------------------------------------------------------------- + + /** + * Checks to see if there is a "previous" page before our "first" page. + * + * @return boolean + */ + public function hasPreviousPage(): bool + { + return $this->current > 1; + } + + //-------------------------------------------------------------------- + + /** + * Returns a URL to the "previous" page. + * + * You MUST call hasPreviousPage() first, or this value may be invalid. + * + * @return string|null + */ + public function getPreviousPage() + { + if (!$this->hasPreviousPage()) { + return null; + } + + $uri = clone $this->uri; + + if ($this->segment === 0) { + $uri->addQuery($this->pageSelector, $this->current - 1); + } else { + $uri->setSegment($this->segment, $this->current - 1); + } + + return (string) $uri; + } + + //-------------------------------------------------------------------- + + /** + * Checks to see if there is a "next" page after our "last" page. + * + * @return boolean + */ + public function hasNextPage(): bool + { + return $this->current < $this->last; + } + + //-------------------------------------------------------------------- + + /** + * Returns a URL to the "next" page. + * + * You MUST call hasNextPage() first, or this value may be invalid. + * + * @return string|null + */ + public function getNextPage() + { + if (!$this->hasNextPage()) { + return null; + } + + $uri = clone $this->uri; + + if ($this->segment === 0) { + $uri->addQuery($this->pageSelector, $this->current + 1); + } else { + $uri->setSegment($this->segment, $this->current + 1); + } + + return (string) $uri; + } } diff --git a/system/Pager/Views/default_full.php b/system/Pager/Views/default_full.php index cc1b8e52..ef446e99 100644 --- a/system/Pager/Views/default_full.php +++ b/system/Pager/Views/default_full.php @@ -1,4 +1,5 @@ <?php + /** * @var \CodeIgniter\Pager\PagerRenderer $pager */ @@ -16,7 +17,7 @@ </li> <li> <a href="<?= $pager->getPrevious() ?>" aria-label="<?= lang('Pager.previous') ?>"> - <span aria-hidden="true">«</span> + <span aria-hidden="true"><?= lang('Pager.previous') ?></span> </a> </li> <?php endif ?> @@ -32,7 +33,7 @@ <?php if ($pager->hasNext()) : ?> <li> <a href="<?= $pager->getNext() ?>" aria-label="<?= lang('Pager.next') ?>"> - <span aria-hidden="true">»</span> + <span aria-hidden="true"><?= lang('Pager.next') ?></span> </a> </li> <li> diff --git a/system/RESTful/ResourceController.php b/system/RESTful/ResourceController.php index 4aaec7c8..a3b75862 100644 --- a/system/RESTful/ResourceController.php +++ b/system/RESTful/ResourceController.php @@ -58,19 +58,13 @@ class ResourceController extends Controller * * @var string Name of the model class managing this resource's data */ - protected $modelName = null; + protected $modelName; /** * * @var \CodeIgniter\Model the model holding this resource's data */ - protected $model = null; - - /** - * - * @var string the representation format to return resource data in (json/xml) - */ - protected $format = 'json'; + protected $model; //-------------------------------------------------------------------- diff --git a/system/RESTful/ResourcePresenter.php b/system/RESTful/ResourcePresenter.php index 2d970e31..d594c146 100644 --- a/system/RESTful/ResourcePresenter.php +++ b/system/RESTful/ResourcePresenter.php @@ -55,13 +55,13 @@ class ResourcePresenter extends Controller * * @var string Name of the model class managing this resource's data */ - protected $modelName = null; + protected $modelName; /** * * @var \CodeIgniter\Model the model holding this resource's data */ - protected $model = null; + protected $model; //-------------------------------------------------------------------- @@ -99,7 +99,6 @@ public function show($id = null) /** * Present a view to present a new single resource object * - * @param type $id * @return string */ public function new() diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 53a27e77..cdb10074 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -38,10 +38,10 @@ namespace CodeIgniter\Router; -use CodeIgniter\HTTP\Request; -use Config\Services; use CodeIgniter\Autoloader\FileLocator; +use CodeIgniter\HTTP\Request; use CodeIgniter\Router\Exceptions\RouterException; +use Config\Services; /** * Class RouteCollection @@ -186,14 +186,14 @@ class RouteCollection implements RouteCollectionInterface * * @var string */ - protected $group = null; + protected $group; /** * The current subdomain. * * @var string */ - protected $currentSubdomain = null; + protected $currentSubdomain; /** * Stores copy of current options being @@ -201,7 +201,7 @@ class RouteCollection implements RouteCollectionInterface * * @var null */ - protected $currentOptions = null; + protected $currentOptions; /** * A little performance booster. @@ -1214,17 +1214,18 @@ public function environment(string $env, \Closure $callback): RouteCollectionInt public function reverseRoute(string $search, ...$params) { // Named routes get higher priority. - foreach ($this->routes as $verb => $collection) + foreach ($this->routes as $collection) { if (array_key_exists($search, $collection)) { - return $this->fillRouteParams(key($collection[$search]['route']), $params); + $route = $this->fillRouteParams(key($collection[$search]['route']), $params); + return $this->localizeRoute($route); } } // If it's not a named route, then loop over // all routes to find a match. - foreach ($this->routes as $verb => $collection) + foreach ($this->routes as $collection) { foreach ($collection as $route) { @@ -1256,7 +1257,8 @@ public function reverseRoute(string $search, ...$params) continue; } - return $this->fillRouteParams($from, $params); + $route = $this->fillRouteParams($from, $params); + return $this->localizeRoute($route); } } @@ -1266,6 +1268,20 @@ public function reverseRoute(string $search, ...$params) //-------------------------------------------------------------------- + /** + * Replaces the {locale} tag with the current application locale + * + * @param string $route + * + * @return string + */ + protected function localizeRoute(string $route) :string + { + return strtr($route, ['{locale}' => Services::language()->getLocale()]); + } + + //-------------------------------------------------------------------- + /** * Checks a route (using the "from") to see if it's filtered or not. * diff --git a/system/Router/Router.php b/system/Router/Router.php index 14f24c66..3c6e6e01 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Router; -use CodeIgniter\HTTP\Request; use CodeIgniter\Exceptions\PageNotFoundException; +use CodeIgniter\HTTP\Request; use CodeIgniter\Router\Exceptions\RedirectException; use CodeIgniter\Router\Exceptions\RouterException; @@ -107,21 +107,21 @@ class Router implements RouterInterface * * @var array|null */ - protected $matchedRoute = null; + protected $matchedRoute; /** * The options set for the matched route. * * @var array|null */ - protected $matchedRouteOptions = null; + protected $matchedRouteOptions; /** * The locale that was detected in a route. * * @var string */ - protected $detectedLocale = null; + protected $detectedLocale; /** * The filter info from Route Collection @@ -164,7 +164,7 @@ public function handle(string $uri = null) // If we cannot find a URI to match against, then // everything runs off of it's default settings. - if (empty($uri)) + if ($uri === null || $uri === '') { return strpos($this->controller, '\\') === false ? $this->collection->getDefaultNamespace() . $this->controller @@ -542,6 +542,32 @@ public function autoRoute(string $uri) $this->params = $segments; } + if ($this->collection->getHTTPVerb() !== 'cli') + { + $controller = '\\' . $this->collection->getDefaultNamespace(); + $controller .= $this->directory ? str_replace('/', '\\', $this->directory) : ''; + $controller .= $this->controllerName(); + $controller = strtolower($controller); + $methodName = strtolower($this->methodName()); + + foreach ($this->collection->getRoutes('cli') as $route) + { + if (is_string($route)) + { + $route = strtolower($route); + if (strpos($route, $controller . '::' . $methodName) === 0) + { + throw new PageNotFoundException(); + } + + if ($route === $controller) + { + throw new PageNotFoundException(); + } + } + } + } + // Load the file so that it's available for CodeIgniter. $file = APPPATH . 'Controllers/' . $this->directory . $this->controllerName() . '.php'; if (is_file($file)) @@ -553,7 +579,7 @@ public function autoRoute(string $uri) // We have to check for a length over 1, since by default it will be '\' if (strpos($this->controller, '\\') === false && strlen($this->collection->getDefaultNamespace()) > 1) { - $this->controller = str_replace('/', '\\', $this->collection->getDefaultNamespace() . $this->directory . $this->controllerName()); + $this->controller = '\\' . ltrim(str_replace('/', '\\', $this->collection->getDefaultNamespace() . $this->directory . $this->controllerName()), '\\'); } } @@ -603,10 +629,11 @@ protected function validateRequest(array $segments): array * @param string|null $dir * @param boolean|false $append */ - protected function setDirectory(string $dir = null, bool $append = false) + public function setDirectory(string $dir = null, bool $append = false) { if (empty($dir)) { + $this->directory = null; return; } diff --git a/system/Session/Handlers/ArrayHandler.php b/system/Session/Handlers/ArrayHandler.php index 90eb9200..15e6324e 100644 --- a/system/Session/Handlers/ArrayHandler.php +++ b/system/Session/Handlers/ArrayHandler.php @@ -39,9 +39,6 @@ namespace CodeIgniter\Session\Handlers; -use CodeIgniter\Session\Exceptions\SessionException; -use CodeIgniter\Config\BaseConfig; -use CodeIgniter\Database\BaseConnection; use Config\Database; /** diff --git a/system/Session/Handlers/DatabaseHandler.php b/system/Session/Handlers/DatabaseHandler.php index 3290a3a5..71ecbde5 100644 --- a/system/Session/Handlers/DatabaseHandler.php +++ b/system/Session/Handlers/DatabaseHandler.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Session\Handlers; -use CodeIgniter\Session\Exceptions\SessionException; use CodeIgniter\Config\BaseConfig; use CodeIgniter\Database\BaseConnection; +use CodeIgniter\Session\Exceptions\SessionException; use Config\Database; /** diff --git a/system/Session/Session.php b/system/Session/Session.php index ff40a471..8e845462 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -94,7 +94,7 @@ class Session implements SessionInterface * * @var string */ - protected $sessionSavePath = null; + protected $sessionSavePath; /** * Whether to match the user's IP address when reading the session data. @@ -199,9 +199,11 @@ public function start() { if (is_cli() && ENVIRONMENT !== 'testing') { + // @codeCoverageIgnoreStart $this->logger->debug('Session: Initialization under CLI aborted.'); return; + // @codeCoverageIgnoreEnd } elseif ((bool) ini_get('session.auto_start')) { @@ -497,13 +499,13 @@ public function set($data, $value = null) */ public function get(string $key = null) { - if (! empty($key) && ! is_null($value = dot_array_search($key, $_SESSION ?? []))) + if (! empty($key) && (! is_null($value = isset($_SESSION[$key]) ? $_SESSION[$key] : null) || ! is_null($value = dot_array_search($key, $_SESSION ?? [])))) { return $value; } elseif (empty($_SESSION)) { - return []; + return $key === null ? [] : null; } if (! empty($key)) @@ -999,7 +1001,9 @@ protected function startSession() return; } + // @codeCoverageIgnoreStart session_start(); + // @codeCoverageIgnoreEnd } //-------------------------------------------------------------------- diff --git a/system/Test/CIDatabaseTestCase.php b/system/Test/CIDatabaseTestCase.php index 1ce43a3f..b1354ec7 100644 --- a/system/Test/CIDatabaseTestCase.php +++ b/system/Test/CIDatabaseTestCase.php @@ -38,14 +38,12 @@ namespace CodeIgniter\Test; -use CodeIgniter\Config\Config; -use Config\Autoload; -use Config\Database; -use Config\Migrations; -use Config\Services; use CodeIgniter\Database\BaseConnection; use CodeIgniter\Database\MigrationRunner; use CodeIgniter\Exceptions\ConfigException; +use Config\Database; +use Config\Migrations; +use Config\Services; /** * CIDatabaseTestCase @@ -169,29 +167,13 @@ protected function setUp(): void if ($this->refresh === true) { - // Delete all of the tables to ensure we're at a clean start. - $tables = $this->db->listTables(); - - if (is_array($tables)) - { - $forge = Database::forge('tests'); - - foreach ($tables as $table) - { - if ($table === $this->db->DBPrefix . 'migrations') - { - $this->db->table($table)->truncate(); - continue; - } - - $forge->dropTable($table, true); - } - } - - // If no namespace was specified then migrate all + // If no namespace was specified then rollback/migrate all if (empty($this->namespace)) { $this->migrations->setNamespace(null); + + $this->migrations->regress(0, 'tests'); + $this->migrations->latest('tests'); } @@ -199,6 +181,13 @@ protected function setUp(): void else { $namespaces = is_array($this->namespace) ? $this->namespace : [$this->namespace]; + + foreach ($namespaces as $namespace) + { + $this->migrations->setNamespace($namespace); + $this->migrations->regress(0, 'tests'); + } + foreach ($namespaces as $namespace) { $this->migrations->setNamespace($namespace); @@ -228,8 +217,10 @@ protected function setUp(): void * Takes care of any required cleanup after the test, like * removing any rows inserted via $this->hasInDatabase() */ - public function tearDown(): void + protected function tearDown(): void { + parent::tearDown(); + if (! empty($this->insertCache)) { foreach ($this->insertCache as $row) diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index 7599d39e..6e84295b 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -39,10 +39,9 @@ namespace CodeIgniter\Test; -use Config\Paths; use CodeIgniter\Events\Events; +use Config\Paths; use PHPUnit\Framework\TestCase; -use CodeIgniter\Test\TestLogger; /** * PHPunit test case. diff --git a/system/Test/ControllerTester.php b/system/Test/ControllerTester.php index d6dc0b67..89d44319 100644 --- a/system/Test/ControllerTester.php +++ b/system/Test/ControllerTester.php @@ -41,9 +41,9 @@ namespace CodeIgniter\Test; use CodeIgniter\HTTP\IncomingRequest; -use CodeIgniter\HTTP\UserAgent; use CodeIgniter\HTTP\Response; use CodeIgniter\HTTP\URI; +use CodeIgniter\HTTP\UserAgent; use Config\App; use Config\Services; use InvalidArgumentException; @@ -251,6 +251,9 @@ public function withRequest($request) { $this->request = $request; + // Make sure it's available for other classes + Services::injectMock('request', $request); + return $this; } diff --git a/system/Test/DOMParser.php b/system/Test/DOMParser.php index 1872a684..343ef752 100644 --- a/system/Test/DOMParser.php +++ b/system/Test/DOMParser.php @@ -298,9 +298,7 @@ protected function doXPath(string $search = null, string $element, array $paths $xpath = new \DOMXPath($this->dom); - $result = $xpath->query($path); - - return $result; + return $xpath->query($path); } /** diff --git a/system/Test/FeatureTestCase.php b/system/Test/FeatureTestCase.php index 63089886..cf981df5 100644 --- a/system/Test/FeatureTestCase.php +++ b/system/Test/FeatureTestCase.php @@ -38,11 +38,11 @@ namespace CodeIgniter\Test; -use CodeIgniter\HTTP\URI; -use CodeIgniter\HTTP\Request; use CodeIgniter\Events\Events; -use CodeIgniter\HTTP\UserAgent; use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\Request; +use CodeIgniter\HTTP\URI; +use CodeIgniter\HTTP\UserAgent; use Config\App; use Config\Services; @@ -151,6 +151,15 @@ public function skipEvents() */ public function call(string $method, string $path, array $params = null) { + // Clean up any open output buffers + // not relevant to unit testing + // @codeCoverageIgnoreStart + if (\ob_get_level() > 0 && $this->clean) + { + \ob_end_clean(); + } + // @codeCoverageIgnoreEnd + // Simulate having a blank session $_SESSION = []; $_SERVER['REQUEST_METHOD'] = $method; @@ -178,19 +187,10 @@ public function call(string $method, string $path, array $params = null) $response->setBody($output); } - // Clean up any open output buffers - // not relevant to unit testing - // @codeCoverageIgnoreStart - - if (ob_get_level() > 0 && $this->clean) - { - ob_end_clean(); - } - // @codeCoverageIgnoreEnd - - $featureResponse = new FeatureResponse($response); + // Reset directory if it has been set + Services::router()->setDirectory(null); - return $featureResponse; + return new FeatureResponse($response); } /** diff --git a/system/Test/Mock/MockChromeLogger.php b/system/Test/Mock/MockChromeLogger.php deleted file mode 100644 index 3bb64b19..00000000 --- a/system/Test/Mock/MockChromeLogger.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php namespace CodeIgniter\Test\Mock; - -/** - * Class MockHandler - * - * Extends ChromeLoggerHandler, exposing some inner workings - */ - -class MockChromeLogger extends \CodeIgniter\Log\Handlers\ChromeLoggerHandler -{ - - //-------------------------------------------------------------------- - - public function __construct(array $config) - { - parent::__construct($config); - } - - // retrieve the message from the JSON response - public function peekaboo() - { - return $this->json['rows'][0]; - } - -} diff --git a/system/Test/Mock/MockConnection.php b/system/Test/Mock/MockConnection.php index e909ce6b..ba112fa1 100644 --- a/system/Test/Mock/MockConnection.php +++ b/system/Test/Mock/MockConnection.php @@ -255,7 +255,6 @@ protected function _foreignKeyData(string $table): array */ protected function _close() { - return; } //-------------------------------------------------------------------- diff --git a/system/Test/Mock/MockEmail.php b/system/Test/Mock/MockEmail.php new file mode 100644 index 00000000..fdbd14f4 --- /dev/null +++ b/system/Test/Mock/MockEmail.php @@ -0,0 +1,24 @@ +<?php namespace CodeIgniter\Test\Mock; + +use CodeIgniter\Email\Email; + +class MockEmail extends Email +{ + /** + * Record of mock emails sent. + * + * @var array + */ + public $archive = []; + + public function send($autoClear = true) + { + if ($autoClear) + { + $this->clear(); + } + + $this->archive = get_object_vars($this); + return true; + } +} diff --git a/system/Test/Mock/MockLogger.php b/system/Test/Mock/MockLogger.php index 837c3661..239dab59 100644 --- a/system/Test/Mock/MockLogger.php +++ b/system/Test/Mock/MockLogger.php @@ -33,16 +33,6 @@ class MockLogger public $threshold = 9; - /* - |-------------------------------------------------------------------------- - | Error Logging Directory Path - |-------------------------------------------------------------------------- - | - | - | - */ - public $path = ''; - /* |-------------------------------------------------------------------------- | Date Format for Logs @@ -97,6 +87,11 @@ class MockLogger 'notice', 'warning', ], + + /* + * Logging Directory Path + */ + 'path' => '', ], ]; diff --git a/system/Test/Mock/MockSecurity.php b/system/Test/Mock/MockSecurity.php index 4bce37c9..f9d5c9cf 100644 --- a/system/Test/Mock/MockSecurity.php +++ b/system/Test/Mock/MockSecurity.php @@ -1,7 +1,7 @@ <?php namespace CodeIgniter\Test\Mock; -use CodeIgniter\Security\Security; use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\Security\Security; class MockSecurity extends Security { diff --git a/system/Test/ReflectionHelper.php b/system/Test/ReflectionHelper.php index 5bdfa984..c6c1005f 100644 --- a/system/Test/ReflectionHelper.php +++ b/system/Test/ReflectionHelper.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Test; +use ReflectionClass; use ReflectionMethod; use ReflectionObject; -use ReflectionClass; /** * Testing helper. diff --git a/system/Test/bootstrap.php b/system/Test/bootstrap.php index 8fde5408..83585f37 100644 --- a/system/Test/bootstrap.php +++ b/system/Test/bootstrap.php @@ -13,15 +13,16 @@ $paths = new Config\Paths(); // Define necessary framework path constants -defined('APPPATH') || define('APPPATH', realpath($paths->appDirectory) . DIRECTORY_SEPARATOR); -defined('WRITEPATH') || define('WRITEPATH', realpath($paths->writableDirectory) . DIRECTORY_SEPARATOR); -defined('SYSTEMPATH') || define('SYSTEMPATH', realpath($paths->systemDirectory) . DIRECTORY_SEPARATOR); -defined('ROOTPATH') || define('ROOTPATH', realpath(APPPATH . '../') . DIRECTORY_SEPARATOR); -defined('CIPATH') || define('CIPATH', realpath(SYSTEMPATH . '../') . DIRECTORY_SEPARATOR); -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('APPPATH') || define('APPPATH', realpath($paths->appDirectory) . DIRECTORY_SEPARATOR); +defined('WRITEPATH') || define('WRITEPATH', realpath($paths->writableDirectory) . DIRECTORY_SEPARATOR); +defined('SYSTEMPATH') || define('SYSTEMPATH', realpath($paths->systemDirectory) . DIRECTORY_SEPARATOR); +defined('ROOTPATH') || define('ROOTPATH', realpath(APPPATH . '../') . DIRECTORY_SEPARATOR); +defined('CIPATH') || define('CIPATH', realpath(SYSTEMPATH . '../') . DIRECTORY_SEPARATOR); +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('VENDORPATH') || define('VENDORPATH', realpath(HOMEPATH . 'vendor') . DIRECTORY_SEPARATOR); // Load Common.php from App then System if (file_exists(APPPATH . 'Common.php')) @@ -58,3 +59,6 @@ class_alias('Config\Services', 'CodeIgniter\Services'); // Register the loader with the SPL autoloader stack. $loader->register(); + +require_once APPPATH . 'Config/Routes.php'; +$routes->getRoutes('*'); diff --git a/system/ThirdParty/Kint/kint.php b/system/ThirdParty/Kint/Kint.php similarity index 100% rename from system/ThirdParty/Kint/kint.php rename to system/ThirdParty/Kint/Kint.php diff --git a/system/Validation/CreditCardRules.php b/system/Validation/CreditCardRules.php index bca1d695..53234e9e 100644 --- a/system/Validation/CreditCardRules.php +++ b/system/Validation/CreditCardRules.php @@ -205,11 +205,10 @@ class CreditCardRules * * @param string $ccNumber * @param string $type - * @param array $data * * @return boolean */ - public function valid_cc_number(string $ccNumber = null, string $type, array $data): bool + public function valid_cc_number(string $ccNumber = null, string $type): bool { $type = strtolower($type); $info = null; diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index 6e7a7d7c..c9290887 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -79,11 +79,10 @@ public function __construct(RequestInterface $request = null) * * @param string $blank * @param string $name - * @param array $data * * @return boolean */ - public function uploaded(string $blank = null, string $name, array $data): bool + public function uploaded(string $blank = null, string $name): bool { $file = $this->request->getFile($name); @@ -110,11 +109,10 @@ public function uploaded(string $blank = null, string $name, array $data): bool * * @param string|null $blank * @param string $params - * @param array $data * * @return boolean */ - public function max_size(string $blank = null, string $params, array $data): bool + public function max_size(string $blank = null, string $params): bool { // Grab the file name off the top of the $params // after we split it. @@ -155,11 +153,10 @@ public function max_size(string $blank = null, string $params, array $data): boo * * @param string|null $blank * @param string $params - * @param array $data * * @return boolean */ - public function is_image(string $blank = null, string $params, array $data): bool + public function is_image(string $blank = null, string $params): bool { // Grab the file name off the top of the $params // after we split it. @@ -203,11 +200,10 @@ public function is_image(string $blank = null, string $params, array $data): boo * * @param string|null $blank * @param string $params - * @param array $data * * @return boolean */ - public function mime_in(string $blank = null, string $params, array $data): bool + public function mime_in(string $blank = null, string $params): bool { // Grab the file name off the top of the $params // after we split it. @@ -247,11 +243,10 @@ public function mime_in(string $blank = null, string $params, array $data): bool * * @param string|null $blank * @param string $params - * @param array $data * * @return boolean */ - public function ext_in(string $blank = null, string $params, array $data): bool + public function ext_in(string $blank = null, string $params): bool { // Grab the file name off the top of the $params // after we split it. @@ -292,11 +287,10 @@ public function ext_in(string $blank = null, string $params, array $data): bool * * @param string|null $blank * @param string $params - * @param array $data * * @return boolean */ - public function max_dims(string $blank = null, string $params, array $data): bool + public function max_dims(string $blank = null, string $params): bool { // Grab the file name off the top of the $params // after we split it. diff --git a/system/Validation/FormatRules.php b/system/Validation/FormatRules.php index d143c6f3..f121b702 100644 --- a/system/Validation/FormatRules.php +++ b/system/Validation/FormatRules.php @@ -152,7 +152,7 @@ public function string($str = null): bool */ public function decimal(string $str = null): bool { - return (bool) preg_match('/^[\-+]?[0-9]+(|\.[0-9]+)$/', $str); + return (bool) preg_match('/^[-+]?[0-9]{0,}\.?[0-9]+$/', $str); } /** @@ -218,11 +218,10 @@ public function numeric(string $str = null): bool * * @param string $str * @param string $pattern - * @param array $data Other field/value pairs * * @return boolean */ - public function regex_match(string $str = null, string $pattern, array $data): bool + public function regex_match(string $str = null, string $pattern): bool { if (strpos($pattern, '/') !== 0) { @@ -329,8 +328,8 @@ public function valid_emails(string $str = null): bool * @return boolean */ public function valid_ip(string $ip = null, string $which = null): bool - { - if(empty($ip)) + { + if (empty($ip)) { return false; } @@ -347,7 +346,7 @@ public function valid_ip(string $ip = null, string $which = null): bool break; } - return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which) || (!ctype_print($ip) && (bool) filter_var(inet_ntop($ip), FILTER_VALIDATE_IP, $which)); + return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which) || (! ctype_print($ip) && (bool) filter_var(inet_ntop($ip), FILTER_VALIDATE_IP, $which)); } /** diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index fb09e7b4..371634b4 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -61,7 +61,7 @@ class Rules */ public function differs(string $str = null, string $field, array $data): bool { - return array_key_exists($field, $data) ? ($str !== $data[$field]) : false; + return array_key_exists($field, $data) && $str !== $data[$field]; } //-------------------------------------------------------------------- @@ -87,11 +87,10 @@ public function equals(string $str = null, string $val): bool * * @param string $str * @param string $val - * @param array $data * * @return boolean */ - public function exact_length(string $str = null, string $val, array $data): bool + public function exact_length(string $str = null, string $val): bool { $val = explode(',', $val); foreach ($val as $tmp) @@ -112,13 +111,12 @@ public function exact_length(string $str = null, string $val, array $data): bool * * @param string $str * @param string $min - * @param array $data * * @return boolean */ - public function greater_than(string $str = null, string $min, array $data): bool + public function greater_than(string $str = null, string $min): bool { - return is_numeric($str) ? ($str > $min) : false; + return is_numeric($str) && $str > $min; } //-------------------------------------------------------------------- @@ -128,13 +126,12 @@ public function greater_than(string $str = null, string $min, array $data): bool * * @param string $str * @param string $min - * @param array $data * * @return boolean */ - public function greater_than_equal_to(string $str = null, string $min, array $data): bool + public function greater_than_equal_to(string $str = null, string $min): bool { - return is_numeric($str) ? ($str >= $min) : false; + return is_numeric($str) && $str >= $min; } //-------------------------------------------------------------------- @@ -183,12 +180,12 @@ public function is_not_unique(string $str = null, string $field, array $data): b /** * Value should be within an array of values * - * @param string $value - * @param string $list - * @param array $data + * @param string $value + * @param string $list + * * @return boolean */ - public function in_list(string $value = null, string $list, array $data): bool + public function in_list(string $value = null, string $list): bool { $list = explode(',', $list); $list = array_map(function ($value) { @@ -250,7 +247,7 @@ public function is_unique(string $str = null, string $field, array $data): bool */ public function less_than(string $str = null, string $max): bool { - return is_numeric($str) ? ($str < $max) : false; + return is_numeric($str) && $str < $max; } //-------------------------------------------------------------------- @@ -265,7 +262,7 @@ public function less_than(string $str = null, string $max): bool */ public function less_than_equal_to(string $str = null, string $max): bool { - return is_numeric($str) ? ($str <= $max) : false; + return is_numeric($str) && $str <= $max; } //-------------------------------------------------------------------- @@ -281,7 +278,7 @@ public function less_than_equal_to(string $str = null, string $max): bool */ public function matches(string $str = null, string $field, array $data): bool { - return array_key_exists($field, $data) ? ($str === $data[$field]) : false; + return array_key_exists($field, $data) && $str === $data[$field]; } //-------------------------------------------------------------------- @@ -291,11 +288,10 @@ public function matches(string $str = null, string $field, array $data): bool * * @param string $str * @param string $val - * @param array $data * * @return boolean */ - public function max_length(string $str = null, string $val, array $data): bool + public function max_length(string $str = null, string $val): bool { return ($val >= mb_strlen($str)); } @@ -307,11 +303,10 @@ public function max_length(string $str = null, string $val, array $data): bool * * @param string $str * @param string $val - * @param array $data * * @return boolean */ - public function min_length(string $str = null, string $val, array $data): bool + public function min_length(string $str = null, string $val): bool { return ($val <= mb_strlen($str)); } diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index 484b1e6e..e4256083 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -155,6 +155,10 @@ public function run(array $data = null, string $group = null, string $db_group = return false; } + // Replace any placeholders (i.e. {id}) in the rules with + // the value found in $data, if exists. + $this->rules = $this->fillPlaceholders($this->rules, $data); + // Need this for searching arrays in validation. helper('array'); @@ -170,9 +174,20 @@ public function run(array $data = null, string $group = null, string $db_group = $rules = $this->splitRules($rules); } - $value = dot_array_search($rField, $data); + $value = dot_array_search($rField, $data); + $fieldNameToken = explode('.', $rField); - $this->processRules($rField, $rSetup['label'] ?? $rField, $value ?? null, $rules, $data); + if (is_array($value) && end($fieldNameToken) === '*') + { + foreach ($value as $val) + { + $this->processRules($rField, $rSetup['label'] ?? $rField, $val ?? null, $rules, $data); + } + } + else + { + $this->processRules($rField, $rSetup['label'] ?? $rField, $value ?? null, $rules, $data); + } } return ! empty($this->getErrors()) ? false : true; @@ -474,8 +489,8 @@ public function getRuleGroup(string $group): array */ public function setRuleGroup(string $group) { - $rules = $this->getRuleGroup($group); - $this->rules = $rules; + $rules = $this->getRuleGroup($group); + $this->setRules($rules); $errorName = $group . '_errors'; if (isset($this->config->$errorName)) @@ -592,6 +607,64 @@ public function loadRuleGroup(string $group = null) return $this->rules; } + //-------------------------------------------------------------------- + + /** + * Replace any placeholders within the rules with the values that + * match the 'key' of any properties being set. For example, if + * we had the following $data array: + * + * [ 'id' => 13 ] + * + * and the following rule: + * + * 'required|is_unique[users,email,id,{id}]' + * + * The value of {id} would be replaced with the actual id in the form data: + * + * 'required|is_unique[users,email,id,13]' + * + * @param array $rules + * @param array $data + * + * @return array + */ + protected function fillPlaceholders(array $rules, array $data): array + { + $replacements = []; + + foreach ($data as $key => $value) + { + $replacements["{{$key}}"] = $value; + } + + if (! empty($replacements)) + { + foreach ($rules as &$rule) + { + if (is_array($rule)) + { + foreach ($rule as &$row) + { + // Should only be an `errors` array + // which doesn't take placeholders. + if (is_array($row)) + { + continue; + } + + $row = strtr($row, $replacements); + } + continue; + } + + $rule = strtr($rule, $replacements); + } + } + + return $rules; + } + //-------------------------------------------------------------------- //-------------------------------------------------------------------- // Errors @@ -698,7 +771,7 @@ protected function getErrorMessage(string $rule, string $field, string $label = // Check if custom message has been defined by user if (isset($this->customErrors[$field][$rule])) { - $message = $this->customErrors[$field][$rule]; + $message = lang($this->customErrors[$field][$rule]); } else { @@ -708,11 +781,10 @@ protected function getErrorMessage(string $rule, string $field, string $label = $message = lang('Validation.' . $rule); } - $message = str_replace('{field}', $label ?? $field, $message); - $message = str_replace('{param}', $this->rules[$param]['label'] ?? $param, $message); - $message = str_replace('{value}', $value, $message); + $message = str_replace('{field}', empty($label) ? $field : lang($label), $message); + $message = str_replace('{param}', empty($this->rules[$param]['label']) ? $param : lang($this->rules[$param]['label']), $message); - return $message; + return str_replace('{value}', $value, $message); } /** diff --git a/system/View/Cell.php b/system/View/Cell.php index a7e79e9c..d3cc6bc3 100644 --- a/system/View/Cell.php +++ b/system/View/Cell.php @@ -40,8 +40,8 @@ use CodeIgniter\Cache\CacheInterface; use CodeIgniter\View\Exceptions\ViewException; -use ReflectionMethod; use Config\Services; +use ReflectionMethod; /** * Class Cell @@ -110,7 +110,9 @@ public function render(string $library, $params = null, int $ttl = 0, string $ca list($class, $method) = $this->determineClass($library); // Is it cached? - $cacheName = ! empty($cacheName) ? $cacheName : $class . $method . md5(serialize($params)); + $cacheName = ! empty($cacheName) + ? $cacheName + : str_replace(['\\', '/'], '', $class) . $method . md5(serialize($params)); if (! empty($this->cache) && $output = $this->cache->get($cacheName)) { diff --git a/system/View/Filters.php b/system/View/Filters.php index ad18ec8a..8d8ed743 100644 --- a/system/View/Filters.php +++ b/system/View/Filters.php @@ -256,16 +256,18 @@ public static function local_number($value, string $type = 'decimal', int $preci * @param $value * @param string $currency * @param string|null $locale + * @param integer $fraction * * @return string */ - public static function local_currency($value, string $currency, string $locale = null): string + public static function local_currency($value, string $currency, string $locale = null, $fraction = null): string { helper('number'); $options = [ 'type' => NumberFormatter::CURRENCY, 'currency' => $currency, + 'fraction' => $fraction, ]; return format_number($value, 2, $locale, $options); @@ -330,13 +332,10 @@ public static function round(string $value, $precision = 2, string $type = 'comm { case 'common': return round($value, $precision); - break; case 'ceil': return ceil($value); - break; case 'floor': return floor($value); - break; } // Still here, just return the value. diff --git a/system/View/Parser.php b/system/View/Parser.php index 1e7bc30c..d5b0de13 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -39,8 +39,8 @@ namespace CodeIgniter\View; -use CodeIgniter\Log\Logger; use CodeIgniter\View\Exceptions\ViewException; +use Psr\Log\LoggerInterface; /** * Class Parser @@ -93,13 +93,13 @@ class Parser extends View /** * Constructor * - * @param \Config\View $config - * @param string $viewPath - * @param mixed $loader - * @param boolean $debug - * @param Logger $logger + * @param \Config\View $config + * @param string $viewPath + * @param mixed $loader + * @param boolean $debug + * @param LoggerInterface $logger */ - public function __construct($config, string $viewPath = null, $loader = null, bool $debug = null, Logger $logger = null) + public function __construct($config, string $viewPath = null, $loader = null, bool $debug = null, LoggerInterface $logger = null) { // Ensure user plugins override core plugins. $this->plugins = $config->plugins ?? []; @@ -157,20 +157,25 @@ public function render(string $view, array $options = null, bool $saveData = nul throw ViewException::forInvalidFile($file); } + if (is_null($this->tempData)) + { + $this->tempData = $this->data; + } + $template = file_get_contents($file); - $output = $this->parse($template, $this->data, $options); + $output = $this->parse($template, $this->tempData, $options); $this->logPerformance($start, microtime(true), $view); - if (! $saveData) + if ($saveData) { - $this->data = []; + $this->data = $this->tempData; } // Should we cache? if (isset($options['cache'])) { cache()->save($cacheName, $output, (int) $options['cache']); } - + $this->tempData = null; return $output; } @@ -196,14 +201,22 @@ public function renderString(string $template, array $options = null, bool $save $saveData = $this->config->saveData; } - $output = $this->parse($template, $this->data, $options); + if (is_null($this->tempData)) + { + $this->tempData = $this->data; + } + + $output = $this->parse($template, $this->tempData, $options); $this->logPerformance($start, microtime(true), $this->excerpt($template)); - if (! $saveData) + if ($saveData) { - $this->data = []; + $this->data = $this->tempData; } + + $this->tempData = null; + return $output; } @@ -243,7 +256,8 @@ public function setData(array $data = [], string $context = null): RendererInter } } - $this->data = array_merge($this->data, $data); + $this->tempData = $this->tempData ?? $this->data; + $this->tempData = array_merge($this->tempData, $data); return $this; } @@ -256,9 +270,10 @@ public function setData(array $data = [], string $context = null): RendererInter * Parses pseudo-variables contained in the specified template, * replacing them with the data in the second param * - * @param string $template - * @param array $data - * @param array $options Future options + * @param string $template + * @param array $data + * @param array $options Future options + * * @return string */ protected function parse(string $template, array $data = [], array $options = null): string @@ -304,9 +319,7 @@ protected function parse(string $template, array $data = [], array $options = nu } } - $template = $this->insertNoparse($template); - - return $template; + return $this->insertNoparse($template); } //-------------------------------------------------------------------- @@ -320,7 +333,7 @@ protected function parse(string $template, array $data = [], array $options = nu */ protected function parseSingle(string $key, string $val): array { - $pattern = '#' . $this->leftDelimiter . '!?\s*' . preg_quote($key) . '\s*\|*\s*([|a-zA-Z0-9<>=\(\),:_\-\s\+]+)*\s*!?' . $this->rightDelimiter . '#ms'; + $pattern = '#' . $this->leftDelimiter . '!?\s*' . preg_quote($key) . '\s*\|*\s*([|\w<>=\(\),:.\-\s\+\\\\/]+)*\s*!?' . $this->rightDelimiter . '#ms'; return [$pattern => $val]; } @@ -402,7 +415,7 @@ protected function parsePair(string $variable, array $data, string $template): a $val = 'Resource'; } - $temp['#' . $this->leftDelimiter . '!?\s*' . preg_quote($key) . '\s*\|*\s*([|\w<>=\(\),:_\-\s\+]+)*\s*!?' . $this->rightDelimiter . '#s'] = $val; + $temp['#' . $this->leftDelimiter . '!?\s*' . preg_quote($key) . '\s*\|*\s*([|\w<>=\(\),:.\-\s\+\\\\/]+)*\s*!?' . $this->rightDelimiter . '#s'] = $val; } // Now replace our placeholders with the new content. @@ -533,7 +546,14 @@ protected function parseConditionals(string $template): string // Parse the PHP itself, or insert an error so they can debug ob_start(); - extract($this->data); + + if (is_null($this->tempData)) + { + $this->tempData = $this->data; + } + + extract($this->tempData); + try { eval('?>' . $template . '<?php '); @@ -543,6 +563,7 @@ protected function parseConditionals(string $template): string ob_end_clean(); throw ViewException::forTagSyntaxError(str_replace(['?>', '<?php '], '', $template)); } + return ob_get_clean(); } @@ -621,9 +642,7 @@ protected function prepareReplacement(array $matches, string $replace, bool $esc } } - $replace = $this->applyFilters($replace, $filters); - - return $replace; + return $this->applyFilters($replace, $filters); } //-------------------------------------------------------------------- @@ -686,7 +705,7 @@ protected function applyFilters(string $replace, array $filters): string foreach ($filters as $filter) { // Grab any parameter we might need to send - preg_match('/\([a-zA-Z0-9\-:_ +,<>=]+\)/', $filter, $param); + preg_match('/\([\w<>=\/\\\,:.\-\s\+]+\)/', $filter, $param); // Remove the () and spaces to we have just the parameter left $param = ! empty($param) ? trim($param[0], '() ') : null; diff --git a/system/View/Plugins.php b/system/View/Plugins.php index 30095fa8..bc883349 100644 --- a/system/View/Plugins.php +++ b/system/View/Plugins.php @@ -48,11 +48,9 @@ class Plugins /** * Wrap helper function to use as view plugin. * - * @param array $params - * * @return string|\CodeIgniter\HTTP\URI */ - public static function currentURL(array $params = []) + public static function currentURL() { return current_url(); } @@ -62,11 +60,9 @@ public static function currentURL(array $params = []) /** * Wrap helper function to use as view plugin. * - * @param array $params - * * @return \CodeIgniter\HTTP\URI|mixed|string */ - public static function previousURL(array $params = []) + public static function previousURL() { return previous_url(); } diff --git a/system/View/Table.php b/system/View/Table.php index 1aaef57a..14471323 100644 --- a/system/View/Table.php +++ b/system/View/Table.php @@ -87,14 +87,14 @@ class Table * * @var string */ - public $caption = null; + public $caption; /** * Table layout template * * @var array */ - public $template = null; + public $template; /** * Newline setting @@ -115,7 +115,7 @@ class Table * * @var function */ - public $function = null; + public $function; /** * Set the template from the table config file if it exists @@ -158,10 +158,9 @@ public function setTemplate($template) * * Can be passed as an array or discreet params * - * @param mixed * @return Table */ - public function setHeading($args = []) + public function setHeading() { $this->heading = $this->_prepArgs(func_get_args()); return $this; @@ -172,10 +171,9 @@ public function setHeading($args = []) * * Can be passed as an array or discreet params * - * @param mixed * @return Table */ - public function setFooting($args = []) + public function setFooting() { $this->footing = $this->_prepArgs(func_get_args()); return $this; @@ -252,10 +250,9 @@ public function setEmpty($value) * * Can be passed as an array or discreet params * - * @param mixed * @return Table */ - public function addRow($args = []) + public function addRow() { $this->rows[] = $this->_prepArgs(func_get_args()); return $this; diff --git a/system/View/View.php b/system/View/View.php index 01241dd6..ff138dd7 100644 --- a/system/View/View.php +++ b/system/View/View.php @@ -58,6 +58,11 @@ class View implements RendererInterface */ protected $data = []; + /** + * Merge savedData and userData + */ + protected $tempData = null; + /** * The base directory to look in for our Views. * @@ -189,11 +194,10 @@ public function render(string $view, array $options = null, bool $saveData = nul // Store the results here so even if // multiple views are called in a view, it won't // clean it unless we mean it to. - if ($saveData !== null) + if (is_null($saveData)) { - $this->saveData = $saveData; + $saveData = $this->saveData; } - $fileExt = pathinfo($view, PATHINFO_EXTENSION); $realPath = empty($fileExt) ? $view . '.php' : $view; // allow Views as .html, .tpl, etc (from CI3) $this->renderVars['view'] = $realPath; @@ -225,11 +229,17 @@ public function render(string $view, array $options = null, bool $saveData = nul } // Make our view data available to the view. - extract($this->data); - if (! $this->saveData) + if (is_null($this->tempData)) { - $this->data = []; + $this->tempData = $this->data; + } + + extract($this->tempData); + + if ($saveData) + { + $this->data = $this->tempData; } ob_start(); @@ -277,6 +287,8 @@ public function render(string $view, array $options = null, bool $saveData = nul cache()->save($this->renderVars['cacheName'], $output, (int) $this->renderVars['options']['cache']); } + $this->tempData = null; + return $output; } @@ -300,16 +312,22 @@ public function render(string $view, array $options = null, bool $saveData = nul public function renderString(string $view, array $options = null, bool $saveData = null): string { $start = microtime(true); + if (is_null($saveData)) { - $saveData = $this->config->saveData; + $saveData = $this->saveData; } - extract($this->data); + if (is_null($this->tempData)) + { + $this->tempData = $this->data; + } + + extract($this->tempData); - if (! $saveData) + if ($saveData) { - $this->data = []; + $this->data = $this->tempData; } ob_start(); @@ -320,6 +338,8 @@ public function renderString(string $view, array $options = null, bool $saveData $this->logPerformance($start, microtime(true), $this->excerpt($view)); + $this->tempData = null; + return $output; } @@ -355,7 +375,8 @@ public function setData(array $data = [], string $context = null): RendererInter $data = \esc($data, $context); } - $this->data = array_merge($this->data, $data); + $this->tempData = $this->tempData ?? $this->data; + $this->tempData = array_merge($this->tempData, $data); return $this; } @@ -379,7 +400,8 @@ public function setVar(string $name, $value = null, string $context = null): Ren $value = \esc($value, $context); } - $this->data[$name] = $value; + $this->tempData = $this->tempData ?? $this->data; + $this->tempData[$name] = $value; return $this; } @@ -407,7 +429,7 @@ public function resetData(): RendererInterface */ public function getData(): array { - return $this->data; + return is_null($this->tempData) ? $this->data : $this->tempData; } //-------------------------------------------------------------------- diff --git a/system/bootstrap.php b/system/bootstrap.php index 33ab4559..e014ad05 100644 --- a/system/bootstrap.php +++ b/system/bootstrap.php @@ -139,6 +139,16 @@ class_alias('Config\Services', 'CodeIgniter\Services'); // Now load Composer's if it's available if (is_file(COMPOSER_PATH)) { + /** + * The path to the vendor directory. + * + * We do not want to enforce this, so set the constant if Composer was used. + */ + if (! defined('VENDORPATH')) + { + define('VENDORPATH', realpath(ROOTPATH . 'vendor') . DIRECTORY_SEPARATOR); + } + require_once COMPOSER_PATH; } diff --git a/tests/_support/Autoloader/UnnamespacedClass.php b/tests/_support/Autoloader/UnnamespacedClass.php deleted file mode 100644 index 67b1ed35..00000000 --- a/tests/_support/Autoloader/UnnamespacedClass.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -class UnnamespacedClass -{ -} diff --git a/tests/_support/CIDatabaseTestCase.php b/tests/_support/CIDatabaseTestCase.php deleted file mode 100644 index 1fc722b4..00000000 --- a/tests/_support/CIDatabaseTestCase.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -use CodeIgniter\Test; - -class CIDatabaseTestCase extends Test\CIDatabaseTestCase -{ - -} diff --git a/tests/_support/CIUnitTestCase.php b/tests/_support/CIUnitTestCase.php deleted file mode 100644 index b475b62e..00000000 --- a/tests/_support/CIUnitTestCase.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -use CodeIgniter\Test; - -class CIUnitTestCase extends Test\CIUnitTestCase -{ - -} diff --git a/tests/_support/Cache/Handlers/MockHandler.php b/tests/_support/Cache/Handlers/MockHandler.php deleted file mode 100644 index 5295178f..00000000 --- a/tests/_support/Cache/Handlers/MockHandler.php +++ /dev/null @@ -1,198 +0,0 @@ -<?php namespace Tests\Support\Cache\Handlers; - -use CodeIgniter\Cache\CacheInterface; - -class MockHandler implements CacheInterface -{ - /** - * Prefixed to all cache names. - * - * @var string - */ - protected $prefix; - - /** - * Mock cache storage. - * - * @var array - */ - protected $cache = []; - - //-------------------------------------------------------------------- - - /** - * Takes care of any handler-specific setup that must be done. - */ - public function initialize() - { - // Not to see here... - } - - //-------------------------------------------------------------------- - - /** - * Attempts to fetch an item from the cache store. - * - * @param string $key Cache item name - * - * @return mixed - */ - public function get(string $key) - { - $key = $this->prefix . $key; - - return array_key_exists($key, $this->cache) - ? $this->cache[$key] - : null; - } - - //-------------------------------------------------------------------- - - /** - * Saves an item to the cache store. - * - * The $raw parameter is only utilized by Mamcache in order to - * allow usage of increment() and decrement(). - * - * @param string $key Cache item name - * @param $value the data to save - * @param null $ttl Time To Live, in seconds (default 60) - * @param boolean $raw Whether to store the raw value. - * - * @return mixed - */ - public function save(string $key, $value, int $ttl = 60, bool $raw = false) - { - $key = $this->prefix . $key; - - $this->cache[$key] = $value; - - return true; - } - - //-------------------------------------------------------------------- - - /** - * Deletes a specific item from the cache store. - * - * @param string $key Cache item name - * - * @return mixed - */ - public function delete(string $key) - { - unset($this->cache[$key]); - } - - //-------------------------------------------------------------------- - - /** - * Performs atomic incrementation of a raw stored value. - * - * @param string $key Cache ID - * @param integer $offset Step/value to increase by - * - * @return mixed - */ - public function increment(string $key, int $offset = 1) - { - $key = $this->prefix . $key; - - $data = $this->cache[$key] ?: null; - - if (empty($data)) - { - $data = 0; - } - elseif (! is_int($data)) - { - return false; - } - - return $this->save($key, $data + $offset); - } - - //-------------------------------------------------------------------- - - /** - * Performs atomic decrementation of a raw stored value. - * - * @param string $key Cache ID - * @param integer $offset Step/value to increase by - * - * @return mixed - */ - public function decrement(string $key, int $offset = 1) - { - $key = $this->prefix . $key; - - $data = $this->cache[$key] ?: null; - - if (empty($data)) - { - $data = 0; - } - elseif (! is_int($data)) - { - return false; - } - - return $this->save($key, $data - $offset); - } - - //-------------------------------------------------------------------- - - /** - * Will delete all items in the entire cache. - * - * @return mixed - */ - public function clean() - { - $this->cache = []; - } - - //-------------------------------------------------------------------- - - /** - * Returns information on the entire cache. - * - * The information returned and the structure of the data - * varies depending on the handler. - * - * @return mixed - */ - public function getCacheInfo() - { - return []; - } - - //-------------------------------------------------------------------- - - /** - * Returns detailed information about the specific item in the cache. - * - * @param string $key Cache item name. - * - * @return mixed - */ - public function getMetaData(string $key) - { - return false; - } - - //-------------------------------------------------------------------- - - /** - * Determines if the driver is supported on this system. - * - * @return boolean - */ - public function isSupported(): bool - { - return true; - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Commands/AbstractInfo.php b/tests/_support/Commands/AbstractInfo.php deleted file mode 100644 index 2ca684e2..00000000 --- a/tests/_support/Commands/AbstractInfo.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -namespace Tests\Support\Commands; - -use CodeIgniter\CLI\BaseCommand; - -abstract class AbstractInfo extends BaseCommand -{ - - protected $group = 'demo'; - protected $name = 'app:pablo'; - protected $description = 'Displays basic application information.'; - -} diff --git a/tests/_support/Commands/AppInfo.php b/tests/_support/Commands/AppInfo.php deleted file mode 100644 index 41d26668..00000000 --- a/tests/_support/Commands/AppInfo.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -namespace Tests\Support\Commands; - -use CodeIgniter\CLI\BaseCommand; -use CodeIgniter\CLI\CLI; -use CodeIgniter\CodeIgniter; - -class AppInfo extends BaseCommand -{ - - protected $group = 'demo'; - protected $name = 'app:info'; - protected $description = 'Displays basic application information.'; - - public function run(array $params) - { - CLI::write('CI Version: ' . CLI::color(CodeIgniter::CI_VERSION, 'red')); - } - - public function bomb() - { - try - { - CLI::color('test', 'white', 'Background'); - } - catch (\RuntimeException $oops) - { - $this->showError($oops); - } - } - - public function helpme() - { - $this->call('help'); - } -} diff --git a/tests/_support/Config/BadRegistrar.php b/tests/_support/Config/BadRegistrar.php deleted file mode 100644 index a651d11d..00000000 --- a/tests/_support/Config/BadRegistrar.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php namespace Tests\Support\Config; - -/** - * Class BadRegistrar - * - * Doesn't provides a basic registrar class for testing BaseConfig registration functions, - * because it doesn't return an associative array - */ - -class BadRegistrar -{ - - public static function RegistrarConfig() - { - return 'I am not worthy'; - } - -} diff --git a/tests/_support/Config/MockAppConfig.php b/tests/_support/Config/MockAppConfig.php deleted file mode 100644 index 23eef138..00000000 --- a/tests/_support/Config/MockAppConfig.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php namespace Tests\Support\Config; - -class MockAppConfig -{ - public $baseURL = 'http://example.com'; - - public $uriProtocol = 'REQUEST_URI'; - - public $cookiePrefix = ''; - public $cookieDomain = ''; - public $cookiePath = '/'; - public $cookieSecure = false; - public $cookieHTTPOnly = false; - - public $proxyIPs = ''; - - public $CSRFProtection = false; - public $CSRFTokenName = 'csrf_test_name'; - public $CSRFHeaderName = 'X-CSRF-TOKEN'; - public $CSRFCookieName = 'csrf_cookie_name'; - public $CSRFExpire = 7200; - public $CSRFRegenerate = true; - public $CSRFExcludeURIs = ['http://example.com']; - public $CSRFRedirect = false; - - public $CSPEnabled = false; - - public $defaultLocale = 'en'; - public $negotiateLocale = false; - public $supportedLocales = [ - 'en', - 'es', - ]; -} diff --git a/tests/_support/Config/MockAutoload.php b/tests/_support/Config/MockAutoload.php deleted file mode 100644 index b916c197..00000000 --- a/tests/_support/Config/MockAutoload.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php namespace Tests\Support\Config; - -use Config\Autoload; - -class MockAutoload extends Autoload -{ - public $psr4 = []; - - public $classmap = []; - - //-------------------------------------------------------------------- - - public function __construct() - { - // Don't call the parent since we don't want the default mappings. - // parent::__construct(); - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Config/MockCLIConfig.php b/tests/_support/Config/MockCLIConfig.php deleted file mode 100644 index 6eea05ce..00000000 --- a/tests/_support/Config/MockCLIConfig.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php namespace Tests\Support\Config; - -class MockCLIConfig extends \Config\App -{ - public $baseURL = 'http://example.com'; - - public $uriProtocol = 'REQUEST_URI'; - - public $cookiePrefix = ''; - public $cookieDomain = ''; - public $cookiePath = '/'; - public $cookieSecure = false; - public $cookieHTTPOnly = false; - - public $proxyIPs = ''; - - public $CSRFProtection = false; - public $CSRFTokenName = 'csrf_test_name'; - public $CSRFCookieName = 'csrf_cookie_name'; - public $CSRFExpire = 7200; - public $CSRFRegenerate = true; - public $CSRFExcludeURIs = ['http://example.com']; - - public $CSPEnabled = false; - - public $defaultLocale = 'en'; - public $negotiateLocale = false; - public $supportedLocales = [ - 'en', - 'es', - ]; -} diff --git a/tests/_support/Config/MockLogger.php b/tests/_support/Config/MockLogger.php deleted file mode 100644 index b304cf53..00000000 --- a/tests/_support/Config/MockLogger.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php namespace Tests\Support\Config; - -class MockLogger -{ - /* - |-------------------------------------------------------------------------- - | Error Logging Threshold - |-------------------------------------------------------------------------- - | - | You can enable error logging by setting a threshold over zero. The - | threshold determines what gets logged. Any values below or equal to the - | threshold will be logged. Threshold options are: - | - | 0 = Disables logging, Error logging TURNED OFF - | 1 = Emergency Messages - System is unusable - | 2 = Alert Messages - Action Must Be Taken Immediately - | 3 = Critical Messages - Application component unavailable, unexpected exception. - | 4 = Runtime Errors - Don't need immediate action, but should be monitored. - | 5 = Warnings - Exceptional occurrences that are not errors. - | 6 = Notices - Normal but significant events. - | 7 = Info - Interesting events, like user logging in, etc. - | 8 = Debug - Detailed debug information. - | 9 = All Messages - | - | You can also pass an array with threshold levels to show individual error types - | - | array(1, 2, 3, 8) = Emergency, Alert, Critical, and Debug messages - | - | For a live site you'll usually enable Critical or higher (3) to be logged otherwise - | your log files will fill up very fast. - | - */ - - public $threshold = 9; - - /* - |-------------------------------------------------------------------------- - | Error Logging Directory Path - |-------------------------------------------------------------------------- - | - | - | - */ - public $path = ''; - - /* - |-------------------------------------------------------------------------- - | Date Format for Logs - |-------------------------------------------------------------------------- - | - | Each item that is logged has an associated date. You can use PHP date - | codes to set your own date formatting - | - */ - public $dateFormat = 'Y-m-d'; - - /* - |-------------------------------------------------------------------------- - | Log Handlers - |-------------------------------------------------------------------------- - | - | The logging system supports multiple actions to be taken when something - | is logged. This is done by allowing for multiple Handlers, special classes - | designed to write the log to their chosen destinations, whether that is - | a file on the getServer, a cloud-based service, or even taking actions such - | as emailing the dev team. - | - | Each handler is defined by the class name used for that handler, and it - | MUST implement the CodeIgniter\Log\Handlers\HandlerInterface interface. - | - | The value of each key is an array of configuration items that are sent - | to the constructor of each handler. The only required configuration item - | is the 'handles' element, which must be an array of integer log levels. - | This is most easily handled by using the constants defined in the - | Psr\Log\LogLevel class. - | - | Handlers are executed in the order defined in this array, starting with - | the handler on top and continuing down. - | - */ - public $handlers = [ - //-------------------------------------------------------------------- - // File Handler - //-------------------------------------------------------------------- - - 'Tests\Support\Log\Handlers\TestHandler' => [ - /* - * The log levels that this handler will handle. - */ - 'handles' => [ - 'critical', - 'alert', - 'emergency', - 'debug', - 'error', - 'info', - 'notice', - 'warning', - ], - ], - ]; - -} diff --git a/tests/_support/Config/MockServices.php b/tests/_support/Config/MockServices.php deleted file mode 100644 index 0bd6400e..00000000 --- a/tests/_support/Config/MockServices.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -namespace Tests\Support\Config; - -use \CodeIgniter\Config\BaseService; - -class MockServices extends BaseService -{ - - public $psr4 = [ - 'Tests/Support' => TESTPATH . '_support/', - ]; - public $classmap = []; - - //-------------------------------------------------------------------- - - public function __construct() - { - // Don't call the parent since we don't want the default mappings. - // parent::__construct(); - } - - //-------------------------------------------------------------------- - public static function locator(bool $getShared = true) - { - return new \CodeIgniter\Autoloader\FileLocator(static::autoloader()); - } - -} diff --git a/tests/_support/Config/Registrar.php b/tests/_support/Config/Registrar.php deleted file mode 100644 index 9d00c6a2..00000000 --- a/tests/_support/Config/Registrar.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php namespace Tests\Support\Config; - -/** - * Class Registrar - * - * Provides a basic registrar class for testing BaseConfig registration functions. - */ - -class Registrar -{ - - public static function RegistrarConfig() - { - return [ - 'bar' => [ - 'first', - 'second', - ], - 'format' => 'nice', - 'fruit' => [ - 'apple', - 'banana', - ], - ]; - } - -} diff --git a/tests/_support/Config/Routes.php b/tests/_support/Config/Routes.php deleted file mode 100644 index 2369e33b..00000000 --- a/tests/_support/Config/Routes.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -/** - * This is a simple file to include for testing the RouteCollection class. - */ - -$routes->add('testing', 'TestController::index'); diff --git a/tests/_support/Controllers/Popcorn.php b/tests/_support/Controllers/Popcorn.php deleted file mode 100644 index 8af998d8..00000000 --- a/tests/_support/Controllers/Popcorn.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -namespace Tests\Support\Controllers; - -use CodeIgniter\API\ResponseTrait; -use CodeIgniter\Controller; -use CodeIgniter\HTTP\Exceptions\HTTPException; - -/** - * This is a testing only controller, intended to blow up in multiple - * ways to make sure we catch them. - */ -class Popcorn extends Controller -{ - - use ResponseTrait; - - public function index() - { - return 'Hi there'; - } - - public function pop() - { - $this->respond('Oops', 567, 'Surprise'); - } - - public function popper() - { - throw new \RuntimeException('Surprise', 500); - } - - public function weasel() - { - $this->respond('', 200); - } - - public function oops() - { - $this->failUnauthorized(); - } - - public function goaway() - { - return redirect()->to('/'); - } - - // @see https://github.com/codeigniter4/CodeIgniter4/issues/1834 - public function index3() - { - $response = $this->response->setJSON([ - 'lang' => $this->request->getLocale(), - ]); - - // echo var_dump($this->response->getBody()); - return $response; - } - - public function canyon() - { - echo 'Hello-o-o'; - } - - public function cat() - { - } - - public function json() - { - $this->responsd(['answer' => 42]); - } - - public function xml() - { - $this->respond('<my><pet>cat</pet></my>'); - } - -} diff --git a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php b/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php deleted file mode 100644 index f0f7eea2..00000000 --- a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php namespace Tests\Support\Database\Migrations; - -class Migration_Create_test_tables extends \CodeIgniter\Database\Migration -{ - public function up() - { - // SQLite3 uses auto increment different - $unique_or_auto = $this->db->DBDriver === 'SQLite3' ? 'unique' : 'auto_increment'; - - // User Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 80, - ], - 'email' => [ - 'type' => 'VARCHAR', - 'constraint' => 100, - ], - 'country' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'created_at' => [ - 'type' => 'DATETIME', - 'null' => true, - ], - 'updated_at' => [ - 'type' => 'DATETIME', - 'null' => true, - ], - 'deleted_at' => [ - 'type' => 'DATETIME', - 'null' => true, - ], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('user', true); - - // Job Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'description' => [ - 'type' => 'TEXT', - 'null' => true, - ], - 'created_at' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'null' => true, - ], - 'updated_at' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'null' => true, - ], - 'deleted_at' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'null' => true, - ], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('job', true); - - // Misc Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'key' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'value' => ['type' => 'TEXT'], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('misc', true); - - // Empty Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'created_at' => [ - 'type' => 'DATE', - 'null' => true, - ], - 'updated_at' => [ - 'type' => 'DATE', - 'null' => true, - ], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('empty', true); - - // Secondary Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'key' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'value' => ['type' => 'TEXT'], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('secondary', true); - } - - //-------------------------------------------------------------------- - - public function down() - { - $this->forge->dropTable('user', true); - $this->forge->dropTable('job', true); - $this->forge->dropTable('misc', true); - $this->forge->dropTable('empty', true); - $this->forge->dropTable('secondary', true); - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Database/MockBuilder.php b/tests/_support/Database/MockBuilder.php deleted file mode 100644 index 98c7fb67..00000000 --- a/tests/_support/Database/MockBuilder.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php namespace Tests\Support\Database; - -use CodeIgniter\Database\BaseBuilder; -use CodeIgniter\Database\ConnectionInterface; - -class MockBuilder extends BaseBuilder { - - public function __construct($tableName, ConnectionInterface &$db, array $options = null) - { - parent::__construct($tableName, $db, $options); - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Database/MockConnection.php b/tests/_support/Database/MockConnection.php deleted file mode 100644 index 3b6fd2a9..00000000 --- a/tests/_support/Database/MockConnection.php +++ /dev/null @@ -1,298 +0,0 @@ -<?php namespace Tests\Support\Database; - -use CodeIgniter\CodeIgniter; -use CodeIgniter\Database\BaseConnection; - -class MockConnection extends BaseConnection -{ - protected $returnValues = []; - - public $database; - - public $lastQuery; - - //-------------------------------------------------------------------- - - public function shouldReturn(string $method, $return) - { - $this->returnValues[$method] = $return; - - return $this; - } - - //-------------------------------------------------------------------- - - /** - * Orchestrates a query against the database. Queries must use - * Database\Statement objects to store the query and build it. - * This method works with the cache. - * - * Should automatically handle different connections for read/write - * queries if needed. - * - * @param string $sql - * @param mixed ...$binds - * @param boolean $setEscapeFlags - * @param string $queryClass - * - * @return \CodeIgniter\Database\BaseResult|\CodeIgniter\Database\Query|false - */ - - public function query(string $sql, $binds = null, bool $setEscapeFlags = true, string $queryClass = 'CodeIgniter\\Database\\Query') - { - $queryClass = str_replace('Connection', 'Query', get_class($this)); - - $query = new $queryClass($this); - - $query->setQuery($sql, $binds, $setEscapeFlags); - - if (! empty($this->swapPre) && ! empty($this->DBPrefix)) - { - $query->swapPrefix($this->DBPrefix, $this->swapPre); - } - - $startTime = microtime(true); - - $this->lastQuery = $query; - - // Run the query - if (false === ($this->resultID = $this->simpleQuery($query->getQuery()))) - { - $query->setDuration($startTime, $startTime); - - // @todo deal with errors - - return false; - } - - $query->setDuration($startTime); - - $resultClass = str_replace('Connection', 'Result', get_class($this)); - - return new $resultClass($this->connID, $this->resultID); - } - - //-------------------------------------------------------------------- - - /** - * Connect to the database. - * - * @param boolean $persistent - * - * @return mixed - */ - public function connect(bool $persistent = false) - { - $return = $this->returnValues['connect'] ?? true; - - if (is_array($return)) - { - // By removing the top item here, we can - // get a different value for, say, testing failover connections. - $return = array_shift($this->returnValues['connect']); - } - - return $return; - } - - //-------------------------------------------------------------------- - - /** - * Keep or establish the connection if no queries have been sent for - * a length of time exceeding the server's idle timeout. - * - * @return boolean - */ - public function reconnect(): bool - { - return true; - } - - //-------------------------------------------------------------------- - - /** - * Select a specific database table to use. - * - * @param string $databaseName - * - * @return mixed - */ - public function setDatabase(string $databaseName) - { - $this->database = $databaseName; - - return $this; - } - - //-------------------------------------------------------------------- - - /** - * Returns a string containing the version of the database being used. - * - * @return string - */ - public function getVersion(): string - { - return CodeIgniter::CI_VERSION; - } - - //-------------------------------------------------------------------- - - /** - * Executes the query against the database. - * - * @param string $sql - * - * @return mixed - */ - protected function execute(string $sql) - { - return $this->returnValues['execute']; - } - - //-------------------------------------------------------------------- - - /** - * Returns the total number of rows affected by this query. - * - * @return integer - */ - public function affectedRows(): int - { - return 1; - } - - //-------------------------------------------------------------------- - - /** - * Returns the last error code and message. - * - * Must return an array with keys 'code' and 'message': - * - * return ['code' => null, 'message' => null); - * - * @return array - */ - public function error(): array - { - return [ - 'code' => null, - 'message' => null, - ]; - } - - //-------------------------------------------------------------------- - - /** - * Insert ID - * - * @return integer - */ - public function insertID(): int - { - return $this->connID->insert_id; - } - - //-------------------------------------------------------------------- - - /** - * Generates the SQL for listing tables in a platform-dependent manner. - * - * @param boolean $constrainByPrefix - * - * @return string - */ - protected function _listTables(bool $constrainByPrefix = false): string - { - return ''; - } - - //-------------------------------------------------------------------- - - /** - * Generates a platform-specific query string so that the column names can be fetched. - * - * @param string $table - * - * @return string - */ - protected function _listColumns(string $table = ''): string - { - return ''; - } - - /** - * @param string $table - * @return array - */ - protected function _fieldData(string $table): array - { - return []; - } - - /** - * @param string $table - * @return array - */ - protected function _indexData(string $table): array - { - return []; - } - - /** - * @param string $table - * @return array - */ - protected function _foreignKeyData(string $table): array - { - return []; - } - - //-------------------------------------------------------------------- - - /** - * Close the connection. - */ - protected function _close() - { - return; - } - - //-------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @return boolean - */ - protected function _transBegin(): bool - { - return true; - } - - //-------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @return boolean - */ - protected function _transCommit(): bool - { - return true; - } - - //-------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @return boolean - */ - protected function _transRollback(): bool - { - return true; - } - - //-------------------------------------------------------------------- -} diff --git a/tests/_support/Database/MockQuery.php b/tests/_support/Database/MockQuery.php deleted file mode 100644 index 0008104a..00000000 --- a/tests/_support/Database/MockQuery.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php namespace Tests\Support\Database; - -use CodeIgniter\Database\Query; - -class MockQuery extends Query -{ - -} diff --git a/tests/_support/Database/MockResult.php b/tests/_support/Database/MockResult.php deleted file mode 100644 index dfe97a99..00000000 --- a/tests/_support/Database/MockResult.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php namespace Tests\Support\Database; - -use CodeIgniter\Database\BaseResult; - -class MockResult extends BaseResult -{ - /** - * Gets the number of fields in the result set. - * - * @return integer - */ - public function getFieldCount(): int - { - } - - //-------------------------------------------------------------------- - - /** - * Generates an array of column names in the result set. - * - * @return array - */ - public function getFieldNames(): array - { - } - - //-------------------------------------------------------------------- - - /** - * Generates an array of objects representing field meta-data. - * - * @return array - */ - public function getFieldData(): array - { - } - - //-------------------------------------------------------------------- - - /** - * Frees the current result. - * - * @return mixed - */ - public function freeResult() - { - } - - //-------------------------------------------------------------------- - - /** - * Moves the internal pointer to the desired offset. This is called - * internally before fetching results to make sure the result set - * starts at zero. - * - * @param integer $n - * - * @return mixed - */ - public function dataSeek($n = 0) - { - } - - //-------------------------------------------------------------------- - - /** - * Returns the result set as an array. - * - * Overridden by driver classes. - * - * @return mixed - */ - protected function fetchAssoc() - { - } - - //-------------------------------------------------------------------- - - /** - * Returns the result set as an object. - * - * Overridden by child classes. - * - * @param string $className - * - * @return object - */ - protected function fetchObject($className = 'stdClass') - { - } - - //-------------------------------------------------------------------- -} diff --git a/tests/_support/Database/MockTestClass.php b/tests/_support/Database/MockTestClass.php deleted file mode 100644 index 18e982a2..00000000 --- a/tests/_support/Database/MockTestClass.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -namespace Tests\Support\Database; - -class MockTestClass -{ -} diff --git a/tests/_support/Database/Seeds/AnotherSeeder.php b/tests/_support/Database/Seeds/AnotherSeeder.php deleted file mode 100644 index 5254d4dd..00000000 --- a/tests/_support/Database/Seeds/AnotherSeeder.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php namespace Tests\Support\Database\Seeds; - -class AnotherSeeder extends \CodeIgniter\Database\Seeder -{ - public function run() - { - $row = [ - 'name' => 'Jerome Lohan', - 'email' => 'jlo@lohanenterprises.com', - 'country' => 'UK', - ]; - - $this->db->table('user')->insert($row); - } -} diff --git a/tests/_support/Database/Seeds/CITestSeeder.php b/tests/_support/Database/Seeds/CITestSeeder.php deleted file mode 100644 index 02dde787..00000000 --- a/tests/_support/Database/Seeds/CITestSeeder.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php namespace Tests\Support\Database\Seeds; - -class CITestSeeder extends \CodeIgniter\Database\Seeder -{ - public function run() - { - // Job Data - $data = [ - 'user' => [ - [ - 'name' => 'Derek Jones', - 'email' => 'derek@world.com', - 'country' => 'US', - ], - [ - 'name' => 'Ahmadinejad', - 'email' => 'ahmadinejad@world.com', - 'country' => 'Iran', - ], - [ - 'name' => 'Richard A Causey', - 'email' => 'richard@world.com', - 'country' => 'US', - ], - [ - 'name' => 'Chris Martin', - 'email' => 'chris@world.com', - 'country' => 'UK', - ], - ], - 'job' => [ - [ - 'name' => 'Developer', - 'description' => 'Awesome job, but sometimes makes you bored', - ], - [ - 'name' => 'Politician', - 'description' => 'This is not really a job', - ], - [ - 'name' => 'Accountant', - 'description' => 'Boring job, but you will get free snack at lunch', - ], - [ - 'name' => 'Musician', - 'description' => 'Only Coldplay can actually called Musician', - ], - ], - 'misc' => [ - [ - 'key' => '\\xxxfoo456', - 'value' => 'Entry with \\xxx', - ], - [ - 'key' => '\\%foo456', - 'value' => 'Entry with \\%', - ], - [ - 'key' => 'spaces and tabs', - 'value' => ' One two three tab', - ], - ], - ]; - - foreach ($data as $table => $dummy_data) - { - $this->db->table($table)->truncate(); - - foreach ($dummy_data as $single_dummy_data) - { - $this->db->table($table)->insert($single_dummy_data); - } - } - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Database/SupportMigrations/001_Some_migration.php b/tests/_support/Database/SupportMigrations/001_Some_migration.php deleted file mode 100644 index e2ace646..00000000 --- a/tests/_support/Database/SupportMigrations/001_Some_migration.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php namespace App\Database\Migrations; - -class Migration_some_migration extends \CodeIgniter\Database\Migration -{ - public function up() - { - $this->forge->addField([ - 'key' => [ - 'type' => 'VARCHAR', - 'constraint' => 255, - ], - ]); - $this->forge->createTable('foo', true); - - $this->db->table('foo')->insert([ - 'key' => 'foobar', - ]); - } - - public function down() - { - $this->forge->dropTable('foo', true); - } -} diff --git a/tests/_support/DatabaseTestMigrations/Database/Migrations/20160428212500_Create_test_tables.php b/tests/_support/DatabaseTestMigrations/Database/Migrations/20160428212500_Create_test_tables.php deleted file mode 100644 index 8514ba4a..00000000 --- a/tests/_support/DatabaseTestMigrations/Database/Migrations/20160428212500_Create_test_tables.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php namespace Tests\Support\DatabaseTestMigrations\Database\Migrations; - -class Migration_Create_test_tables extends \CodeIgniter\Database\Migration -{ - public function up() - { - // SQLite3 uses auto increment different - $unique_or_auto = $this->db->DBDriver === 'SQLite3' ? 'unique' : 'auto_increment'; - - // User Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 80, - ], - 'email' => [ - 'type' => 'VARCHAR', - 'constraint' => 100, - ], - 'country' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'created_at' => [ - 'type' => 'DATETIME', - 'null' => true, - ], - 'updated_at' => [ - 'type' => 'DATETIME', - 'null' => true, - ], - 'deleted_at' => [ - 'type' => 'DATETIME', - 'null' => true, - ], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('user', true); - - // Job Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'description' => [ - 'type' => 'TEXT', - 'null' => true, - ], - 'created_at' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'null' => true, - ], - 'updated_at' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'null' => true, - ], - 'deleted_at' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'null' => true, - ], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('job', true); - - // Misc Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'key' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'value' => ['type' => 'TEXT'], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('misc', true); - - // Empty Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'created_at' => [ - 'type' => 'DATE', - 'null' => true, - ], - 'updated_at' => [ - 'type' => 'DATE', - 'null' => true, - ], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('empty', true); - - // Secondary Table - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $unique_or_auto => true, - ], - 'key' => [ - 'type' => 'VARCHAR', - 'constraint' => 40, - ], - 'value' => ['type' => 'TEXT'], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('secondary', true); - } - - //-------------------------------------------------------------------- - - public function down() - { - $this->forge->dropTable('user', true); - $this->forge->dropTable('job', true); - $this->forge->dropTable('misc', true); - $this->forge->dropTable('empty', true); - $this->forge->dropTable('secondary', true); - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Events/MockEvents.php b/tests/_support/Events/MockEvents.php deleted file mode 100644 index eb9186b8..00000000 --- a/tests/_support/Events/MockEvents.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php namespace Tests\Support\Events; - -/** - * CodeIgniter - * - * An open source application development framework for PHP - * - * This content is released under the MIT License (MIT) - * - * Copyright (c) 2014-2018 British Columbia Institute of Technology - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @package CodeIgniter - * @author CodeIgniter Dev Team - * @copyright 2014-2018 British Columbia Institute of Technology (https://bcit.ca/) - * @license https://opensource.org/licenses/MIT MIT License - * @link https://codeigniter.com - * @since Version 4.0.0 - * @filesource - */ - -use CodeIgniter\Events\Events; - -/** - * Events - */ -class MockEvents extends Events -{ - - public function getListeners() - { - return self::$listeners; - } - - public function getEventsFile() - { - return self::$eventsFile; - } - - public function getSimulate() - { - return self::$simulate; - } - - public function unInitialize() - { - static::$initialized = false; - } -} diff --git a/tests/_support/Files/able/apple.php b/tests/_support/Files/able/apple.php deleted file mode 100644 index d4a780d2..00000000 --- a/tests/_support/Files/able/apple.php +++ /dev/null @@ -1,6 +0,0 @@ -<?php -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ diff --git a/tests/_support/Files/able/fig_3.php b/tests/_support/Files/able/fig_3.php deleted file mode 100644 index d4a780d2..00000000 --- a/tests/_support/Files/able/fig_3.php +++ /dev/null @@ -1,6 +0,0 @@ -<?php -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ diff --git a/tests/_support/Files/able/prune_ripe.php b/tests/_support/Files/able/prune_ripe.php deleted file mode 100644 index d4a780d2..00000000 --- a/tests/_support/Files/able/prune_ripe.php +++ /dev/null @@ -1,6 +0,0 @@ -<?php -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ diff --git a/tests/_support/Files/baker/banana.php b/tests/_support/Files/baker/banana.php deleted file mode 100644 index d4a780d2..00000000 --- a/tests/_support/Files/baker/banana.php +++ /dev/null @@ -1,6 +0,0 @@ -<?php -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ diff --git a/tests/_support/HTTP/Files/CookiesHolder.txt b/tests/_support/HTTP/Files/CookiesHolder.txt deleted file mode 100644 index 8b137891..00000000 --- a/tests/_support/HTTP/Files/CookiesHolder.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/_support/HTTP/Files/tmp/fileA.txt b/tests/_support/HTTP/Files/tmp/fileA.txt deleted file mode 100644 index 8b137891..00000000 --- a/tests/_support/HTTP/Files/tmp/fileA.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/_support/HTTP/Files/tmp/fileB.txt b/tests/_support/HTTP/Files/tmp/fileB.txt deleted file mode 100644 index 8b137891..00000000 --- a/tests/_support/HTTP/Files/tmp/fileB.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/_support/HTTP/MockCURLRequest.php b/tests/_support/HTTP/MockCURLRequest.php deleted file mode 100644 index 2cd73b54..00000000 --- a/tests/_support/HTTP/MockCURLRequest.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -namespace Tests\Support\HTTP; - -use CodeIgniter\HTTP\CURLRequest; - -/** - * Class MockCURLRequest - * - * Simply allows us to not actually call cURL during the - * test runs. Instead, we can set the desired output - * and get back the set options. - */ -class MockCURLRequest extends CURLRequest -{ - - public $curl_options; - protected $output = ''; - - //-------------------------------------------------------------------- - - public function setOutput($output) - { - $this->output = $output; - - return $this; - } - - //-------------------------------------------------------------------- - - protected function sendRequest(array $curl_options = []): string - { - // Save so we can access later. - $this->curl_options = $curl_options; - - return $this->output; - } - - //-------------------------------------------------------------------- - // for testing purposes only - public function getBaseURI() - { - return $this->baseURI; - } - - // for testing purposes only - public function getDelay() - { - return $this->delay; - } - -} diff --git a/tests/_support/HTTP/MockIncomingRequest.php b/tests/_support/HTTP/MockIncomingRequest.php deleted file mode 100644 index 627fc9a0..00000000 --- a/tests/_support/HTTP/MockIncomingRequest.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php namespace Tests\Support\HTTP; - -use CodeIgniter\HTTP\IncomingRequest; - -class MockIncomingRequest extends IncomingRequest -{ - // public function populateHeaders() - // { - // // Don't do anything... force the tester to manually set the headers they want. - // } - - public function detectURI($protocol, $baseURL) - { - // Do nothing... - } - -} diff --git a/tests/_support/HTTP/MockResponse.php b/tests/_support/HTTP/MockResponse.php deleted file mode 100755 index 8f31840b..00000000 --- a/tests/_support/HTTP/MockResponse.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -namespace Tests\Support\HTTP; - -use CodeIgniter\HTTP\Response; - -/** - * Class MockResponse - */ -class MockResponse extends Response -{ - - /** - * If true, will not write output. Useful during testing. - * - * @var boolean - */ - protected $pretend = true; - - // for testing - public function getPretend() - { - return $this->pretend; - } - - // artificial error for testing - public function misbehave() - { - $this->statusCode = 0; - } - -} diff --git a/tests/_support/Images/EXIFsamples/down-mirrored.jpg b/tests/_support/Images/EXIFsamples/down-mirrored.jpg deleted file mode 100644 index 34a7b1d3..00000000 Binary files a/tests/_support/Images/EXIFsamples/down-mirrored.jpg and /dev/null differ diff --git a/tests/_support/Images/EXIFsamples/down.jpg b/tests/_support/Images/EXIFsamples/down.jpg deleted file mode 100644 index 9077a7c9..00000000 Binary files a/tests/_support/Images/EXIFsamples/down.jpg and /dev/null differ diff --git a/tests/_support/Images/EXIFsamples/left-mirrored.jpg b/tests/_support/Images/EXIFsamples/left-mirrored.jpg deleted file mode 100644 index 18327024..00000000 Binary files a/tests/_support/Images/EXIFsamples/left-mirrored.jpg and /dev/null differ diff --git a/tests/_support/Images/EXIFsamples/left.jpg b/tests/_support/Images/EXIFsamples/left.jpg deleted file mode 100644 index ad1f8985..00000000 Binary files a/tests/_support/Images/EXIFsamples/left.jpg and /dev/null differ diff --git a/tests/_support/Images/EXIFsamples/right-mirrored.jpg b/tests/_support/Images/EXIFsamples/right-mirrored.jpg deleted file mode 100644 index cc8a29ae..00000000 Binary files a/tests/_support/Images/EXIFsamples/right-mirrored.jpg and /dev/null differ diff --git a/tests/_support/Images/EXIFsamples/right.jpg b/tests/_support/Images/EXIFsamples/right.jpg deleted file mode 100644 index 183ffebb..00000000 Binary files a/tests/_support/Images/EXIFsamples/right.jpg and /dev/null differ diff --git a/tests/_support/Images/EXIFsamples/up-mirrored.jpg b/tests/_support/Images/EXIFsamples/up-mirrored.jpg deleted file mode 100644 index e1865a5f..00000000 Binary files a/tests/_support/Images/EXIFsamples/up-mirrored.jpg and /dev/null differ diff --git a/tests/_support/Images/EXIFsamples/up.jpg b/tests/_support/Images/EXIFsamples/up.jpg deleted file mode 100644 index 70fc26ff..00000000 Binary files a/tests/_support/Images/EXIFsamples/up.jpg and /dev/null differ diff --git a/tests/_support/Images/Steveston_dusk.JPG b/tests/_support/Images/Steveston_dusk.JPG deleted file mode 100644 index c3b9b121..00000000 Binary files a/tests/_support/Images/Steveston_dusk.JPG and /dev/null differ diff --git a/tests/_support/Images/ci-logo.gif b/tests/_support/Images/ci-logo.gif deleted file mode 100644 index 3001b2f7..00000000 Binary files a/tests/_support/Images/ci-logo.gif and /dev/null differ diff --git a/tests/_support/Images/ci-logo.jpeg b/tests/_support/Images/ci-logo.jpeg deleted file mode 100644 index 1b0178bb..00000000 Binary files a/tests/_support/Images/ci-logo.jpeg and /dev/null differ diff --git a/tests/_support/Images/ci-logo.png b/tests/_support/Images/ci-logo.png deleted file mode 100644 index 34fb0108..00000000 Binary files a/tests/_support/Images/ci-logo.png and /dev/null differ diff --git a/tests/_support/Language/MockLanguage.php b/tests/_support/Language/MockLanguage.php deleted file mode 100644 index 49301db3..00000000 --- a/tests/_support/Language/MockLanguage.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php namespace Tests\Support\Language; - -use CodeIgniter\Language\Language; - -class MockLanguage extends Language -{ - - /** - * Stores the data that should be - * returned by the 'requireFile()' method. - * - * @var mixed - */ - protected $data; - - //-------------------------------------------------------------------- - - /** - * Sets the data that should be returned by the - * 'requireFile()' method to allow easy overrides - * during testing. - * - * @param array $data - * @param string $file - * @param string|null $locale - * - * @return $this - */ - public function setData(string $file, array $data, string $locale = null) - { - $this->language[$locale ?? $this->locale][$file] = $data; - - return $this; - } - - //-------------------------------------------------------------------- - - /** - * Provides an override that allows us to set custom - * data to be returned easily during testing. - * - * @param string $path - * - * @return array|mixed - */ - protected function requireFile(string $path): array - { - return $this->data ?? []; - } - - //-------------------------------------------------------------------- - - /** - * Arbitrarily turnoff internationalization support for testing - */ - public function disableIntlSupport() - { - $this->intlSupport = false; - } - -} diff --git a/tests/_support/Language/SecondMockLanguage.php b/tests/_support/Language/SecondMockLanguage.php deleted file mode 100644 index 71428854..00000000 --- a/tests/_support/Language/SecondMockLanguage.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php namespace Tests\Support\Language; - -use CodeIgniter\Language\Language; - -class SecondMockLanguage extends Language -{ - //-------------------------------------------------------------------- - - /** - * Expose the protected *load* method - */ - public function loadem(string $file, string $locale = 'en', bool $return = false) - { - return $this->load($file, $locale, $return); - } - - //-------------------------------------------------------------------- - - /** - * Expose the loaded language files - */ - public function loaded(string $locale = 'en') - { - return $this->loadedFiles[$locale]; - } - -} diff --git a/tests/_support/Language/ab-CD/Allin.php b/tests/_support/Language/ab-CD/Allin.php deleted file mode 100644 index 3b153883..00000000 --- a/tests/_support/Language/ab-CD/Allin.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -// seven wonders of the ancient world -return [ - 'one' => 'Pyramid of Giza', - 'tre' => 'Colossus of Rhodes', - 'fiv' => 'Temple of Artemis', - 'sev' => 'Hanging Gardens of Babylon', -]; diff --git a/tests/_support/Language/ab/Allin.php b/tests/_support/Language/ab/Allin.php deleted file mode 100644 index 69120758..00000000 --- a/tests/_support/Language/ab/Allin.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -// seven deadly sins -return [ - 'two' => 'gluttony', - 'tre' => 'greed', - 'six' => 'envy', - 'sev' => 'pride', -]; diff --git a/tests/_support/Language/en-ZZ/More.php b/tests/_support/Language/en-ZZ/More.php deleted file mode 100644 index 66810205..00000000 --- a/tests/_support/Language/en-ZZ/More.php +++ /dev/null @@ -1,6 +0,0 @@ -<?php -return [ - 'strongForce' => 'These are not the droids you are looking for', - 'notaMoon' => "It's made of cheese", - 'wisdom' => 'There is no try', -]; diff --git a/tests/_support/Language/en/Allin.php b/tests/_support/Language/en/Allin.php deleted file mode 100644 index 6a10dcc7..00000000 --- a/tests/_support/Language/en/Allin.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -// 12 days of Christmas -return [ - 'for' => 'four calling birds', - 'fiv' => 'five golden rings', - 'six' => 'six geese a laying', - 'sev' => 'seven swans a swimming', -]; diff --git a/tests/_support/Language/en/Core.php b/tests/_support/Language/en/Core.php deleted file mode 100644 index b2fc4c18..00000000 --- a/tests/_support/Language/en/Core.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Core language strings. - * - * @package CodeIgniter - * @author CodeIgniter Dev Team - * @copyright 2014-2018 British Columbia Institute of Technology (https://bcit.ca/) - * @license https://opensource.org/licenses/MIT MIT License - * @link https://codeigniter.com - * @since Version 4.0.0 - * @filesource - * - * @codeCoverageIgnore - */ - -// looking for a system message not likely to be part of unit testing -return [ - 'missingExtension' => '{0} extension could not be found.', - 'bazillion' => 'billions and billions', // adds a new setting -]; diff --git a/tests/_support/Language/en/More.php b/tests/_support/Language/en/More.php deleted file mode 100644 index 5b4eaea5..00000000 --- a/tests/_support/Language/en/More.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -return [ - 'strongForce' => 'These are not the droids you are looking for', - 'notaMoon' => "That's no moon... it's a space station", - 'cannotMove' => 'I have a very bad feeling about this', -]; diff --git a/tests/_support/Language/ru/Language.php b/tests/_support/Language/ru/Language.php deleted file mode 100644 index d6c6632b..00000000 --- a/tests/_support/Language/ru/Language.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -return [ - 'languageGetLineInvalidArgumentException' => 'Whatever this would be, translated', -]; diff --git a/tests/_support/Log/Handlers/MockChromeHandler.php b/tests/_support/Log/Handlers/MockChromeHandler.php deleted file mode 100644 index 1a64b220..00000000 --- a/tests/_support/Log/Handlers/MockChromeHandler.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php namespace Tests\Support\Log\Handlers; - -/** - * Class MockHandler - * - * Extends ChromeLoggerHandler, exposing some inner workings - */ - -class MockChromeHandler extends \CodeIgniter\Log\Handlers\ChromeLoggerHandler -{ - - //-------------------------------------------------------------------- - - public function __construct(array $config) - { - parent::__construct($config); - } - - // retrieve the message from the JSON response - public function peekaboo() - { - return $this->json['rows'][0]; - } - -} diff --git a/tests/_support/Log/Handlers/MockFileHandler.php b/tests/_support/Log/Handlers/MockFileHandler.php deleted file mode 100644 index efd72a99..00000000 --- a/tests/_support/Log/Handlers/MockFileHandler.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php namespace Tests\Support\Log\Handlers; - -/** - * Class MockHandler - * - * Extends FileHandler, exposing some inner workings - */ - -class MockFileHandler extends \CodeIgniter\Log\Handlers\FileHandler -{ - /** - * Where would the log be written? - */ - public $destination; - - //-------------------------------------------------------------------- - - public function __construct(array $config) - { - parent::__construct($config); - $this->handles = $config['handles'] ?? []; - $this->destination = $this->path . 'log-' . date('Y-m-d') . '.' . $this->fileExtension; - } - -} diff --git a/tests/_support/Log/Handlers/TestHandler.php b/tests/_support/Log/Handlers/TestHandler.php deleted file mode 100644 index e22559c6..00000000 --- a/tests/_support/Log/Handlers/TestHandler.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php namespace Tests\Support\Log\Handlers; - -/** - * Class TestHandler - * - * A simple LogHandler that stores the logs in memory. - * Only used for testing purposes. - */ - -class TestHandler extends \CodeIgniter\Log\Handlers\FileHandler -{ - - /** - * Local storage for logs. - * - * @var array - */ - protected static $logs = []; - - /** - * Where would the log be written? - */ - //-------------------------------------------------------------------- - - public function __construct(array $config) - { - parent::__construct($config); - $this->handles = $config['handles'] ?? []; - $this->destination = $this->path . 'log-' . date('Y-m-d') . '.' . $this->fileExtension; - - self::$logs = []; - } - - //-------------------------------------------------------------------- - - /** - * Handles logging the message. - * If the handler returns false, then execution of handlers - * will stop. Any handlers that have not run, yet, will not - * be run. - * - * @param $level - * @param $message - * - * @return boolean - */ - public function handle($level, $message): bool - { - $date = date($this->dateFormat); - - self::$logs[] = strtoupper($level) . ' - ' . $date . ' --> ' . $message; - - return true; - } - - //-------------------------------------------------------------------- - - public static function getLogs() - { - return self::$logs; - } - - //-------------------------------------------------------------------- -} diff --git a/tests/_support/Log/TestLogger.php b/tests/_support/Log/TestLogger.php deleted file mode 100644 index a88eb629..00000000 --- a/tests/_support/Log/TestLogger.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php namespace Tests\Support\Log; - -use CodeIgniter\Log\Logger; - -class TestLogger extends Logger -{ - - protected static $op_logs = []; - - //-------------------------------------------------------------------- - - /** - * The log method is overridden so that we can store log history during - * the tests to allow us to check ->assertLogged() methods. - * - * @param string $level - * @param string $message - * @param array $context - * - * @return boolean - */ - public function log($level, $message, array $context = []): bool - { - // While this requires duplicate work, we want to ensure - // we have the final message to test against. - $log_message = $this->interpolate($message, $context); - - // Determine the file and line by finding the first - // backtrace that is not part of our logging system. - $trace = debug_backtrace(); - $file = null; - - foreach ($trace as $row) - { - if (! in_array($row['function'], ['log', 'log_message'])) - { - $file = basename($row['file'] ?? ''); - break; - } - } - - self::$op_logs[] = [ - 'level' => $level, - 'message' => $log_message, - 'file' => $file, - ]; - - // Let the parent do it's thing. - return parent::log($level, $message, $context); - } - - //-------------------------------------------------------------------- - - /** - * Used by CIUnitTestCase class to provide ->assertLogged() methods. - * - * @param string $level - * @param string $message - * - * @return boolean - */ - public static function didLog(string $level, $message) - { - foreach (self::$op_logs as $log) - { - if (strtolower($log['level']) === strtolower($level) && $message === $log['message']) - { - return true; - } - } - - return false; - } - - //-------------------------------------------------------------------- - // Expose cleanFileNames() - public function cleanup($file) - { - return $this->cleanFileNames($file); - } - -} diff --git a/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102300_Another_migration.py b/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102300_Another_migration.py deleted file mode 100644 index 908a6dd4..00000000 --- a/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102300_Another_migration.py +++ /dev/null @@ -1,24 +0,0 @@ -<?php namespace Tests\Support\MigrationTestMigrations\Database\Migrations; - -class Migration_another_migration extends \CodeIgniter\Database\Migration -{ - public function up() - { - $this->forge->addField([ - 'key' => [ - 'type' => 'VARCHAR', - 'constraint' => 255, - ], - ]); - $this->forge->createTable('foo', true); - - $this->db->table('foo')->insert([ - 'key' => 'foobar', - ]); - } - - public function down() - { - $this->forge->dropTable('foo', true); - } -} diff --git a/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102301_Some_migration.php b/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102301_Some_migration.php deleted file mode 100644 index e7763a83..00000000 --- a/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102301_Some_migration.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php namespace Tests\Support\MigrationTestMigrations\Database\Migrations; - -class Migration_some_migration extends \CodeIgniter\Database\Migration -{ - public function up() - { - $this->forge->addField([ - 'key' => [ - 'type' => 'VARCHAR', - 'constraint' => 255, - ], - ]); - $this->forge->createTable('foo', true); - - $this->db->table('foo')->insert([ - 'key' => 'foobar', - ]); - } - - public function down() - { - $this->forge->dropTable('foo', true); - } -} diff --git a/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102302_Another_migration.php b/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102302_Another_migration.php deleted file mode 100644 index 797aae70..00000000 --- a/tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102302_Another_migration.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php namespace Tests\Support\MigrationTestMigrations\Database\Migrations; - -class Migration_another_migration extends \CodeIgniter\Database\Migration -{ - public function up() - { - $fields = [ - 'value' => [ - 'type' => 'VARCHAR', - 'constraint' => 255, - ], - ]; - $this->forge->addColumn('foo', $fields); - - $this->db->table('foo')->insert([ - 'key' => 'foobar', - 'value' => 'raboof', - ]); - } - - public function down() - { - if ($this->db->tableExists('foo')) - { - $this->forge->dropColumn('foo', 'value'); - } - } -} diff --git a/tests/_support/MockCodeIgniter.php b/tests/_support/MockCodeIgniter.php deleted file mode 100644 index 3e31d001..00000000 --- a/tests/_support/MockCodeIgniter.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php namespace Tests\Support; - -use CodeIgniter\CodeIgniter; - -class MockCodeIgniter extends CodeIgniter -{ - protected function callExit($code) - { - // Do not call exit() in testing. - } -} diff --git a/tests/_support/MockCommon.php b/tests/_support/MockCommon.php deleted file mode 100644 index 21fd5426..00000000 --- a/tests/_support/MockCommon.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * Common Functions for testing - * - * Several application-wide utility methods. - * - * @package CodeIgniter - * @category Common Functions - */ - -if (! function_exists('is_cli')) -{ - /** - * Is CLI? - * - * Test to see if a request was made from the command line. - * You can set the return value for testing. - * - * @param boolean $new_return return value to set - * @return boolean - */ - function is_cli(bool $new_return = null): bool - { - // PHPUnit always runs via CLI. - static $return_value = true; - - if ($new_return !== null) - { - $return_value = $new_return; - } - - return $return_value; - } -} - -//-------------------------------------------------------------------- diff --git a/tests/_support/Models/EntityModel.php b/tests/_support/Models/EntityModel.php deleted file mode 100644 index 46abfa59..00000000 --- a/tests/_support/Models/EntityModel.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Model; - -class EntityModel extends Model -{ - protected $table = 'job'; - - protected $returnType = '\Tests\Support\Models\SimpleEntity'; - - protected $useSoftDeletes = false; - - protected $dateFormat = 'int'; - - protected $deletedField = 'deleted_at'; - - protected $allowedFields = [ - 'name', - 'description', - 'created_at', - ]; -} diff --git a/tests/_support/Models/EventModel.php b/tests/_support/Models/EventModel.php deleted file mode 100644 index ed331242..00000000 --- a/tests/_support/Models/EventModel.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Model; - -class EventModel extends Model -{ - protected $table = 'user'; - - protected $returnType = 'array'; - - protected $useSoftDeletes = false; - - protected $dateFormat = 'datetime'; - - protected $allowedFields = [ - 'name', - 'email', - 'country', - 'deleted_at', - ]; - - protected $beforeInsert = ['beforeInsertMethod']; - protected $afterInsert = ['afterInsertMethod']; - protected $beforeUpdate = ['beforeUpdateMethod']; - protected $afterUpdate = ['afterUpdateMethod']; - protected $afterFind = ['afterFindMethod']; - protected $afterDelete = ['afterDeleteMethod']; - - // Holds stuff for testing events - protected $tokens = []; - - protected function beforeInsertMethod(array $data) - { - $this->tokens[] = 'beforeInsert'; - - return $data; - } - - protected function afterInsertMethod(array $data) - { - $this->tokens[] = 'afterInsert'; - - return $data; - } - - protected function beforeUpdateMethod(array $data) - { - $this->tokens[] = 'beforeUpdate'; - - return $data; - } - - protected function afterUpdateMethod(array $data) - { - $this->tokens[] = 'afterUpdate'; - - return $data; - } - - protected function afterFindMethod(array $data) - { - $this->tokens[] = 'afterFind'; - - return $data; - } - - protected function afterDeleteMethod(array $data) - { - $this->tokens[] = 'afterDelete'; - - return $data; - } - - public function hasToken(string $token) - { - return in_array($token, $this->tokens); - } - -} diff --git a/tests/_support/Models/JobModel.php b/tests/_support/Models/JobModel.php deleted file mode 100644 index 98c4ad28..00000000 --- a/tests/_support/Models/JobModel.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Model; - -class JobModel extends Model -{ - protected $table = 'job'; - - protected $returnType = 'object'; - - protected $useSoftDeletes = false; - - protected $dateFormat = 'int'; - - protected $allowedFields = [ - 'name', - 'description', - ]; - - public $name = ''; - - public $description = ''; -} diff --git a/tests/_support/Models/SecondaryModel.php b/tests/_support/Models/SecondaryModel.php deleted file mode 100644 index 18e0b0aa..00000000 --- a/tests/_support/Models/SecondaryModel.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Model; - -class SecondaryModel extends Model -{ - protected $table = 'secondary'; - - protected $primaryKey = 'id'; - - protected $returnType = 'object'; - - protected $useSoftDeletes = false; - - protected $dateFormat = 'int'; - - protected $allowedFields = [ - 'key', - 'value', - ]; -} diff --git a/tests/_support/Models/SimpleEntity.php b/tests/_support/Models/SimpleEntity.php deleted file mode 100644 index d7648a32..00000000 --- a/tests/_support/Models/SimpleEntity.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Entity; - -/** - * Class SimpleEntity - * - * Simple Entity-type class for testing creating and saving entities - * in the model so we can support Entity/Repository type patterns. - * - * @package Tests\Support\Models - */ -class SimpleEntity extends Entity -{ - -} diff --git a/tests/_support/Models/UserModel.php b/tests/_support/Models/UserModel.php deleted file mode 100644 index 7be6d889..00000000 --- a/tests/_support/Models/UserModel.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Model; - -class UserModel extends Model -{ - protected $table = 'user'; - - protected $allowedFields = [ - 'name', - 'email', - 'country', - 'deleted_at', - ]; - - protected $returnType = 'object'; - - protected $useSoftDeletes = true; - - protected $dateFormat = 'datetime'; - - public $name = ''; - - public $email = ''; - - public $country = ''; -} diff --git a/tests/_support/Models/ValidErrorsModel.php b/tests/_support/Models/ValidErrorsModel.php deleted file mode 100644 index 189ac63b..00000000 --- a/tests/_support/Models/ValidErrorsModel.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Model; - -class ValidErrorsModel extends Model -{ - protected $table = 'job'; - - protected $returnType = 'object'; - - protected $useSoftDeletes = false; - - protected $dateFormat = 'int'; - - protected $allowedFields = [ - 'name', - 'description', - ]; - - protected $validationRules = [ - 'name' => [ - 'required', - 'min_length[10]', - 'errors' => [ - 'min_length' => 'Minimum Length Error', - ] - ], - 'token' => 'in_list[{id}]', - ]; -} diff --git a/tests/_support/Models/ValidModel.php b/tests/_support/Models/ValidModel.php deleted file mode 100644 index dc9ec7c4..00000000 --- a/tests/_support/Models/ValidModel.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php namespace Tests\Support\Models; - -use CodeIgniter\Model; - -class ValidModel extends Model -{ - protected $table = 'job'; - - protected $returnType = 'object'; - - protected $useSoftDeletes = false; - - protected $dateFormat = 'int'; - - protected $allowedFields = [ - 'name', - 'description', - ]; - - protected $validationRules = [ - 'name' => [ - 'required', - 'min_length[3]', - ], - 'token' => 'permit_empty|in_list[{id}]', - ]; - - protected $validationMessages = [ - 'name' => [ - 'required' => 'You forgot to name the baby.', - 'min_length' => 'Too short, man!', - ], - ]; -} diff --git a/tests/_support/RESTful/MockResourceController.php b/tests/_support/RESTful/MockResourceController.php deleted file mode 100644 index 21f43434..00000000 --- a/tests/_support/RESTful/MockResourceController.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -namespace Tests\Support\RESTful; - -use CodeIgniter\RESTful\ResourceController; - -class MockResourceController extends ResourceController -{ - - public function getModel() - { - return $this->model; - } - - public function getModelName() - { - return $this->modelName; - } - - public function getFormat() - { - return $this->format; - } - -} diff --git a/tests/_support/RESTful/MockResourcePresenter.php b/tests/_support/RESTful/MockResourcePresenter.php deleted file mode 100644 index d9b09510..00000000 --- a/tests/_support/RESTful/MockResourcePresenter.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -namespace Tests\Support\RESTful; - -use CodeIgniter\RESTful\ResourcePresenter; - -class MockResourcePresenter extends ResourcePresenter -{ - - public function getModel() - { - return $this->model; - } - - public function getModelName() - { - return $this->modelName; - } - - public function getFormat() - { - return $this->format; - } - -} diff --git a/tests/_support/RESTful/Worker.php b/tests/_support/RESTful/Worker.php deleted file mode 100644 index 973543fa..00000000 --- a/tests/_support/RESTful/Worker.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -namespace Tests\Support\RESTful; - -use CodeIgniter\RESTful\ResourceController; - -/** - * An extendable controller to provide a RESTful API for a resource. - */ -class Worker extends ResourceController -{ -} diff --git a/tests/_support/RESTful/Worker2.php b/tests/_support/RESTful/Worker2.php deleted file mode 100644 index bced2e95..00000000 --- a/tests/_support/RESTful/Worker2.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php -namespace Tests\Support\RESTful; - -use CodeIgniter\RESTful\ResourcePresenter; - -/** - * An extendable controller to provide a RESTful API for a resource. - */ -class Worker2 extends ResourcePresenter -{ - -} diff --git a/tests/_support/Security/MockSecurity.php b/tests/_support/Security/MockSecurity.php deleted file mode 100644 index 7a9239f5..00000000 --- a/tests/_support/Security/MockSecurity.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php namespace Tests\Support\Security; - -use CodeIgniter\Security\Security; -use CodeIgniter\HTTP\RequestInterface; - -class MockSecurity extends Security -{ - public function CSRFSetCookie(RequestInterface $request) - { - $_COOKIE['csrf_cookie_name'] = $this->CSRFHash; - - return $this; - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Services.php b/tests/_support/Services.php deleted file mode 100644 index 48e8283d..00000000 --- a/tests/_support/Services.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php namespace CodeIgniter; - -use CIUnitTestCase; -use Config\Services as ConfigServices; - -/** - * Services class for testing. - */ -class Services -{ - /** - * Mock objects for testing which are returned if exist. - * - * @var array - */ - static protected $mocks = []; - - //-------------------------------------------------------------------- - - /** - * Reset shared instances and mocks for testing. - */ - public static function reset() - { - static::$mocks = []; - - CIUnitTestCase::setPrivateProperty(ConfigServices::class, 'instances', []); - } - - //-------------------------------------------------------------------- - - /** - * Inject mock object for testing. - * - * @param string $name - * @param $mock - */ - public static function injectMock(string $name, $mock) - { - $name = strtolower($name); - static::$mocks[$name] = $mock; - } - - //-------------------------------------------------------------------- - - /** - * Returns a service - * - * @param string $name - * @param array $arguments - */ - public static function __callStatic(string $name, array $arguments) - { - $name = strtolower($name); - - // Returns mock if exists - if (isset(static::$mocks[$name])) - { - return static::$mocks[$name]; - } - - if (method_exists(ConfigServices::class, $name)) - { - return ConfigServices::$name(...$arguments); - } - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Session/MockSession.php b/tests/_support/Session/MockSession.php deleted file mode 100644 index 3bf76d40..00000000 --- a/tests/_support/Session/MockSession.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php namespace Tests\Support\Session; - -use CodeIgniter\Session\Session; - -/** - * Class MockSession - * - * Provides a safe way to test the Session class itself, - * that doesn't interact with the session or cookies at all. - */ -class MockSession extends Session -{ - /** - * Holds our "cookie" data. - * - * @var array - */ - public $cookies = []; - - public $didRegenerate = false; - - //-------------------------------------------------------------------- - - /** - * Sets the driver as the session handler in PHP. - * Extracted for easier testing. - */ - protected function setSaveHandler() - { - // session_set_save_handler($this->driver, true); - } - - //-------------------------------------------------------------------- - - /** - * Starts the session. - * Extracted for testing reasons. - */ - protected function startSession() - { - // session_start(); - } - - //-------------------------------------------------------------------- - - /** - * Takes care of setting the cookie on the client side. - * Extracted for testing reasons. - */ - protected function setCookie() - { - $this->cookies[] = [ - $this->sessionCookieName, - session_id(), - (empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration), - $this->cookiePath, - $this->cookieDomain, - $this->cookieSecure, - true, - ]; - } - - //-------------------------------------------------------------------- - - public function regenerate(bool $destroy = false) - { - $this->didRegenerate = true; - $_SESSION['__ci_last_regenerate'] = time(); - } - - //-------------------------------------------------------------------- -} diff --git a/tests/_support/SomeEntity.php b/tests/_support/SomeEntity.php deleted file mode 100644 index 3d229931..00000000 --- a/tests/_support/SomeEntity.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -namespace Tests\Support; - -use CodeIgniter\Entity; - -class SomeEntity extends Entity -{ - protected $attributes = [ - 'foo' => null, - 'bar' => null, - ]; - -} diff --git a/tests/_support/Validation/TestRules.php b/tests/_support/Validation/TestRules.php deleted file mode 100644 index 2c40f32b..00000000 --- a/tests/_support/Validation/TestRules.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php namespace Tests\Support\Validation; - -class TestRules { - - public function customError(string $str, string &$error = null) - { - $error = 'My lovely error'; - - return false; - } - - //-------------------------------------------------------------------- - -} diff --git a/tests/_support/Validation/uploads/phpUxc0ty b/tests/_support/Validation/uploads/phpUxc0ty deleted file mode 100644 index b2b46dad..00000000 Binary files a/tests/_support/Validation/uploads/phpUxc0ty and /dev/null differ diff --git a/tests/_support/View/MockTable.php b/tests/_support/View/MockTable.php deleted file mode 100644 index d286b226..00000000 --- a/tests/_support/View/MockTable.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -namespace Tests\Support\View; - -class MockTable extends \CodeIgniter\View\Table { - - // Override inaccessible protected method - public function __call($method, $params) - { - if (is_callable([$this, '_' . $method])) - { - return call_user_func_array([$this, '_' . $method], $params); - } - - throw new BadMethodCallException('Method ' . $method . ' was not found'); - } - -} diff --git a/tests/_support/View/SampleClass.php b/tests/_support/View/SampleClass.php deleted file mode 100644 index a7c1d3c2..00000000 --- a/tests/_support/View/SampleClass.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php namespace Tests\Support\View; - -/** - * Class SampleClass - * - * This class is only used to provide a reference point - * during tests to make sure that things work as expected. - */ - -class SampleClass { - - public function index() - { - return 'Hello World'; - } - - public function hello() - { - return 'Hello'; - } - - //-------------------------------------------------------------------- - - public function echobox($params) - { - if (is_array($params)) - { - $params = implode(',', $params); - } - - return $params; - } - - //-------------------------------------------------------------------- - - public static function staticEcho($params) - { - if (is_array($params)) - { - $params = implode(',', $params); - } - - return $params; - } - - //-------------------------------------------------------------------- - - public function work($p1, $p2, $p4) - { - return 'Right on'; - } -} diff --git a/tests/_support/View/Views/simple.php b/tests/_support/View/Views/simple.php deleted file mode 100644 index afd6c25f..00000000 --- a/tests/_support/View/Views/simple.php +++ /dev/null @@ -1 +0,0 @@ -<h1><?= $testString ?></h1> \ No newline at end of file diff --git a/tests/_support/View/Views/simpler.php b/tests/_support/View/Views/simpler.php deleted file mode 100644 index 0588b62c..00000000 --- a/tests/_support/View/Views/simpler.php +++ /dev/null @@ -1 +0,0 @@ -<h1>{testString}</h1> \ No newline at end of file diff --git a/tests/_support/_bootstrap.php b/tests/_support/_bootstrap.php deleted file mode 100644 index 65dd9a13..00000000 --- a/tests/_support/_bootstrap.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -ini_set('error_reporting', E_ALL); -; -ini_set('display_errors', '1'); -ini_set('display_startup_errors', '1'); - -// Make sure it recognizes that we're testing. -$_SERVER['CI_ENVIRONMENT'] = 'testing'; -define('ENVIRONMENT', 'testing'); - -// Load our paths config file -require __DIR__ . '/../../app/Config/Paths.php'; - -// path to the directory that holds the front controller (index.php) -define('FCPATH', realpath(__DIR__ . '/../../') . '/public' . DIRECTORY_SEPARATOR); - -// The path to the "tests" directory -define('TESTPATH', realpath(__DIR__ . '/../') . DIRECTORY_SEPARATOR); - -define('SUPPORTPATH', realpath(TESTPATH . '_support/') . DIRECTORY_SEPARATOR); - -// Set environment values that would otherwise stop the framework from functioning during tests. -if (! isset($_SERVER['app.baseURL'])) -{ - $_SERVER['app.baseURL'] = 'http://example.com'; -} - -//-------------------------------------------------------------------- -// Load our TestCase -//-------------------------------------------------------------------- - -require __DIR__ . '/CIUnitTestCase.php'; diff --git a/tests/_support/coverage.txt b/tests/_support/coverage.txt deleted file mode 100644 index 0fb8b92b..00000000 --- a/tests/_support/coverage.txt +++ /dev/null @@ -1,70 +0,0 @@ - - -Code Coverage Report: - 2016-03-31 06:36:22 - - Summary: - Classes: 29.51% (18/61) - Methods: 42.91% (227/529) - Lines: 53.58% (1967/3671) - -\CodeIgniter::CodeIgniter - Methods: 28.57% ( 4/14) Lines: 69.11% ( 85/123) -\CodeIgniter::Controller - Methods: 0.00% ( 0/ 2) Lines: 66.67% ( 6/ 9) -\CodeIgniter\Autoloader::Autoloader - Methods: 42.86% ( 3/ 7) Lines: 89.36% ( 42/ 47) -\CodeIgniter\Autoloader::FileLocator - Methods: 100.00% ( 3/ 3) Lines: 100.00% ( 39/ 39) -\CodeIgniter\CLI::CLI - Methods: 6.25% ( 1/16) Lines: 2.54% ( 3/118) -\CodeIgniter\Config::AutoloadConfig - Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 52/ 52) -\CodeIgniter\Config::BaseConfig - Methods: 50.00% ( 1/ 2) Lines: 83.33% ( 15/ 18) -\CodeIgniter\Config::DotEnv - Methods: 62.50% ( 5/ 8) Lines: 89.29% ( 50/ 56) -\CodeIgniter\Database::BaseBuilder - Methods: 43.33% (39/90) Lines: 62.25% (460/739) -\CodeIgniter\Database::BaseConnection - Methods: 0.00% ( 0/17) Lines: 3.33% ( 2/ 60) -\CodeIgniter\Database::BaseQuery - Methods: 64.71% (11/17) Lines: 74.44% ( 67/ 90) -\CodeIgniter\Debug::Exceptions - Methods: 10.00% ( 1/10) Lines: 2.91% ( 3/103) -\CodeIgniter\Debug::Timer - Methods: 75.00% ( 3/ 4) Lines: 95.24% ( 20/ 21) -\CodeIgniter\HTTP::CLIRequest - Methods: 33.33% ( 2/ 6) Lines: 40.00% ( 14/ 35) -\CodeIgniter\HTTP::CURLRequest - Methods: 62.50% (10/16) Lines: 68.49% (100/146) -\CodeIgniter\HTTP::Header - Methods: 88.89% ( 8/ 9) Lines: 96.88% ( 31/ 32) -\CodeIgniter\HTTP::IncomingRequest - Methods: 60.00% ( 6/10) Lines: 54.05% ( 40/ 74) -\CodeIgniter\HTTP::Message - Methods: 78.57% (11/14) Lines: 86.21% ( 50/ 58) -\CodeIgniter\HTTP::Negotiate - Methods: 75.00% ( 9/12) Lines: 87.65% ( 71/ 81) -\CodeIgniter\HTTP::Request - Methods: 57.14% ( 4/ 7) Lines: 45.79% ( 49/107) -\CodeIgniter\HTTP::Response - Methods: 53.85% ( 7/13) Lines: 79.01% ( 64/ 81) -\CodeIgniter\HTTP::URI - Methods: 81.82% (27/33) Lines: 93.44% (171/183) -\CodeIgniter\HTTP\Files::FileCollection - Methods: 66.67% ( 4/ 6) Lines: 96.49% ( 55/ 57) -\CodeIgniter\HTTP\Files::UploadedFile - Methods: 42.86% ( 6/14) Lines: 39.22% ( 20/ 51) -\CodeIgniter\Hooks::Hooks - Methods: 66.67% ( 4/ 6) Lines: 92.31% ( 36/ 39) -\CodeIgniter\Log::Logger - Methods: 76.92% (10/13) Lines: 92.05% ( 81/ 88) -\CodeIgniter\Router::RouteCollection - Methods: 74.36% (29/39) Lines: 79.64% (133/167) -\CodeIgniter\Router::Router - Methods: 37.50% ( 6/16) Lines: 78.57% ( 77/ 98) -\CodeIgniter\Security::Security - Methods: 100.00% ( 6/ 6) Lines: 100.00% ( 50/ 50) -\CodeIgniter\View::View - Methods: 85.71% ( 6/ 7) Lines: 97.14% ( 34/ 35)