From 105d8e5cfc5bb391896acea85989a3a704aeb85d Mon Sep 17 00:00:00 2001 From: Derek Caswell Date: Wed, 26 Aug 2020 16:28:13 -0600 Subject: [PATCH 001/328] Handling requests sent back from filters This explicitly sets the current request to anything sent back from the filters so that if a filter changed the request, it reflects in the current request. --- system/CodeIgniter.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 71ccf17c160c..8e670704efd2 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -434,6 +434,11 @@ protected function handleRequest(RouteCollectionInterface $routes = null, Cache { return $possibleRedirect->send(); } + // If a Request instance is returned, set the current request to the one that was returned + if ($possibleRedirect instanceof RequestInterface) + { + $this->request = $possibleRedirect; + } } $returned = $this->startController(); From d391065e28466103eac97d5533c513b5f33fb636 Mon Sep 17 00:00:00 2001 From: Derek Caswell Date: Wed, 26 Aug 2020 16:53:04 -0600 Subject: [PATCH 002/328] Adding RequestInterface import statement --- system/CodeIgniter.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 8e670704efd2..dbd71d8e85b6 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -47,6 +47,7 @@ use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\HTTP\RedirectResponse; +use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\Request; use CodeIgniter\HTTP\Response; use CodeIgniter\HTTP\ResponseInterface; From dd573bf4911c49760273bc112e4f0fd7de5a329f Mon Sep 17 00:00:00 2001 From: Derek Caswell Date: Wed, 26 Aug 2020 17:08:33 -0600 Subject: [PATCH 003/328] Updated the docblock on Codeigniter::$request Updated the docblock to allow for a RequestInterface object --- system/CodeIgniter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index dbd71d8e85b6..5be87374a51c 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -102,7 +102,7 @@ class CodeIgniter /** * Current request. * - * @var HTTP\Request|HTTP\IncomingRequest|CLIRequest + * @var HTTP\Request|HTTP\IncomingRequest|CLIRequest|RequestInterface */ protected $request; From 63466a29930d639b9c21c7a106aa567ddabaf83d Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 7 Aug 2020 09:37:57 +0200 Subject: [PATCH 004/328] Add ability to add custom namespace filters to allow config of seperate code not to require change of core systems config files. This will allow easier updates of core system without concern of overwriting custom filters that was added. Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- app/Config/Filters.php | 3 +++ system/Filters/Filters.php | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 11359e7c9d01..fad083636ffd 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -4,6 +4,9 @@ class Filters extends BaseConfig { + //Enable or Disable Auto Discovery of custom Filters + public $discoverFilters = false; + // Makes reading things below nicer, // and simpler to change out script that's used. public $aliases = [ diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 86c427f73165..54fc9debe600 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -42,6 +42,7 @@ use CodeIgniter\Filters\Exceptions\FilterException; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; +use Config\Services; /** * Filters @@ -110,7 +111,40 @@ public function __construct($config, RequestInterface $request, ResponseInterfac $this->config = $config; $this->request = &$request; $this->setResponse($response); + if ($this->config->discoverFilters) + { + $this->discoverFilters(); + } } + + //-------------------------------------------------------------------- + + /** + * If discoverFilters is enabled in Config then system will try to auto + * Discovery custom filters files in Namespaces and allow access to + * The config object via the variable $customfilters as with the routes file + * Sample : + * $customfilters->aliases['custom-auth'] = \Acme\Blob\Filters\BlobAuth::class; + */ + private function discoverFilters() + { + $locater = Services::locator(); + + $customfilters = $this->config; + + $files = $locater->search('Config/Filters.php'); + + foreach ($files as $file) + { + // Don't include our main file again... + if ($file === APPPATH . 'Config/Filters.php') + { + continue; + } + + include $file; + } + } /** * Set the response explicity. From 99b74218a7df355a739bf4b4025aa9fcebc391db Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 7 Aug 2020 10:35:06 +0200 Subject: [PATCH 005/328] Add logic to allow test's that is just based on array values to work Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 54fc9debe600..885774426941 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -111,7 +111,7 @@ public function __construct($config, RequestInterface $request, ResponseInterfac $this->config = $config; $this->request = &$request; $this->setResponse($response); - if ($this->config->discoverFilters) + if ((!is_array($this->config)) && ($this->config->discoverFilters)) { $this->discoverFilters(); } From fd05b8cc03826ba414088c26655e39f03542c18c Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 7 Aug 2020 11:04:22 +0200 Subject: [PATCH 006/328] add discoverFilters to testing array Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/system/Filters/FiltersTest.php | 38 ++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index c66e20754e3c..ec6cf58f054d 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -36,6 +36,7 @@ protected function setUp(): void public function testProcessMethodDetectsCLI() { $config = [ + 'discoverFilters' => false, 'methods' => [ 'cli' => ['foo'], ], @@ -57,6 +58,7 @@ public function testProcessMethodDetectsGetRequests() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'methods' => [ 'get' => ['foo'], ], @@ -78,6 +80,7 @@ public function testProcessMethodRespectsMethod() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'methods' => [ 'post' => ['foo'], 'get' => ['bar'], @@ -98,6 +101,7 @@ public function testProcessMethodIgnoresMethod() $_SERVER['REQUEST_METHOD'] = 'DELETE'; $config = [ + 'discoverFilters' => false, 'methods' => [ 'post' => ['foo'], 'get' => ['bar'], @@ -120,6 +124,7 @@ public function testProcessMethodProcessGlobals() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['bar'], // not excluded @@ -165,6 +170,7 @@ public function testProcessMethodProcessGlobalsWithExcept(array $except) $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => $except], @@ -195,6 +201,7 @@ public function testProcessMethodProcessesFiltersBefore() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'filters' => [ 'foo' => [ 'before' => ['admin/*'], @@ -220,6 +227,7 @@ public function testProcessMethodProcessesFiltersAfter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'filters' => [ 'foo' => [ 'before' => ['admin/*'], @@ -247,6 +255,7 @@ public function testProcessMethodProcessesCombined() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foog' => ['except' => ['admin/*']], @@ -326,6 +335,7 @@ public function testRunThrowsWithInvalidAlias() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => [], 'globals' => [ 'before' => ['invalid'], @@ -348,6 +358,7 @@ public function testRunThrowsWithInvalidClassType() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['invalid' => 'CodeIgniter\Filters\fixtures\InvalidClass'], 'globals' => [ 'before' => ['invalid'], @@ -370,6 +381,7 @@ public function testRunDoesBefore() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => ['google'], @@ -392,6 +404,7 @@ public function testRunDoesAfter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => [], @@ -414,6 +427,7 @@ public function testShortCircuit() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['banana' => 'CodeIgniter\Filters\fixtures\GoogleYou'], 'globals' => [ 'before' => ['banana'], @@ -434,6 +448,7 @@ public function testOtherResult() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => [ 'nowhere' => 'CodeIgniter\Filters\fixtures\GoogleEmpty', 'banana' => 'CodeIgniter\Filters\fixtures\GoogleCurious', @@ -462,6 +477,7 @@ public function testBeforeExceptString() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin/*'], @@ -490,6 +506,7 @@ public function testBeforeExceptInapplicable() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'george/*'], @@ -519,6 +536,7 @@ public function testAfterExceptString() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'bar' @@ -547,6 +565,7 @@ public function testAfterExceptInapplicable() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'bar' @@ -578,6 +597,7 @@ public function testAddFilter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => ['google'], @@ -600,7 +620,9 @@ public function testAddFilterSection() { $_SERVER['REQUEST_METHOD'] = 'GET'; - $config = []; + $config = [ + 'discoverFilters' => false, + ]; $filters = new Filters((object) $config, $this->request, $this->response); @@ -615,7 +637,9 @@ public function testInitializeTwice() { $_SERVER['REQUEST_METHOD'] = 'GET'; - $config = []; + $config = [ + 'discoverFilters' => false, + ]; $filters = new Filters((object) $config, $this->request, $this->response); @@ -632,6 +656,7 @@ public function testEnableFilter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => [], @@ -655,6 +680,7 @@ public function testEnableFilterWithArguments() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['role' => 'CodeIgniter\Filters\fixtures\Role'], 'globals' => [ 'before' => [], @@ -687,6 +713,7 @@ public function testEnableFilterWithNoArguments() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['role' => 'CodeIgniter\Filters\fixtures\Role'], 'globals' => [ 'before' => [], @@ -719,6 +746,7 @@ public function testEnableNonFilter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => [], @@ -741,6 +769,7 @@ public function testMatchesURICaseInsensitively() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'Admin/*'], @@ -783,6 +812,7 @@ public function testFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'filters' => [ 'frak' => [ 'before' => ['admin*'], @@ -813,6 +843,7 @@ public function testGlobalFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -850,6 +881,7 @@ public function testCombinedFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -893,6 +925,7 @@ public function testSegmentedFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -930,6 +963,7 @@ public function testSegmentedFilterMatching() public function testFilterAlitasMultiple() { $config = [ + 'discoverFilters' => false, 'aliases' => [ 'multipeTest' => [ 'CodeIgniter\Filters\fixtures\Multiple1', From 63bfd369faf52c4bde1a7ce4ec62bef8974079ee Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 7 Aug 2020 11:42:08 +0200 Subject: [PATCH 007/328] Add discoverFilters to Honeypot test so that it works Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/system/Honeypot/HoneypotTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/system/Honeypot/HoneypotTest.php b/tests/system/Honeypot/HoneypotTest.php index d24298a8b839..981876aaebd4 100644 --- a/tests/system/Honeypot/HoneypotTest.php +++ b/tests/system/Honeypot/HoneypotTest.php @@ -103,6 +103,7 @@ public function testConfigName() public function testHoneypotFilterBefore() { $config = [ + 'discoverFilters' => false, 'aliases' => ['trap' => '\CodeIgniter\Filters\Honeypot'], 'globals' => [ 'before' => ['trap'], @@ -120,6 +121,7 @@ public function testHoneypotFilterBefore() public function testHoneypotFilterAfter() { $config = [ + 'discoverFilters' => false, 'aliases' => ['trap' => '\CodeIgniter\Filters\Honeypot'], 'globals' => [ 'before' => [], From 1faeacc96cb9b4d4105b8969e6f464d86394e220 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 7 Aug 2020 18:44:34 +0200 Subject: [PATCH 008/328] update as suggested by change request Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 885774426941..5e4ce1991e20 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -111,7 +111,7 @@ public function __construct($config, RequestInterface $request, ResponseInterfac $this->config = $config; $this->request = &$request; $this->setResponse($response); - if ((!is_array($this->config)) && ($this->config->discoverFilters)) + if (isset($this->config->discoverFilters) && $this->config->discoverFilters) { $this->discoverFilters(); } From 25de82398711dc5b228510b288bde823ce14dc17 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Tue, 18 Aug 2020 07:02:27 +0200 Subject: [PATCH 009/328] Add Custom filter Testing logic Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Config/Filters.php | 3 +++ tests/_support/Filters/Customfilter.php | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/_support/Config/Filters.php create mode 100644 tests/_support/Filters/Customfilter.php diff --git a/tests/_support/Config/Filters.php b/tests/_support/Config/Filters.php new file mode 100644 index 000000000000..511d92a1a42d --- /dev/null +++ b/tests/_support/Config/Filters.php @@ -0,0 +1,3 @@ +aliases['api-auth'] = \Raapp\Server\Filters\ApiAuth::class; diff --git a/tests/_support/Filters/Customfilter.php b/tests/_support/Filters/Customfilter.php new file mode 100644 index 000000000000..daf0a8615d47 --- /dev/null +++ b/tests/_support/Filters/Customfilter.php @@ -0,0 +1,19 @@ + Date: Tue, 18 Aug 2020 07:03:51 +0200 Subject: [PATCH 010/328] Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Config/Filters.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/_support/Config/Filters.php b/tests/_support/Config/Filters.php index 511d92a1a42d..3464cca160cc 100644 --- a/tests/_support/Config/Filters.php +++ b/tests/_support/Config/Filters.php @@ -1,3 +1,2 @@ -aliases['api-auth'] = \Raapp\Server\Filters\ApiAuth::class; +aliases['test-customfilter'] = Tests\Support\Filters\Customfilter::class; From 70bd012273fe394feb0cf7affcc1c92eb7551611 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Tue, 18 Aug 2020 07:17:27 +0200 Subject: [PATCH 011/328] Adding filter to route to test that custom filter actually works Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Config/Routes.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/_support/Config/Routes.php b/tests/_support/Config/Routes.php index 8102b4a95ae1..1eceaa3e3327 100644 --- a/tests/_support/Config/Routes.php +++ b/tests/_support/Config/Routes.php @@ -5,3 +5,6 @@ */ $routes->add('testing', 'TestController::index', ['as' => 'testing-index']); + +/*Will throw error if filter is not found, thus testing the custom filter ability*/ +$routes->add('testingfilter', 'TestController::index', ['filter' => 'test-customfilter']); From e74748b0d021c6bfa6914bc75acc8a9a02f3bb02 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Tue, 18 Aug 2020 12:06:50 +0200 Subject: [PATCH 012/328] Add custom filter test inside route to make sure custom filter was loaded and can be used Call then in CodeIgniter test to make sure the loading of route and filters work till the calling of the controller, thus whole code module loading successs Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Config/Routes.php | 2 +- tests/system/CodeIgniterTest.php | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/_support/Config/Routes.php b/tests/_support/Config/Routes.php index 1eceaa3e3327..644526c5c0b9 100644 --- a/tests/_support/Config/Routes.php +++ b/tests/_support/Config/Routes.php @@ -7,4 +7,4 @@ $routes->add('testing', 'TestController::index', ['as' => 'testing-index']); /*Will throw error if filter is not found, thus testing the custom filter ability*/ -$routes->add('testingfilter', 'TestController::index', ['filter' => 'test-customfilter']); +$routes->add('testingfilter', 'Hello::index', ['filter' => 'test-customfilter']); diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index baacda948642..45979188b63f 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -56,6 +56,23 @@ public function testRunEmptyDefaultRoute() $this->assertStringContainsString('Welcome to CodeIgniter', $output); } + + //-------------------------------------------------------------------- + + public function testRunCustomFilterRoute() + { + $_SERVER['argv'] = [ + 'index.php', + 'testingfilter', + ]; + $_SERVER['argc'] = 2; + + ob_start(); + $this->codeigniter->useSafeOutput(true)->run(); + $output = ob_get_clean(); + + $this->assertStringContainsString('Hello', $output); + } //-------------------------------------------------------------------- From 99fded512bed783bfc88160a9132151324f9eab7 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Tue, 18 Aug 2020 12:35:18 +0200 Subject: [PATCH 013/328] forgot to add request uri Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/system/CodeIgniterTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 45979188b63f..26cb07a0f705 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -66,6 +66,7 @@ public function testRunCustomFilterRoute() 'testingfilter', ]; $_SERVER['argc'] = 2; + $_SERVER['REQUEST_URI'] = 'testingfilter'; ob_start(); $this->codeigniter->useSafeOutput(true)->run(); From c7cad71c669e8122e3514110ef2dce6606f1bec7 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Tue, 18 Aug 2020 14:19:55 +0200 Subject: [PATCH 014/328] routes did not have namespace for some reason, made rout point directly to code module controller Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Config/Routes.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/_support/Config/Routes.php b/tests/_support/Config/Routes.php index 644526c5c0b9..ccc19398ca0e 100644 --- a/tests/_support/Config/Routes.php +++ b/tests/_support/Config/Routes.php @@ -1,5 +1,4 @@ -add('testing', 'TestController::index', ['as' => 'testing-index']); /*Will throw error if filter is not found, thus testing the custom filter ability*/ -$routes->add('testingfilter', 'Hello::index', ['filter' => 'test-customfilter']); +$routes->add('testingfilter', '\Tests\Support\Controllers\Hello::index', ['filter' => 'test-customfilter']); From bc4a61641920589f713b1fcbf9b7e27f9a006733 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Thu, 20 Aug 2020 06:46:55 +0200 Subject: [PATCH 015/328] Moved testing to not test with route but test with filter side only via method filtering, this will then test the loading and calling of the filter correctly Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Filters/Customfilter.php | 4 +++- tests/system/CodeIgniterTest.php | 18 ------------------ tests/system/Filters/FiltersTest.php | 24 ++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/tests/_support/Filters/Customfilter.php b/tests/_support/Filters/Customfilter.php index daf0a8615d47..79d76d072808 100644 --- a/tests/_support/Filters/Customfilter.php +++ b/tests/_support/Filters/Customfilter.php @@ -7,7 +7,9 @@ class Customfilter implements \CodeIgniter\Filters\FilterInterface public function before(RequestInterface $request, $arguments = null) { -// + $request->url = 'http://hellowworld.com'; + + return $request; } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 26cb07a0f705..8c60bdb6033c 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -59,24 +59,6 @@ public function testRunEmptyDefaultRoute() //-------------------------------------------------------------------- - public function testRunCustomFilterRoute() - { - $_SERVER['argv'] = [ - 'index.php', - 'testingfilter', - ]; - $_SERVER['argc'] = 2; - $_SERVER['REQUEST_URI'] = 'testingfilter'; - - ob_start(); - $this->codeigniter->useSafeOutput(true)->run(); - $output = ob_get_clean(); - - $this->assertStringContainsString('Hello', $output); - } - - //-------------------------------------------------------------------- - public function testRunClosureRoute() { $_SERVER['argv'] = [ diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index ec6cf58f054d..4ffb02a1f893 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -73,6 +73,7 @@ public function testProcessMethodDetectsGetRequests() $this->assertEquals($expected, $filters->initialize()->getFilters()); } + //-------------------------------------------------------------------- public function testProcessMethodRespectsMethod() @@ -350,6 +351,29 @@ public function testRunThrowsWithInvalidAlias() $filters->run($uri); } + + //-------------------------------------------------------------------- + + public function testCustomFiltersLoad() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + + $config = [ + 'discoverFilters' => true, + 'globals' => [ + 'before' => ['test-customfilter'], + 'after' => [], + ], + ]; + + $filters = new Filters((object) $config, $this->request, $this->response); + $uri = 'admin/foo/bar'; + + $request = $filters->run($uri, 'before'); + + $this->assertEquals('http://hellowworld.com', $request->url); + } + //-------------------------------------------------------------------- From ad9aca0723457031032d4b6657c117b570c2233a Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:11:55 +0200 Subject: [PATCH 016/328] add aliases variables to config array Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/system/Filters/FiltersTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index 4ffb02a1f893..d1fff38ccbcd 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -360,6 +360,7 @@ public function testCustomFiltersLoad() $config = [ 'discoverFilters' => true, + 'aliases' => [], 'globals' => [ 'before' => ['test-customfilter'], 'after' => [], From 0c80df92cc02ec7c79fb3a0ca01bae9a35f94ffb Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Thu, 20 Aug 2020 10:25:26 +0200 Subject: [PATCH 017/328] Added auotload config values to try and get the namespaces correct for this test Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Config/Routes.php | 4 ---- tests/system/Filters/FiltersTest.php | 10 ++++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/_support/Config/Routes.php b/tests/_support/Config/Routes.php index ccc19398ca0e..c7be66199e05 100644 --- a/tests/_support/Config/Routes.php +++ b/tests/_support/Config/Routes.php @@ -2,8 +2,4 @@ /** * This is a simple file to include for testing the RouteCollection class. */ - $routes->add('testing', 'TestController::index', ['as' => 'testing-index']); - -/*Will throw error if filter is not found, thus testing the custom filter ability*/ -$routes->add('testingfilter', '\Tests\Support\Controllers\Hello::index', ['filter' => 'test-customfilter']); diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index d1fff38ccbcd..9b55ab429de3 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -27,9 +27,19 @@ protected function setUp(): void { parent::setUp(); Services::reset(); + + $defaults = [ + 'Config' => APPPATH . 'Config', + 'App' => APPPATH, + 'Tests\Support' => TESTPATH . '_support', + ]; + Services::autoloader()->addNamespace($defaults); + $loader = Services::locator(); + $this->request = Services::request(); $this->response = Services::response(); + } //-------------------------------------------------------------------- From 3591479c899e021e6650856693aa1aabdf17dc1e Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Thu, 20 Aug 2020 11:03:10 +0200 Subject: [PATCH 018/328] Correct class creation in custom filter Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Config/Filters.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/_support/Config/Filters.php b/tests/_support/Config/Filters.php index 3464cca160cc..de3976b09e46 100644 --- a/tests/_support/Config/Filters.php +++ b/tests/_support/Config/Filters.php @@ -1,2 +1,4 @@ -aliases['test-customfilter'] = Tests\Support\Filters\Customfilter::class; +aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; From f3857e891c520059b3a4feaa4c03d249d845698d Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Thu, 20 Aug 2020 12:07:43 +0200 Subject: [PATCH 019/328] added uses clases in custom filter now getting called at last in testing Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- tests/_support/Filters/Customfilter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/_support/Filters/Customfilter.php b/tests/_support/Filters/Customfilter.php index 79d76d072808..c05abf622cf4 100644 --- a/tests/_support/Filters/Customfilter.php +++ b/tests/_support/Filters/Customfilter.php @@ -1,6 +1,10 @@ Date: Thu, 20 Aug 2020 15:24:50 +0200 Subject: [PATCH 020/328] Changed to rather use Modules config to enable disable searching for custom filters than a config optionin filters config file, is more inline with CI design Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- app/Config/Filters.php | 4 +--- app/Config/Modules.php | 1 + system/Filters/Filters.php | 17 +++++++++++-- tests/system/Filters/FiltersTest.php | 33 -------------------------- tests/system/Honeypot/HoneypotTest.php | 2 -- 5 files changed, 17 insertions(+), 40 deletions(-) diff --git a/app/Config/Filters.php b/app/Config/Filters.php index fad083636ffd..4fabceb63eba 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -4,9 +4,7 @@ class Filters extends BaseConfig { - //Enable or Disable Auto Discovery of custom Filters - public $discoverFilters = false; - + // Makes reading things below nicer, // and simpler to change out script that's used. public $aliases = [ diff --git a/app/Config/Modules.php b/app/Config/Modules.php index 40cb987564ca..3bcef9252f93 100644 --- a/app/Config/Modules.php +++ b/app/Config/Modules.php @@ -41,5 +41,6 @@ class Modules extends CoreModules 'registrars', 'routes', 'services', + 'filters', ]; } diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 5e4ce1991e20..c46361116fe1 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -96,6 +96,13 @@ class Filters * @var array */ protected $arguments = []; + + /** + * Handle to the modules config. + * + * @var \Config\Modules + */ + protected $moduleConfig; //-------------------------------------------------------------------- @@ -105,13 +112,19 @@ class Filters * @param \Config\Filters $config * @param RequestInterface $request * @param ResponseInterface $response + * @param $moduleConfig */ - public function __construct($config, RequestInterface $request, ResponseInterface $response) + public function __construct($config, RequestInterface $request, ResponseInterface $response,$moduleConfig = null) { $this->config = $config; $this->request = &$request; $this->setResponse($response); - if (isset($this->config->discoverFilters) && $this->config->discoverFilters) + $this->moduleConfig = $moduleConfig; + if ($this->moduleConfig == null) + { + $this->moduleConfig = config('Modules'); + } + if ($this->moduleConfig->shouldDiscover('filters')) { $this->discoverFilters(); } diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index 9b55ab429de3..afb87e0b5167 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -46,7 +46,6 @@ protected function setUp(): void public function testProcessMethodDetectsCLI() { $config = [ - 'discoverFilters' => false, 'methods' => [ 'cli' => ['foo'], ], @@ -68,7 +67,6 @@ public function testProcessMethodDetectsGetRequests() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'methods' => [ 'get' => ['foo'], ], @@ -91,7 +89,6 @@ public function testProcessMethodRespectsMethod() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'methods' => [ 'post' => ['foo'], 'get' => ['bar'], @@ -112,7 +109,6 @@ public function testProcessMethodIgnoresMethod() $_SERVER['REQUEST_METHOD'] = 'DELETE'; $config = [ - 'discoverFilters' => false, 'methods' => [ 'post' => ['foo'], 'get' => ['bar'], @@ -135,7 +131,6 @@ public function testProcessMethodProcessGlobals() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['bar'], // not excluded @@ -181,7 +176,6 @@ public function testProcessMethodProcessGlobalsWithExcept(array $except) $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => $except], @@ -212,7 +206,6 @@ public function testProcessMethodProcessesFiltersBefore() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'filters' => [ 'foo' => [ 'before' => ['admin/*'], @@ -238,7 +231,6 @@ public function testProcessMethodProcessesFiltersAfter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'filters' => [ 'foo' => [ 'before' => ['admin/*'], @@ -266,7 +258,6 @@ public function testProcessMethodProcessesCombined() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foog' => ['except' => ['admin/*']], @@ -346,7 +337,6 @@ public function testRunThrowsWithInvalidAlias() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => [], 'globals' => [ 'before' => ['invalid'], @@ -369,7 +359,6 @@ public function testCustomFiltersLoad() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => true, 'aliases' => [], 'globals' => [ 'before' => ['test-customfilter'], @@ -393,7 +382,6 @@ public function testRunThrowsWithInvalidClassType() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['invalid' => 'CodeIgniter\Filters\fixtures\InvalidClass'], 'globals' => [ 'before' => ['invalid'], @@ -416,7 +404,6 @@ public function testRunDoesBefore() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => ['google'], @@ -439,7 +426,6 @@ public function testRunDoesAfter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => [], @@ -462,7 +448,6 @@ public function testShortCircuit() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['banana' => 'CodeIgniter\Filters\fixtures\GoogleYou'], 'globals' => [ 'before' => ['banana'], @@ -483,7 +468,6 @@ public function testOtherResult() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => [ 'nowhere' => 'CodeIgniter\Filters\fixtures\GoogleEmpty', 'banana' => 'CodeIgniter\Filters\fixtures\GoogleCurious', @@ -512,7 +496,6 @@ public function testBeforeExceptString() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin/*'], @@ -541,7 +524,6 @@ public function testBeforeExceptInapplicable() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'george/*'], @@ -571,7 +553,6 @@ public function testAfterExceptString() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'bar' @@ -600,7 +581,6 @@ public function testAfterExceptInapplicable() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'bar' @@ -632,7 +612,6 @@ public function testAddFilter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => ['google'], @@ -656,7 +635,6 @@ public function testAddFilterSection() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, ]; $filters = new Filters((object) $config, $this->request, $this->response); @@ -673,7 +651,6 @@ public function testInitializeTwice() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, ]; $filters = new Filters((object) $config, $this->request, $this->response); @@ -691,7 +668,6 @@ public function testEnableFilter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => [], @@ -715,7 +691,6 @@ public function testEnableFilterWithArguments() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['role' => 'CodeIgniter\Filters\fixtures\Role'], 'globals' => [ 'before' => [], @@ -748,7 +723,6 @@ public function testEnableFilterWithNoArguments() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['role' => 'CodeIgniter\Filters\fixtures\Role'], 'globals' => [ 'before' => [], @@ -781,7 +755,6 @@ public function testEnableNonFilter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'aliases' => ['google' => 'CodeIgniter\Filters\fixtures\GoogleMe'], 'globals' => [ 'before' => [], @@ -804,7 +777,6 @@ public function testMatchesURICaseInsensitively() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'Admin/*'], @@ -847,7 +819,6 @@ public function testFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'filters' => [ 'frak' => [ 'before' => ['admin*'], @@ -878,7 +849,6 @@ public function testGlobalFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -916,7 +886,6 @@ public function testCombinedFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -960,7 +929,6 @@ public function testSegmentedFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ - 'discoverFilters' => false, 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -998,7 +966,6 @@ public function testSegmentedFilterMatching() public function testFilterAlitasMultiple() { $config = [ - 'discoverFilters' => false, 'aliases' => [ 'multipeTest' => [ 'CodeIgniter\Filters\fixtures\Multiple1', diff --git a/tests/system/Honeypot/HoneypotTest.php b/tests/system/Honeypot/HoneypotTest.php index 981876aaebd4..d24298a8b839 100644 --- a/tests/system/Honeypot/HoneypotTest.php +++ b/tests/system/Honeypot/HoneypotTest.php @@ -103,7 +103,6 @@ public function testConfigName() public function testHoneypotFilterBefore() { $config = [ - 'discoverFilters' => false, 'aliases' => ['trap' => '\CodeIgniter\Filters\Honeypot'], 'globals' => [ 'before' => ['trap'], @@ -121,7 +120,6 @@ public function testHoneypotFilterBefore() public function testHoneypotFilterAfter() { $config = [ - 'discoverFilters' => false, 'aliases' => ['trap' => '\CodeIgniter\Filters\Honeypot'], 'globals' => [ 'before' => [], From 9959ebbfc637d4db9d75c917f1cb6e6674fa9433 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Thu, 20 Aug 2020 15:28:02 +0200 Subject: [PATCH 021/328] phpdoc param fix Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index c46361116fe1..4301bfd8b214 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -112,7 +112,7 @@ class Filters * @param \Config\Filters $config * @param RequestInterface $request * @param ResponseInterface $response - * @param $moduleConfig + * @param \Config\Modules $moduleConfig */ public function __construct($config, RequestInterface $request, ResponseInterface $response,$moduleConfig = null) { From 36665da7348dfcee5c7a340ecde99c1cbe316591 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 21 Aug 2020 05:58:50 +0200 Subject: [PATCH 022/328] change request as per @samsonasik Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 4301bfd8b214..86b022c0d920 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -112,7 +112,7 @@ class Filters * @param \Config\Filters $config * @param RequestInterface $request * @param ResponseInterface $response - * @param \Config\Modules $moduleConfig + * @param \Config\Modules|null $moduleConfig */ public function __construct($config, RequestInterface $request, ResponseInterface $response,$moduleConfig = null) { From 907a62f80f15e44a1dbfe71f65ab02b08b53ceee Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 21 Aug 2020 06:00:33 +0200 Subject: [PATCH 023/328] change reqeust as per @samsonasik Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 86b022c0d920..9c1807d09df9 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -119,11 +119,10 @@ public function __construct($config, RequestInterface $request, ResponseInterfac $this->config = $config; $this->request = &$request; $this->setResponse($response); + $this->moduleConfig = $moduleConfig; - if ($this->moduleConfig == null) - { - $this->moduleConfig = config('Modules'); - } + $this->moduleConfig = $moduleConfig ?? config('Modules'); + if ($this->moduleConfig->shouldDiscover('filters')) { $this->discoverFilters(); From 599594fef2ba5e875106debe3e9723efa2541fc0 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Fri, 21 Aug 2020 06:08:57 +0200 Subject: [PATCH 024/328] need \Config\Modules type hint from @samsonasik I Think this is how it is done, or what is asked. Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 9c1807d09df9..563d708cf5dd 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -43,6 +43,7 @@ use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Config\Services; +use Config\Modules; /** * Filters @@ -114,7 +115,7 @@ class Filters * @param ResponseInterface $response * @param \Config\Modules|null $moduleConfig */ - public function __construct($config, RequestInterface $request, ResponseInterface $response,$moduleConfig = null) + public function __construct($config, RequestInterface $request, ResponseInterface $response,Modules $moduleConfig = null) { $this->config = $config; $this->request = &$request; From fc6ccfa5409aea63c5b60d5559312ef39e11170e Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Sat, 22 Aug 2020 18:43:59 +0200 Subject: [PATCH 025/328] changes as per change request from @michalsn Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 13 ++++++------- tests/_support/Config/Filters.php | 2 +- tests/system/Filters/FiltersTest.php | 7 ++----- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 563d708cf5dd..c58f99afd943 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -123,11 +123,6 @@ public function __construct($config, RequestInterface $request, ResponseInterfac $this->moduleConfig = $moduleConfig; $this->moduleConfig = $moduleConfig ?? config('Modules'); - - if ($this->moduleConfig->shouldDiscover('filters')) - { - $this->discoverFilters(); - } } //-------------------------------------------------------------------- @@ -143,7 +138,7 @@ private function discoverFilters() { $locater = Services::locator(); - $customfilters = $this->config; + $filters = $this->config; $files = $locater->search('Config/Filters.php'); @@ -282,7 +277,11 @@ public function initialize(string $uri = null) { return $this; } - + if ($this->moduleConfig->shouldDiscover('filters')) + { + $this->discoverFilters(); + } + $this->processGlobals($uri); $this->processMethods(); $this->processFilters($uri); diff --git a/tests/_support/Config/Filters.php b/tests/_support/Config/Filters.php index de3976b09e46..269c5b478617 100644 --- a/tests/_support/Config/Filters.php +++ b/tests/_support/Config/Filters.php @@ -1,4 +1,4 @@ aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; +$filters->aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index afb87e0b5167..36efe961d47f 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -35,7 +35,6 @@ protected function setUp(): void ]; Services::autoloader()->addNamespace($defaults); - $loader = Services::locator(); $this->request = Services::request(); $this->response = Services::response(); @@ -634,8 +633,7 @@ public function testAddFilterSection() { $_SERVER['REQUEST_METHOD'] = 'GET'; - $config = [ - ]; + $config = []; $filters = new Filters((object) $config, $this->request, $this->response); @@ -650,8 +648,7 @@ public function testInitializeTwice() { $_SERVER['REQUEST_METHOD'] = 'GET'; - $config = [ - ]; + $config = []; $filters = new Filters((object) $config, $this->request, $this->response); From d5665020f96e594707c62b040b7655239b898b8d Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Tue, 25 Aug 2020 07:16:05 +0200 Subject: [PATCH 026/328] change request as per @michalsn Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- system/Filters/Filters.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index c58f99afd943..552a5e744e78 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -115,13 +115,12 @@ class Filters * @param ResponseInterface $response * @param \Config\Modules|null $moduleConfig */ - public function __construct($config, RequestInterface $request, ResponseInterface $response,Modules $moduleConfig = null) + public function __construct($config, RequestInterface $request, ResponseInterface $response, Modules $moduleConfig = null) { $this->config = $config; $this->request = &$request; $this->setResponse($response); - $this->moduleConfig = $moduleConfig; $this->moduleConfig = $moduleConfig ?? config('Modules'); } @@ -132,7 +131,7 @@ public function __construct($config, RequestInterface $request, ResponseInterfac * Discovery custom filters files in Namespaces and allow access to * The config object via the variable $customfilters as with the routes file * Sample : - * $customfilters->aliases['custom-auth'] = \Acme\Blob\Filters\BlobAuth::class; + * $filters->aliases['custom-auth'] = \Acme\Blob\Filters\BlobAuth::class; */ private function discoverFilters() { From 6a0d06c868e5b0b5d54c67a4db050456e5b37da6 Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Thu, 27 Aug 2020 11:32:25 +0200 Subject: [PATCH 027/328] Changes as per request from @MGatner Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- app/Config/Filters.php | 1 - tests/_support/Config/Routes.php | 2 +- tests/system/CodeIgniterTest.php | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 4fabceb63eba..11359e7c9d01 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -4,7 +4,6 @@ class Filters extends BaseConfig { - // Makes reading things below nicer, // and simpler to change out script that's used. public $aliases = [ diff --git a/tests/_support/Config/Routes.php b/tests/_support/Config/Routes.php index c7be66199e05..78dc6cfd4317 100644 --- a/tests/_support/Config/Routes.php +++ b/tests/_support/Config/Routes.php @@ -1,4 +1,4 @@ -assertStringContainsString('Welcome to CodeIgniter', $output); } - + //-------------------------------------------------------------------- public function testRunClosureRoute() From 50303d2ff5031bec0a45a2d5bdb2d8d62d7c559e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 27 Aug 2020 16:44:59 +0700 Subject: [PATCH 028/328] Fix phpstan error notice after update to v0.12.40 --- system/HTTP/Response.php | 4 ++-- system/I18n/Time.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index 27ce1a6be510..383649f8b5ea 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -830,7 +830,7 @@ public function redirect(string $uri, string $method = 'auto', int $code = null) * @param string $prefix Cookie name prefix * @param boolean $secure Whether to only transfer cookies via SSL * @param boolean $httponly Whether only make the cookie accessible via HTTP (no javascript) - * @param string|null $samesite + * @param string|null $samesite * * @return $this */ @@ -899,7 +899,7 @@ public function setCookie( } else { - $expire = ($expire > 0) ? time() + $expire : 0; // @phpstan-ignore-line + $expire = ($expire > 0) ? time() + $expire : 0; } $cookie = [ diff --git a/system/I18n/Time.php b/system/I18n/Time.php index d2c535100d12..329b0cf5b835 100644 --- a/system/I18n/Time.php +++ b/system/I18n/Time.php @@ -557,7 +557,7 @@ public function getAge() $time = $this->getTimestamp(); // future dates have no age - return max(0, date('Y', $now) - date('Y', $time)); // @phpstan-ignore-line + return max(0, date('Y', $now) - date('Y', $time)); } //-------------------------------------------------------------------- From 6dadbaaaca0656036f949755de8779cd1194d963 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 27 Aug 2020 16:57:14 +0700 Subject: [PATCH 029/328] update phpstan to v0.12.40 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8d8f2cd66f90..087b285d9e2e 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "codeigniter4/codeigniter4-standard": "^1.0", "fzaninotto/faker": "^1.9@dev", "mikey179/vfsstream": "1.6.*", - "phpstan/phpstan": "^0.12.37", + "phpstan/phpstan": "^0.12.40", "phpunit/phpunit": "^8.5", "predis/predis": "^1.1", "squizlabs/php_codesniffer": "^3.3" From 26748bcaaa09be7d783f07ef435d5b25326cdd13 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 27 Aug 2020 18:18:59 +0700 Subject: [PATCH 030/328] update phpstan version to "^0.12" Co-authored-by: John Paul E. Balandan, CPA <51850998+paulbalandan@users.noreply.github.com> --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 087b285d9e2e..123dc7b7a15b 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "codeigniter4/codeigniter4-standard": "^1.0", "fzaninotto/faker": "^1.9@dev", "mikey179/vfsstream": "1.6.*", - "phpstan/phpstan": "^0.12.40", + "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^8.5", "predis/predis": "^1.1", "squizlabs/php_codesniffer": "^3.3" From 5bd696f9389b1ed607902c126990bc69dec0bb85 Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 26 Aug 2020 15:19:03 +0000 Subject: [PATCH 031/328] Standardize Action workflows --- ...{apidocs-action.yml => deploy-apidocs.yml} | 25 ++++----- .../{deploy.yml => deploy-framework.yml} | 14 +++-- .github/workflows/deploy-userguide-latest.yml | 54 +++++++++++++++++++ .github/workflows/docs_nightly.yml | 52 ------------------ .github/workflows/static-analysis-check.yml | 37 ------------- .github/workflows/test-phpstan.yml | 35 ++++++++++++ .github/workflows/test-userguide.yml | 23 ++++++++ .github/workflows/userguide_ci.yml | 17 ------ 8 files changed, 134 insertions(+), 123 deletions(-) rename .github/workflows/{apidocs-action.yml => deploy-apidocs.yml} (75%) rename .github/workflows/{deploy.yml => deploy-framework.yml} (87%) create mode 100644 .github/workflows/deploy-userguide-latest.yml delete mode 100644 .github/workflows/docs_nightly.yml delete mode 100644 .github/workflows/static-analysis-check.yml create mode 100644 .github/workflows/test-phpstan.yml create mode 100644 .github/workflows/test-userguide.yml delete mode 100644 .github/workflows/userguide_ci.yml diff --git a/.github/workflows/apidocs-action.yml b/.github/workflows/deploy-apidocs.yml similarity index 75% rename from .github/workflows/apidocs-action.yml rename to .github/workflows/deploy-apidocs.yml index ecad14f2399c..393adaf7c277 100644 --- a/.github/workflows/apidocs-action.yml +++ b/.github/workflows/deploy-apidocs.yml @@ -1,4 +1,7 @@ -name: API Documentation +# When changes are pushed to the develop branch, +# build the current version of the API documentation +# with phpDocumentor and deploy it to the api branch. +name: Deploy API Documentation on: push: @@ -10,10 +13,9 @@ on: jobs: build: - name: Generate API Docs + name: Deploy to api if: (github.repository == 'codeigniter4/CodeIgniter4') runs-on: ubuntu-latest - steps: - name: Setup credentials run: | @@ -25,7 +27,7 @@ jobs: with: path: source - - name: Checkout api + - name: Checkout target uses: actions/checkout@v2 with: repository: codeigniter4/api @@ -35,13 +37,12 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.2' - extensions: intl, json, mbstring, mysqlnd, xdebug, xml, sqlite3 - coverage: xdebug + php-version: '7.4' + extensions: intl - - name: Download GraphViz for phpDocumentor + - name: Install GraphViz for phpDocumentor run: | - sudo apt-get install -yq graphviz + sudo apt install -yq graphviz - name: Download phpDocumentor run: | @@ -50,21 +51,21 @@ jobs: -L https://github.com/phpDocumentor/phpDocumentor/releases/download/v2.9.1/phpDocumentor.phar \ -o admin/phpDocumentor.phar - - name: Prepare API + - name: Prepare run: | cd ./api rm -rf ./api/docs* mkdir -p ./api/docs git reset --hard master - - name: Make the new API docs + - name: Build run: | cd ./source chmod +x admin/phpDocumentor.phar admin/phpDocumentor.phar cp -R ${GITHUB_WORKSPACE}/source/api/build/* ${GITHUB_WORKSPACE}/api/docs - - name: Deploy API + - name: Deploy run: | cd ./api git add . diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy-framework.yml similarity index 87% rename from .github/workflows/deploy.yml rename to .github/workflows/deploy-framework.yml index b7bc1892ea37..ce925ed66639 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy-framework.yml @@ -1,4 +1,6 @@ -name: Deploy +# When a new Release is created, deploy relevant +# files to each of the generated repos. +name: Deploy Framework on: release: @@ -6,8 +8,9 @@ on: jobs: framework: + name: Deploy to framework + if: (github.repository == 'codeigniter4/CodeIgniter4') runs-on: ubuntu-latest - steps: - name: Identify run: | @@ -33,7 +36,7 @@ jobs: run: ./source/.github/scripts/deploy-framework ${GITHUB_WORKSPACE}/source ${GITHUB_WORKSPACE}/framework ${GITHUB_REF##*/} - name: Release - uses: actions/github-script@0.8.0 + uses: actions/github-script@v3 with: github-token: ${{secrets.ACCESS_TOKEN}} script: | @@ -50,8 +53,9 @@ jobs: }) appstarter: + name: Deploy to appstarter + if: (github.repository == 'codeigniter4/CodeIgniter4') runs-on: ubuntu-latest - steps: - name: Identify run: | @@ -77,7 +81,7 @@ jobs: run: ./source/.github/scripts/deploy-appstarter ${GITHUB_WORKSPACE}/source ${GITHUB_WORKSPACE}/appstarter ${GITHUB_REF##*/} - name: Release - uses: actions/github-script@0.8.0 + uses: actions/github-script@v3 with: github-token: ${{secrets.ACCESS_TOKEN}} script: | diff --git a/.github/workflows/deploy-userguide-latest.yml b/.github/workflows/deploy-userguide-latest.yml new file mode 100644 index 000000000000..4ed64cf3d0b9 --- /dev/null +++ b/.github/workflows/deploy-userguide-latest.yml @@ -0,0 +1,54 @@ +# When changes are pushed to the develop branch, +# build the current version of the User Guide +# with Sphinx and deploy it to the gh-pages branch. +# +# @todo Consolidate checkouts +name: Deploy User Guide (latest) + +on: + push: + branches: + - 'develop' + paths: + - 'user_guide_src/**' + +jobs: + build: + name: Deploy to gh-pages + if: (github.repository == 'codeigniter4/CodeIgniter4') + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + # Build the latest User Guide + - name: Build with Sphinx + uses: ammaraskar/sphinx-action@v0.4 + with: + docs-folder: user_guide_src/ + + # Create an artifact of the html output + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: HTML Documentation + path: user_guide_src/build/html/ + + # Commit changes to the gh-pages branch + - name: Commit changes + run: | + git clone https://github.com/codeigniter4/CodeIgniter4.git --branch gh-pages --single-branch gh-pages + cp -r user_guide_src/build/html/* gh-pages/ + cd gh-pages + git config --local user.email "action@github.com" + git config --local user.name "${GITHUB_ACTOR}" + git add . + # Ignore failures due to lack of changes + git commit -m "Update User Guide" -a || true + + - name: Push changes + uses: ad-m/github-push-action@v0.6 + with: + branch: gh-pages + directory: gh-pages + github_token: ${{ secrets.ACCESS_TOKEN }} diff --git a/.github/workflows/docs_nightly.yml b/.github/workflows/docs_nightly.yml deleted file mode 100644 index 0093e9fbdd1c..000000000000 --- a/.github/workflows/docs_nightly.yml +++ /dev/null @@ -1,52 +0,0 @@ -# When changes are pushed to the develop branch, -# build the current version of the docs and -# deploy to gh-pages so that our development -# docs are always up to date. -name: "Update development docs" - -on: - push: - branches: - - 'develop' - paths: - - 'user_guide_src/**' - -jobs: - build: - if: (github.repository == 'codeigniter4/CodeIgniter4') - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - # Build the current docs - - uses: ammaraskar/sphinx-action@master - with: - docs-folder: "user_guide_src/" - - # Create an artifact of the html output - - uses: actions/upload-artifact@v2 - with: - name: HTML Documentation - path: user_guide_src/build/html/ - - # Commit changes to the gh-pages branch - - name: Commit documentation changes - run: | - git clone https://github.com/codeigniter4/CodeIgniter4.git --branch gh-pages --single-branch gh-pages - cp -r user_guide_src/build/html/* gh-pages/ - cd gh-pages - git config --local user.email "action@github.com" - git config --local user.name "${GITHUB_ACTOR}" - git add . - git commit -m "Update documentation" -a || true - # The above command will fail if no changes were present, so we ignore - # that. - - - name: Push changes - uses: ad-m/github-push-action@master - with: - branch: gh-pages - directory: gh-pages - github_token: ${{ secrets.ACCESS_TOKEN }} diff --git a/.github/workflows/static-analysis-check.yml b/.github/workflows/static-analysis-check.yml deleted file mode 100644 index dbd2166128d6..000000000000 --- a/.github/workflows/static-analysis-check.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: "Static Analysis Check" - -on: - pull_request: - branches: - - 'develop' - - '4.1' - paths: - - 'app/**' - - 'system/**' - push: - branches: - - 'develop' - - '4.1' - paths: - - 'app/**' - - 'system/**' - -jobs: - build: - name: Run Check - runs-on: ubuntu-latest - steps: - - name: Setup PHP Action - uses: shivammathur/setup-php@v2 - with: - extensions: intl - php-version: "7.4" - - - name: Checkout - uses: actions/checkout@v2 - - - name: "Install dependencies" - run: "composer install" - - - name: "Static analysis Check" - run: "vendor/bin/phpstan analyze --level=5 app system" diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml new file mode 100644 index 000000000000..64c4f297adc3 --- /dev/null +++ b/.github/workflows/test-phpstan.yml @@ -0,0 +1,35 @@ +# When a Pull Request is opened, perform a static +# analysis check on the code using PHPStan. +# +# @todo Consider removing from Composer and using +# https://github.com/marketplace/actions/oskar-phpstan +name: PHPStan + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**' + - 'system/**' + +jobs: + build: + name: Analyze code (PHPStan) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + extensions: intl + + - name: Install dependencies + run: composer install --no-progress --no-suggest --no-interaction + + - name: Run static analysis + run: vendor/bin/phpstan analyze --level=5 app system diff --git a/.github/workflows/test-userguide.yml b/.github/workflows/test-userguide.yml new file mode 100644 index 000000000000..92bb72bceb65 --- /dev/null +++ b/.github/workflows/test-userguide.yml @@ -0,0 +1,23 @@ +# When a Pull Request is opened that modifies +# the User Guide source, build the User Guide +# with Sphinx and let the contributor know of +# any errors. +name: Test User Guide + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - 'user_guide_src/**' + +jobs: + syntax_check: + name: Check User Guide syntax + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ammaraskar/sphinx-action@master + with: + docs-folder: user_guide_src diff --git a/.github/workflows/userguide_ci.yml b/.github/workflows/userguide_ci.yml deleted file mode 100644 index c00d4c56e921..000000000000 --- a/.github/workflows/userguide_ci.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Builds the user guide and lets the contributor know of -# any user guide build errors. -name: UserGuide CI - -on: - pull_request: - paths: - - 'user_guide_src/**' - -jobs: - syntax_check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: ammaraskar/sphinx-action@master - with: - docs-folder: user_guide_src From 7c5655a37193da4314457bc7f2d84150374e6708 Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 26 Aug 2020 15:19:15 +0000 Subject: [PATCH 032/328] Add Dependabot for Actions and Composer --- .github/dependabot.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..6532121074ad --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: +- package-ecosystem: composer + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + +- package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every weekday + interval: "daily" From 788e7677abe7f482c7e3310fe869c896c034d17c Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 26 Aug 2020 17:12:06 +0000 Subject: [PATCH 033/328] [ci skip] Reinstate phpstan workflow on push --- .github/workflows/test-phpstan.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml index 64c4f297adc3..cd437cce2d6c 100644 --- a/.github/workflows/test-phpstan.yml +++ b/.github/workflows/test-phpstan.yml @@ -1,5 +1,5 @@ -# When a Pull Request is opened, perform a static -# analysis check on the code using PHPStan. +# When a PR is opened or a push is made, perform +# a static analysis check on the code using PHPStan. # # @todo Consider removing from Composer and using # https://github.com/marketplace/actions/oskar-phpstan @@ -13,6 +13,13 @@ on: paths: - 'app/**' - 'system/**' + push: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**' + - 'system/**' jobs: build: From cfd5e6b08d450a4f01cddab8d9ff924f82b28ee5 Mon Sep 17 00:00:00 2001 From: MGatner Date: Thu, 27 Aug 2020 10:16:49 -0400 Subject: [PATCH 034/328] Remove todo note --- .github/workflows/test-phpstan.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml index cd437cce2d6c..ebfe14c3202d 100644 --- a/.github/workflows/test-phpstan.yml +++ b/.github/workflows/test-phpstan.yml @@ -1,8 +1,5 @@ # When a PR is opened or a push is made, perform # a static analysis check on the code using PHPStan. -# -# @todo Consider removing from Composer and using -# https://github.com/marketplace/actions/oskar-phpstan name: PHPStan on: From 669073ad799bde3300a3c48771b986c03775765e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2020 11:52:10 +0000 Subject: [PATCH 035/328] Bump ad-m/github-push-action from v0.6 to v0.6.0 Bumps [ad-m/github-push-action](https://github.com/ad-m/github-push-action) from v0.6 to v0.6.0. - [Release notes](https://github.com/ad-m/github-push-action/releases) - [Commits](https://github.com/ad-m/github-push-action/compare/v0.6...40bf560936a8022e68a3c00e7d2abefaf01305a6) Signed-off-by: dependabot[bot] --- .github/workflows/deploy-userguide-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-userguide-latest.yml b/.github/workflows/deploy-userguide-latest.yml index 4ed64cf3d0b9..bde85ff4db6c 100644 --- a/.github/workflows/deploy-userguide-latest.yml +++ b/.github/workflows/deploy-userguide-latest.yml @@ -47,7 +47,7 @@ jobs: git commit -m "Update User Guide" -a || true - name: Push changes - uses: ad-m/github-push-action@v0.6 + uses: ad-m/github-push-action@v0.6.0 with: branch: gh-pages directory: gh-pages From 82f2965c6d0f59616f86048452bfcabafee9b91a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2020 11:52:09 +0000 Subject: [PATCH 036/328] Update ammaraskar/sphinx-action requirement to 0.4 Updates the requirements on [ammaraskar/sphinx-action](https://github.com/ammaraskar/sphinx-action) to permit the latest version. - [Release notes](https://github.com/ammaraskar/sphinx-action/releases) - [Commits](https://github.com/ammaraskar/sphinx-action/commits/8b4f60114d7fd1faeba1a712269168508d4750d2) Signed-off-by: dependabot[bot] --- .github/workflows/deploy-userguide-latest.yml | 2 +- .github/workflows/test-userguide.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-userguide-latest.yml b/.github/workflows/deploy-userguide-latest.yml index bde85ff4db6c..22e6eed4db4f 100644 --- a/.github/workflows/deploy-userguide-latest.yml +++ b/.github/workflows/deploy-userguide-latest.yml @@ -23,7 +23,7 @@ jobs: # Build the latest User Guide - name: Build with Sphinx - uses: ammaraskar/sphinx-action@v0.4 + uses: ammaraskar/sphinx-action@0.4 with: docs-folder: user_guide_src/ diff --git a/.github/workflows/test-userguide.yml b/.github/workflows/test-userguide.yml index 92bb72bceb65..75e6f4dc2524 100644 --- a/.github/workflows/test-userguide.yml +++ b/.github/workflows/test-userguide.yml @@ -18,6 +18,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: ammaraskar/sphinx-action@master + - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: user_guide_src From d0b3897f3a5cc6404630cb8b56a53ca2c0ef5809 Mon Sep 17 00:00:00 2001 From: "Merlas Paul (Crustamet)" <36475250+crustamet@users.noreply.github.com> Date: Thu, 27 Aug 2020 12:04:19 +0300 Subject: [PATCH 037/328] Fix Session gets expired using AJAX with Session Fix session class setcookie not getting renewed when using ajax and put expire date in the past. --- system/Session/Session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Session/Session.php b/system/Session/Session.php index 8f8fec3ea1a3..6d1d2d78ee5e 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -1077,7 +1077,7 @@ protected function setCookie() { // PHP 7.3 adds another function signature allowing setting of samesite $params = [ - 'expires' => $this->sessionExpiration, + 'expires' => (empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration), 'path' => $this->cookiePath, 'domain' => $this->cookieDomain, 'secure' => $this->cookieSecure, From 8c7c70f8971438636d6cb1b8f2e39670370631c0 Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 27 Aug 2020 19:37:55 +0200 Subject: [PATCH 038/328] Add samesite attribute for debug-bar related cookies --- system/Debug/Toolbar/Views/toolbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index 6319d1ccf2dc..ca38f9b47bb5 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -579,7 +579,7 @@ var ciDebugBar = { var expires = ""; } - document.cookie = name + "=" + value + expires + "; path=/"; + document.cookie = name + "=" + value + expires + "; path=/; samesite=Lax"; }, //-------------------------------------------------------------------- From 1e453eb4c6d0b7ceb695245c78bb15686dc471b0 Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 27 Aug 2020 19:06:18 +0200 Subject: [PATCH 039/328] More tests for session and mock fixes --- system/Session/Session.php | 4 +- system/Test/Mock/MockSession.php | 4 +- tests/system/Session/SessionTest.php | 63 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/system/Session/Session.php b/system/Session/Session.php index 6d1d2d78ee5e..64560662d07b 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -1066,7 +1066,7 @@ protected function setCookie() setcookie( $this->sessionCookieName, session_id(), - (empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration), + empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration, $this->cookiePath . $sameSite, // Hacky way to set SameSite for PHP 7.2 and earlier $this->cookieDomain, $this->cookieSecure, @@ -1077,7 +1077,7 @@ protected function setCookie() { // PHP 7.3 adds another function signature allowing setting of samesite $params = [ - 'expires' => (empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration), + 'expires' => empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration, 'path' => $this->cookiePath, 'domain' => $this->cookieDomain, 'secure' => $this->cookieSecure, diff --git a/system/Test/Mock/MockSession.php b/system/Test/Mock/MockSession.php index 88d2973fee68..6e6f2c510909 100644 --- a/system/Test/Mock/MockSession.php +++ b/system/Test/Mock/MockSession.php @@ -61,7 +61,7 @@ protected function setCookie() $this->cookies[] = [ $this->sessionCookieName, session_id(), - (empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration), + empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration, $this->cookiePath . $sameSite, // Hacky way to set SameSite for PHP 7.2 and earlier $this->cookieDomain, $this->cookieSecure, @@ -72,7 +72,7 @@ protected function setCookie() { // PHP 7.3 adds another function signature allowing setting of samesite $params = [ - 'lifetime' => $this->sessionExpiration, + 'expires' => empty($this->sessionExpiration) ? 0 : time() + $this->sessionExpiration, 'path' => $this->cookiePath, 'domain' => $this->cookieDomain, 'secure' => $this->cookieSecure, diff --git a/tests/system/Session/SessionTest.php b/tests/system/Session/SessionTest.php index 6f829c9d449b..9f4a856a0be0 100644 --- a/tests/system/Session/SessionTest.php +++ b/tests/system/Session/SessionTest.php @@ -594,6 +594,27 @@ public function testNoneSameSite() } } + public function testNoSameSite() + { + $session = $this->getInstance(['cookieSameSite' => '']); + $session->start(); + + $cookies = $session->cookies; + $this->assertCount(1, $cookies); + + if (PHP_VERSION_ID < 70300) + { + $this->assertCount(7, $cookies[0]); + $this->assertStringNotContainsString('samesite', $cookies[0][3]); + } + else + { + $this->assertCount(3, $cookies[0]); + $this->assertIsArray($cookies[0][2]); + $this->assertArrayNotHasKey('samesite', $cookies[0][2]); + } + } + public function testInvalidSameSite() { $this->expectException(SessionException::class); @@ -603,4 +624,46 @@ public function testInvalidSameSite() $session->start(); } + public function testExpires() + { + $session = $this->getInstance(['sessionExpiration' => 8000]); + $session->start(); + + $cookies = $session->cookies; + $this->assertCount(1, $cookies); + + if (PHP_VERSION_ID < 70300) + { + $this->assertCount(7, $cookies[0]); + $this->assertGreaterThan(8000, $cookies[0][2]); + } + else + { + $this->assertCount(3, $cookies[0]); + $this->assertIsArray($cookies[0][2]); + $this->assertEquals(8000, $cookies[0][2]['expires']); + } + } + + public function testExpiresOnClose() + { + $session = $this->getInstance(['sessionExpiration' => 0]); + $session->start(); + + $cookies = $session->cookies; + $this->assertCount(1, $cookies); + + if (PHP_VERSION_ID < 70300) + { + $this->assertCount(7, $cookies[0]); + $this->assertEquals(0, $cookies[0][2]); + } + else + { + $this->assertCount(3, $cookies[0]); + $this->assertIsArray($cookies[0][2]); + $this->assertEquals(0, $cookies[0][2]['expires']); + } + } + } From 1900461b2fff1902ee28105c54d4268cc95858ac Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 27 Aug 2020 21:30:15 +0200 Subject: [PATCH 040/328] Fix test --- tests/system/Session/SessionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/Session/SessionTest.php b/tests/system/Session/SessionTest.php index 9f4a856a0be0..b1132ff8f7f9 100644 --- a/tests/system/Session/SessionTest.php +++ b/tests/system/Session/SessionTest.php @@ -641,7 +641,7 @@ public function testExpires() { $this->assertCount(3, $cookies[0]); $this->assertIsArray($cookies[0][2]); - $this->assertEquals(8000, $cookies[0][2]['expires']); + $this->assertGreaterThan(8000, $cookies[0][2]['expires']); } } From 8b4d8666173395c036d37aead276861e7f77217f Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 29 Aug 2020 01:34:00 +0800 Subject: [PATCH 041/328] Update query builder doc [ci skip] --- .../source/database/query_builder.rst | 812 +++++++++--------- 1 file changed, 399 insertions(+), 413 deletions(-) diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index a827d37ae7d1..d957d613ea3d 100755 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -122,13 +122,13 @@ Permits you to write the SELECT portion of your query:: to select all fields and automatically adds 'SELECT \*'. ``$builder->select()`` accepts an optional second parameter. If you set it -to FALSE, CodeIgniter will not try to protect your field or table names. +to ``false``, CodeIgniter will not try to protect your field or table names. This is useful if you need a compound select statement where automatic escaping of fields may break them. :: - $builder->select('(SELECT SUM(payments.amount) FROM payments WHERE payments.invoice_id=4) AS amount_paid', FALSE); + $builder->select('(SELECT SUM(payments.amount) FROM payments WHERE payments.invoice_id=4) AS amount_paid', false); $query = $builder->get(); **$builder->selectMax()** @@ -289,11 +289,11 @@ methods: $builder->where($where); ``$builder->where()`` accepts an optional third parameter. If you set it to - FALSE, CodeIgniter will not try to protect your field or table names. + ``false``, CodeIgniter will not try to protect your field or table names. :: - $builder->where('MATCH (field) AGAINST ("value")', NULL, FALSE); + $builder->where('MATCH (field) AGAINST ("value")', null, false); #. **Subqueries:** You can use an anonymous function to create a subquery. @@ -505,12 +505,12 @@ You can also pass an array of multiple values as well:: If you are using a database that CodeIgniter escapes queries for, you can prevent escaping content by passing an optional third argument, and -setting it to FALSE. +setting it to ``false``. :: $builder->having('user_id', 45); // Produces: HAVING `user_id` = 45 in some databases such as MySQL - $builder->having('user_id', 45, FALSE); // Produces: HAVING user_id = 45 + $builder->having('user_id', 45, false); // Produces: HAVING user_id = 45 **$builder->orHaving()** @@ -742,7 +742,7 @@ Builder query. Queries will accept Query Builder restrictors such as echo $builder->countAllResults(); // Produces an integer, like 17 However, this method also resets any field values that you may have passed -to ``select()``. If you need to keep them, you can pass ``FALSE`` as the +to ``select()``. If you need to keep them, you can pass ``false`` as the first parameter. echo $builder->countAllResults(false); // Produces an integer, like 17 @@ -755,7 +755,7 @@ Example:: echo $builder->countAll(); // Produces an integer, like 25 As is in countAllResult method, this method resets any field values that you may have passed -to ``select()`` as well. If you need to keep them, you can pass ``FALSE`` as the +to ``select()`` as well. If you need to keep them, you can pass ``false`` as the first parameter. ************** @@ -779,7 +779,7 @@ you to create queries with complex WHERE clauses. Nested groups are supported. E // Generates: // SELECT * FROM (`my_table`) WHERE ( `a` = 'a' OR ( `b` = 'b' AND `c` = 'c' ) ) AND `d` = 'd' -.. note:: groups need to be balanced, make sure every groupStart() is matched by a groupEnd(). +.. note:: Groups need to be balanced, make sure every groupStart() is matched by a groupEnd(). **$builder->groupStart()** @@ -801,23 +801,23 @@ Starts a new group by adding an opening parenthesis to the WHERE clause of the q Ends the current group by adding a closing parenthesis to the WHERE clause of the query. -**$builder->groupHavingStart()** +**$builder->havingGroupStart()** Starts a new group by adding an opening parenthesis to the HAVING clause of the query. -**$builder->orGroupHavingStart()** +**$builder->orHavingGroupStart()** Starts a new group by adding an opening parenthesis to the HAVING clause of the query, prefixing it with 'OR'. -**$builder->notGroupHavingStart()** +**$builder->notHavingGroupStart()** Starts a new group by adding an opening parenthesis to the HAVING clause of the query, prefixing it with 'NOT'. -**$builder->orNotGroupHavingStart()** +**$builder->orNotHavingGroupStart()** Starts a new group by adding an opening parenthesis to the HAVING clause of the query, prefixing it with 'OR NOT'. -**$builder->groupHavingEnd()** +**$builder->havingGroupEnd()** Ends the current group by adding a closing parenthesis to the HAVING clause of the query. @@ -834,7 +834,7 @@ function. Here is an example using an array:: $data = [ 'title' => 'My title', 'name' => 'My Name', - 'date' => 'My date' + 'date' => 'My date', ]; $builder->insert($data); @@ -844,13 +844,12 @@ The first parameter is an associative array of values. Here is an example using an object:: - /* - class Myclass { + class Myclass + { public $title = 'My Title'; public $content = 'My Content'; public $date = 'My Date'; } - */ $object = new Myclass; $builder->insert($object); @@ -869,7 +868,7 @@ You can optionally pass an **boolean** to the function. Here is an example using $data = [ 'title' => 'My title', 'name' => 'My Name', - 'date' => 'My date' + 'date' => 'My date', ]; $builder->ignore(true)->insert($data); @@ -886,7 +885,7 @@ Example:: $data = [ 'title' => 'My title', 'name' => 'My Name', - 'date' => 'My date' + 'date' => 'My date', ]; $sql = $builder->set($data)->getCompiledInsert('mytable'); @@ -897,7 +896,7 @@ Example:: The second parameter enables you to set whether or not the query builder query will be reset (by default it will be--just like $builder->insert()):: - echo $builder->set('title', 'My Title')->getCompiledInsert('mytable', FALSE); + echo $builder->set('title', 'My Title')->getCompiledInsert('mytable', false); // Produces string: INSERT INTO mytable (`title`) VALUES ('My Title') @@ -911,7 +910,7 @@ parameter. The reason this worked is that the query has not been executed using `$builder->insert()` which resets values or reset directly using `$builder->resetQuery()`. -.. note:: This method doesn't work for batched inserts. +.. note:: This method doesn't work for batch inserts. **$builder->insertBatch()** @@ -923,13 +922,13 @@ function. Here is an example using an array:: [ 'title' => 'My title', 'name' => 'My Name', - 'date' => 'My date' + 'date' => 'My date', ], [ 'title' => 'Another title', 'name' => 'Another Name', - 'date' => 'Another date' - ] + 'date' => 'Another date', + ], ]; $builder->insertBatch($data); @@ -957,7 +956,7 @@ Example:: $data = [ 'title' => 'My title', 'name' => 'My Name', - 'date' => 'My date' + 'date' => 'My date', ]; $builder->replace($data); @@ -992,13 +991,13 @@ based on whether you are doing an insert or an update:: $builder->insert(); **set()** will also accept an optional third parameter (``$escape``), that -will prevent data from being escaped if set to FALSE. To illustrate the +will prevent data from being escaped if set to ``false``. To illustrate the difference, here is ``set()`` used both with and without the escape parameter. :: - $builder->set('field', 'field+1', FALSE); + $builder->set('field', 'field+1', false); $builder->where('id', 2); $builder->update(); // gives UPDATE mytable SET field = field+1 WHERE `id` = 2 @@ -1011,7 +1010,7 @@ You can also pass an associative array to this function:: $array = [ 'name' => $name, 'title' => $title, - 'status' => $status + 'status' => $status, ]; $builder->set($array); @@ -1019,13 +1018,12 @@ You can also pass an associative array to this function:: Or an object:: - /* - class Myclass { + class Myclass + { public $title = 'My Title'; public $content = 'My Content'; public $date = 'My Date'; } - */ $object = new Myclass; $builder->set($object); @@ -1040,7 +1038,7 @@ is an example using an array:: $data = [ 'title' => $title, 'name' => $name, - 'date' => $date + 'date' => $date, ]; $builder->where('id', $id); @@ -1053,13 +1051,12 @@ is an example using an array:: Or you can supply an object:: - /* - class Myclass { + class Myclass + { public $title = 'My Title'; public $content = 'My Content'; public $date = 'My Date'; } - */ $object = new Myclass; $builder->where('id', $id); @@ -1095,13 +1092,13 @@ Here is an example using an array:: [ 'title' => 'My title' , 'name' => 'My Name 2' , - 'date' => 'My date 2' + 'date' => 'My date 2', ], [ 'title' => 'Another title' , 'name' => 'Another Name 2' , - 'date' => 'Another date 2' - ] + 'date' => 'Another date 2', + ], ]; $builder->updateBatch($data, 'title'); @@ -1215,7 +1212,7 @@ This is useful in situations where you are using Query Builder to generate SQL (ex. ``$builder->getCompiledSelect()``) but then choose to, for instance, run the query:: - // Note that the second parameter of the get_compiled_select method is FALSE + // Note that the second parameter of the get_compiled_select method is false $sql = $builder->select(['field1','field2']) ->where('field3',5) ->getCompiledSelect(false); @@ -1238,493 +1235,482 @@ Class Reference .. php:method:: resetQuery() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` Resets the current Query Builder state. Useful when you want to build a query that can be canceled under certain conditions. - .. php:method:: countAllResults([$reset = TRUE]) + .. php:method:: countAllResults([$reset = true]) - :param bool $reset: Whether to reset values for SELECTs - :returns: Number of rows in the query result + :param bool $reset: Whether to reset values for SELECTs + :returns: Number of rows in the query result :rtype: int Generates a platform-specific query string that counts all records returned by an Query Builder query. - .. php:method:: countAll([$reset = TRUE]) + .. php:method:: countAll([$reset = true]) - :param bool $reset: Whether to reset values for SELECTs - :returns: Number of rows in the query result + :param bool $reset: Whether to reset values for SELECTs + :returns: Number of rows in the query result :rtype: int Generates a platform-specific query string that counts all records returned by an Query Builder query. - .. php:method:: get([$limit = NULL[, $offset = NULL[, $reset = TRUE]]]]) + .. php:method:: get([$limit = null[, $offset = null[, $reset = true]]]]) - :param int $limit: The LIMIT clause - :param int $offset: The OFFSET clause - :param bool $reset: Do we want to clear query builder values? - :returns: \CodeIgniter\Database\ResultInterface instance (method chaining) - :rtype: \CodeIgniter\Database\ResultInterface + :param int $limit: The LIMIT clause + :param int $offset: The OFFSET clause + :param bool $reset: Do we want to clear query builder values? + :returns: ``\CodeIgniter\Database\ResultInterface`` instance (method chaining) + :rtype: ``\CodeIgniter\Database\ResultInterface`` - Compiles and runs SELECT statement based on the already + Compiles and runs ``SELECT`` statement based on the already called Query Builder methods. - .. php:method:: getWhere([$where = NULL[, $limit = NULL[, $offset = NULL[, $reset = TRUE]]]]]) + .. php:method:: getWhere([$where = null[, $limit = null[, $offset = null[, $reset = true]]]]]) - :param string $where: The WHERE clause - :param int $limit: The LIMIT clause - :param int $offset: The OFFSET clause - :param bool $reset: Do we want to clear query builder values? - :returns: \CodeIgniter\Database\ResultInterface instance (method chaining) - :rtype: \CodeIgniter\Database\ResultInterface + :param string $where: The WHERE clause + :param int $limit: The LIMIT clause + :param int $offset: The OFFSET clause + :param bool $reset: Do we want to clear query builder values? + :returns: ``\CodeIgniter\Database\ResultInterface`` instance (method chaining) + :rtype: ``\CodeIgniter\Database\ResultInterface`` Same as ``get()``, but also allows the WHERE to be added directly. - .. php:method:: select([$select = '*'[, $escape = NULL]]) + .. php:method:: select([$select = '*'[, $escape = null]]) - :param string $select: The SELECT portion of a query - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $select: The SELECT portion of a query + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a SELECT clause to a query. + Adds a ``SELECT`` clause to a query. .. php:method:: selectAvg([$select = ''[, $alias = '']]) - :param string $select: Field to compute the average of - :param string $alias: Alias for the resulting value name - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $select: Field to compute the average of + :param string $alias: Alias for the resulting value name + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a SELECT AVG(field) clause to a query. + Adds a ``SELECT AVG(field)`` clause to a query. .. php:method:: selectMax([$select = ''[, $alias = '']]) - :param string $select: Field to compute the maximum of - :param string $alias: Alias for the resulting value name - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $select: Field to compute the maximum of + :param string $alias: Alias for the resulting value name + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a SELECT MAX(field) clause to a query. + Adds a ``SELECT MAX(field)`` clause to a query. .. php:method:: selectMin([$select = ''[, $alias = '']]) - :param string $select: Field to compute the minimum of - :param string $alias: Alias for the resulting value name - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $select: Field to compute the minimum of + :param string $alias: Alias for the resulting value name + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a SELECT MIN(field) clause to a query. + Adds a ``SELECT MIN(field)`` clause to a query. .. php:method:: selectSum([$select = ''[, $alias = '']]) - :param string $select: Field to compute the sum of - :param string $alias: Alias for the resulting value name - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $select: Field to compute the sum of + :param string $alias: Alias for the resulting value name + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a SELECT SUM(field) clause to a query. + Adds a ``SELECT SUM(field)`` clause to a query. .. php:method:: selectCount([$select = ''[, $alias = '']]) - :param string $select: Field to compute the average of - :param string $alias: Alias for the resulting value name - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $select: Field to compute the average of + :param string $alias: Alias for the resulting value name + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a SELECT COUNT(field) clause to a query. + Adds a ``SELECT COUNT(field)`` clause to a query. - .. php:method:: distinct([$val = TRUE]) + .. php:method:: distinct([$val = true]) - :param bool $val: Desired value of the "distinct" flag - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param bool $val: Desired value of the "distinct" flag + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` Sets a flag which tells the query builder to add - a DISTINCT clause to the SELECT portion of the query. + a ``DISTINCT`` clause to the ``SELECT`` portion of the query. - .. php:method:: from($from[, $overwrite = FALSE]) + .. php:method:: from($from[, $overwrite = false]) - :param mixed $from: Table name(s); string or array - :param bool $overwrite: Should we remove the first table existing? - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param mixed $from: Table name(s); string or array + :param bool $overwrite: Should we remove the first table existing? + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Specifies the FROM clause of a query. + Specifies the ``FROM`` clause of a query. - .. php:method:: join($table, $cond[, $type = ''[, $escape = NULL]]) + .. php:method:: join($table, $cond[, $type = ''[, $escape = null]]) - :param string $table: Table name to join - :param string $cond: The JOIN ON condition - :param string $type: The JOIN type - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $table: Table name to join + :param string $cond: The JOIN ON condition + :param string $type: The JOIN type + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a JOIN clause to a query. + Adds a ``JOIN`` clause to a query. - .. php:method:: where($key[, $value = NULL[, $escape = NULL]]) + .. php:method:: where($key[, $value = null[, $escape = null]]) - :param mixed $key: Name of field to compare, or associative array - :param mixed $value: If a single key, compared to this value - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param mixed $key: Name of field to compare, or associative array + :param mixed $value: If a single key, compared to this value + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates the WHERE portion of the query. - Separates multiple calls with 'AND'. + Generates the ``WHERE`` portion of the query. Separates multiple calls with ``AND``. - .. php:method:: orWhere($key[, $value = NULL[, $escape = NULL]]) + .. php:method:: orWhere($key[, $value = null[, $escape = null]]) - :param mixed $key: Name of field to compare, or associative array - :param mixed $value: If a single key, compared to this value - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param mixed $key: Name of field to compare, or associative array + :param mixed $value: If a single key, compared to this value + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates the WHERE portion of the query. - Separates multiple calls with 'OR'. + Generates the ``WHERE`` portion of the query. Separates multiple calls with ``OR``. - .. php:method:: orWhereIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: orWhereIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: The field to search - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param string $key: The field to search + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a WHERE field IN('item', 'item') SQL query, - joined with 'OR' if appropriate. + Generates a ``WHERE`` field ``IN('item', 'item')`` SQL query, joined with ``OR`` if appropriate. - .. php:method:: orWhereNotIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: orWhereNotIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: The field to search - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param string $key: The field to search + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a WHERE field NOT IN('item', 'item') SQL query, - joined with 'OR' if appropriate. + Generates a ``WHERE`` field ``NOT IN('item', 'item')`` SQL query, joined with ``OR`` if appropriate. - .. php:method:: whereIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: whereIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: Name of field to examine - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param string $key: Name of field to examine + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a WHERE field IN('item', 'item') SQL query, - joined with 'AND' if appropriate. + Generates a ``WHERE`` field ``IN('item', 'item')`` SQL query, joined with ``AND`` if appropriate. - .. php:method:: whereNotIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: whereNotIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: Name of field to examine - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param string $key: Name of field to examine + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a WHERE field NOT IN('item', 'item') SQL query, - joined with 'AND' if appropriate. + Generates a ``WHERE`` field ``NOT IN('item', 'item')`` SQL query, joined with ``AND`` if appropriate. .. php:method:: groupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression, using ANDs for the conditions inside it. + Starts a group expression, using ``AND`` for the conditions inside it. .. php:method:: orGroupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression, using ORs for the conditions inside it. + Starts a group expression, using ``OR`` for the conditions inside it. .. php:method:: notGroupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression, using AND NOTs for the conditions inside it. + Starts a group expression, using ``AND NOT`` for the conditions inside it. .. php:method:: orNotGroupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression, using OR NOTs for the conditions inside it. + Starts a group expression, using ``OR NOT`` for the conditions inside it. .. php:method:: groupEnd() - :returns: BaseBuilder instance - :rtype: object + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` Ends a group expression. - .. php:method:: like($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: like($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a LIKE clause to a query, separating multiple calls with AND. + Adds a ``LIKE`` clause to a query, separating multiple calls with ``AND``. - .. php:method:: orLike($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: orLike($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a LIKE clause to a query, separating multiple class with OR. + Adds a ``LIKE`` clause to a query, separating multiple class with ``OR``. - .. php:method:: notLike($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: notLike($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a NOT LIKE clause to a query, separating multiple calls with AND. + Adds a ``NOT LIKE`` clause to a query, separating multiple calls with ``AND``. - .. php:method:: orNotLike($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: orNotLike($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a NOT LIKE clause to a query, separating multiple calls with OR. + Adds a ``NOT LIKE`` clause to a query, separating multiple calls with ``OR``. - .. php:method:: having($key[, $value = NULL[, $escape = NULL]]) + .. php:method:: having($key[, $value = null[, $escape = null]]) - :param mixed $key: Identifier (string) or associative array of field/value pairs - :param string $value: Value sought if $key is an identifier - :param string $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param mixed $key: Identifier (string) or associative array of field/value pairs + :param string $value: Value sought if $key is an identifier + :param string $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a HAVING clause to a query, separating multiple calls with AND. + Adds a ``HAVING`` clause to a query, separating multiple calls with ``AND``. - .. php:method:: orHaving($key[, $value = NULL[, $escape = NULL]]) + .. php:method:: orHaving($key[, $value = null[, $escape = null]]) - :param mixed $key: Identifier (string) or associative array of field/value pairs - :param string $value: Value sought if $key is an identifier - :param string $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param mixed $key: Identifier (string) or associative array of field/value pairs + :param string $value: Value sought if $key is an identifier + :param string $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a HAVING clause to a query, separating multiple calls with OR. + Adds a ``HAVING`` clause to a query, separating multiple calls with ``OR``. - .. php:method:: orHavingIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: orHavingIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: The field to search - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param string $key: The field to search + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a HAVING field IN('item', 'item') SQL query, - joined with 'OR' if appropriate. + Generates a ``HAVING`` field IN('item', 'item') SQL query, joined with ``OR`` if appropriate. - .. php:method:: orHavingNotIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: orHavingNotIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: The field to search - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param string $key: The field to search + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a HAVING field NOT IN('item', 'item') SQL query, - joined with 'OR' if appropriate. + Generates a ``HAVING`` field ``NOT IN('item', 'item')`` SQL query, joined with ``OR`` if appropriate. - .. php:method:: havingIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: havingIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: Name of field to examine - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance - :rtype: object + :param string $key: Name of field to examine + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a HAVING field IN('item', 'item') SQL query, - joined with 'AND' if appropriate. + Generates a ``HAVING`` field ``IN('item', 'item')`` SQL query, joined with ``AND`` if appropriate. - .. php:method:: havingNotIn([$key = NULL[, $values = NULL[, $escape = NULL]]]) + .. php:method:: havingNotIn([$key = null[, $values = null[, $escape = null]]]) - :param string $key: Name of field to examine - :param array|Closure $values: Array of target values, or anonymous function for subquery - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance - :rtype: object + :param string $key: Name of field to examine + :param array|Closure $values: Array of target values, or anonymous function for subquery + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Generates a HAVING field NOT IN('item', 'item') SQL query, - joined with 'AND' if appropriate. + Generates a ``HAVING`` field ``NOT IN('item', 'item')`` SQL query, joined with ``AND`` if appropriate. - .. php:method:: havingLike($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: havingLike($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a LIKE clause to a HAVING part of the query, separating multiple calls with AND. + Adds a ``LIKE`` clause to a ``HAVING`` part of the query, separating multiple calls with ``AND``. - .. php:method:: orHavingLike($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: orHavingLike($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a LIKE clause to a HAVING part of the query, separating multiple class with OR. + Adds a ``LIKE`` clause to a ``HAVING`` part of the query, separating multiple class with ``OR``. - .. php:method:: notHavingLike($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: notHavingLike($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :param bool $insensitiveSearch: Whether to force a case-insensitive search - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :param bool $insensitiveSearch: Whether to force a case-insensitive search + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a NOT LIKE clause to a HAVING part of the query, separating multiple calls with AND. + Adds a ``NOT LIKE`` clause to a ``HAVING`` part of the query, separating multiple calls with ``AND``. - .. php:method:: orNotHavingLike($field[, $match = ''[, $side = 'both'[, $escape = NULL[, $insensitiveSearch = FALSE]]]]) + .. php:method:: orNotHavingLike($field[, $match = ''[, $side = 'both'[, $escape = null[, $insensitiveSearch = false]]]]) - :param string $field: Field name - :param string $match: Text portion to match - :param string $side: Which side of the expression to put the '%' wildcard on - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $field: Field name + :param string $match: Text portion to match + :param string $side: Which side of the expression to put the '%' wildcard on + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a NOT LIKE clause to a HAVING part of the query, separating multiple calls with OR. + Adds a ``NOT LIKE`` clause to a ``HAVING`` part of the query, separating multiple calls with ``OR``. .. php:method:: havingGroupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression for HAVING clause, using ANDs for the conditions inside it. + Starts a group expression for ``HAVING`` clause, using ``AND`` for the conditions inside it. .. php:method:: orHavingGroupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression for HAVING clause, using ORs for the conditions inside it. + Starts a group expression for ``HAVING`` clause, using ``OR`` for the conditions inside it. .. php:method:: notHavingGroupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression for HAVING clause, using AND NOTs for the conditions inside it. + Starts a group expression for ``HAVING`` clause, using ``AND NOT`` for the conditions inside it. .. php:method:: orNotHavingGroupStart() - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Starts a group expression for HAVING clause, using OR NOTs for the conditions inside it. + Starts a group expression for ``HAVING`` clause, using ``OR NOT`` for the conditions inside it. .. php:method:: havingGroupEnd() - :returns: BaseBuilder instance - :rtype: object + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Ends a group expression for HAVING clause. + Ends a group expression for ``HAVING`` clause. - .. php:method:: groupBy($by[, $escape = NULL]) + .. php:method:: groupBy($by[, $escape = null]) - :param mixed $by: Field(s) to group by; string or array - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param mixed $by: Field(s) to group by; string or array + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds a GROUP BY clause to a query. + Adds a ``GROUP BY`` clause to a query. - .. php:method:: orderBy($orderby[, $direction = ''[, $escape = NULL]]) + .. php:method:: orderBy($orderby[, $direction = ''[, $escape = null]]) - :param string $orderby: Field to order by - :param string $direction: The order requested - ASC, DESC or random - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param string $orderby: Field to order by + :param string $direction: The order requested - ASC, DESC or random + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds an ORDER BY clause to a query. + Adds an ``ORDER BY`` clause to a query. .. php:method:: limit($value[, $offset = 0]) - :param int $value: Number of rows to limit the results to - :param int $offset: Number of rows to skip - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param int $value: Number of rows to limit the results to + :param int $offset: Number of rows to skip + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds LIMIT and OFFSET clauses to a query. + Adds ``LIMIT`` and ``OFFSET`` clauses to a query. .. php:method:: offset($offset) - :param int $offset: Number of rows to skip - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param int $offset: Number of rows to skip + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds an OFFSET clause to a query. + Adds an ``OFFSET`` clause to a query. - .. php:method:: set($key[, $value = ''[, $escape = NULL]]) + .. php:method:: set($key[, $value = ''[, $escape = null]]) - :param mixed $key: Field name, or an array of field/value pairs - :param string $value: Field value, if $key is a single field - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param mixed $key: Field name, or an array of field/value pairs + :param string $value: Field value, if $key is a single field + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` - Adds field/value pairs to be passed later to ``insert()``, - ``update()`` or ``replace()``. + Adds field/value pairs to be passed later to ``insert()``, ``update()`` or ``replace()``. - .. php:method:: insert([$set = NULL[, $escape = NULL]]) + .. php:method:: insert([$set = null[, $escape = null]]) - :param array $set: An associative array of field/value pairs - :param bool $escape: Whether to escape values and identifiers - :returns: TRUE on success, FALSE on failure + :param array $set: An associative array of field/value pairs + :param bool $escape: Whether to escape values and identifiers + :returns: ``true`` on success, ``false`` on failure :rtype: bool - Compiles and executes an INSERT statement. + Compiles and executes an ``INSERT`` statement. - .. php:method:: insertBatch([$set = NULL[, $escape = NULL[, $batch_size = 100]]]) + .. php:method:: insertBatch([$set = null[, $escape = null[, $batch_size = 100]]]) - :param array $set: Data to insert - :param bool $escape: Whether to escape values and identifiers - :param int $batch_size: Count of rows to insert at once - :returns: Number of rows inserted or FALSE on failure - :rtype: mixed + :param array $set: Data to insert + :param bool $escape: Whether to escape values and identifiers + :param int $batch_size: Count of rows to insert at once + :returns: Number of rows inserted or ``false`` on failure + :rtype: int|false Compiles and executes batch ``INSERT`` statements. @@ -1732,33 +1718,33 @@ Class Reference ``INSERT`` queries will be executed, each trying to insert up to ``$batch_size`` rows. - .. php:method:: setInsertBatch($key[, $value = ''[, $escape = NULL]]) + .. php:method:: setInsertBatch($key[, $value = ''[, $escape = null]]) - :param mixed $key: Field name or an array of field/value pairs - :param string $value: Field value, if $key is a single field - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param mixed $key: Field name or an array of field/value pairs + :param string $value: Field value, if $key is a single field + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` Adds field/value pairs to be inserted in a table later via ``insertBatch()``. - .. php:method:: update([$set = NULL[, $where = NULL[, $limit = NULL]]]) + .. php:method:: update([$set = null[, $where = null[, $limit = null]]]) - :param array $set: An associative array of field/value pairs - :param string $where: The WHERE clause - :param int $limit: The LIMIT clause - :returns: TRUE on success, FALSE on failure + :param array $set: An associative array of field/value pairs + :param string $where: The WHERE clause + :param int $limit: The LIMIT clause + :returns: ``true`` on success, ``false`` on failure :rtype: bool - Compiles and executes an UPDATE statement. + Compiles and executes an ``UPDATE`` statement. - .. php:method:: updateBatch([$set = NULL[, $value = NULL[, $batch_size = 100]]]) + .. php:method:: updateBatch([$set = null[, $value = null[, $batch_size = 100]]]) - :param array $set: Field name, or an associative array of field/value pairs - :param string $value: Field value, if $set is a single field - :param int $batch_size: Count of conditions to group in a single query - :returns: Number of rows updated or FALSE on failure - :rtype: mixed + :param array $set: Field name, or an associative array of field/value pairs + :param string $value: Field value, if $set is a single field + :param int $batch_size: Count of conditions to group in a single query + :returns: Number of rows updated or ``false`` on failure + :rtype: int|false Compiles and executes batch ``UPDATE`` statements. @@ -1766,97 +1752,97 @@ Class Reference multiple queries will be executed, each handling up to ``$batch_size`` field/value pairs. - .. php:method:: setUpdateBatch($key[, $value = ''[, $escape = NULL]]) + .. php:method:: setUpdateBatch($key[, $value = ''[, $escape = null]]) - :param mixed $key: Field name or an array of field/value pairs - :param string $value: Field value, if $key is a single field - :param bool $escape: Whether to escape values and identifiers - :returns: BaseBuilder instance (method chaining) - :rtype: BaseBuilder + :param mixed $key: Field name or an array of field/value pairs + :param string $value: Field value, if $key is a single field + :param bool $escape: Whether to escape values and identifiers + :returns: ``BaseBuilder`` instance (method chaining) + :rtype: ``BaseBuilder`` Adds field/value pairs to be updated in a table later via ``updateBatch()``. - .. php:method:: replace([$set = NULL]) + .. php:method:: replace([$set = null]) - :param array $set: An associative array of field/value pairs - :returns: TRUE on success, FALSE on failure + :param array $set: An associative array of field/value pairs + :returns: ``true`` on success, ``false`` on failure :rtype: bool - Compiles and executes a REPLACE statement. + Compiles and executes a ``REPLACE`` statement. - .. php:method:: delete([$where = ''[, $limit = NULL[, $reset_data = TRUE]]]) + .. php:method:: delete([$where = ''[, $limit = null[, $reset_data = true]]]) - :param string $where: The WHERE clause - :param int $limit: The LIMIT clause - :param bool $reset_data: TRUE to reset the query "write" clause - :returns: BaseBuilder instance (method chaining) or FALSE on failure - :rtype: mixed + :param string $where: The WHERE clause + :param int $limit: The LIMIT clause + :param bool $reset_data: true to reset the query "write" clause + :returns: ``BaseBuilder`` instance (method chaining) or ``false`` on failure + :rtype: ``BaseBuilder|false`` - Compiles and executes a DELETE query. + Compiles and executes a ``DELETE`` query. - .. php:method:: increment($column[, $value = 1]) + .. php:method:: increment($column[, $value = 1]) :param string $column: The name of the column to increment - :param int $value: The amount to increment the column by + :param int $value: The amount to increment in the column Increments the value of a field by the specified amount. If the field - is not a numeric field, like a VARCHAR, it will likely be replaced - with $value. + is not a numeric field, like a ``VARCHAR``, it will likely be replaced + with ``$value``. - .. php:method:: decrement($column[, $value = 1]) + .. php:method:: decrement($column[, $value = 1]) :param string $column: The name of the column to decrement - :param int $value: The amount to decrement the column by + :param int $value: The amount to decrement in the column Decrements the value of a field by the specified amount. If the field - is not a numeric field, like a VARCHAR, it will likely be replaced - with $value. + is not a numeric field, like a ``VARCHAR``, it will likely be replaced + with ``$value``. .. php:method:: truncate() - :returns: TRUE on success, FALSE on failure - :rtype: bool + :returns: ``true`` on success, ``false`` on failure, string on test mode + :rtype: bool|string - Executes a TRUNCATE statement on a table. + Executes a ``TRUNCATE`` statement on a table. - .. note:: If the database platform in use doesn't support TRUNCATE, - a DELETE statement will be used instead. + .. note:: If the database platform in use doesn't support ``TRUNCATE``, + a ``DELETE`` statement will be used instead. .. php:method:: emptyTable() - :returns: TRUE on success, FALSE on failure + :returns: ``true`` on success, ``false`` on failure :rtype: bool - Deletes all records from a table via a DELETE statement. + Deletes all records from a table via a ``DELETE`` statement. - .. php:method:: getCompiledSelect([$reset = TRUE]) + .. php:method:: getCompiledSelect([$reset = true]) - :param bool $reset: Whether to reset the current QB values or not - :returns: The compiled SQL statement as a string + :param bool $reset: Whether to reset the current QB values or not + :returns: The compiled SQL statement as a string :rtype: string - Compiles a SELECT statement and returns it as a string. + Compiles a ``SELECT`` statement and returns it as a string. - .. php:method:: getCompiledInsert([$reset = TRUE]) + .. php:method:: getCompiledInsert([$reset = true]) - :param bool $reset: Whether to reset the current QB values or not - :returns: The compiled SQL statement as a string + :param bool $reset: Whether to reset the current QB values or not + :returns: The compiled SQL statement as a string :rtype: string - Compiles an INSERT statement and returns it as a string. + Compiles an ``INSERT`` statement and returns it as a string. - .. php:method:: getCompiledUpdate([$reset = TRUE]) + .. php:method:: getCompiledUpdate([$reset = true]) - :param bool $reset: Whether to reset the current QB values or not - :returns: The compiled SQL statement as a string + :param bool $reset: Whether to reset the current QB values or not + :returns: The compiled SQL statement as a string :rtype: string - Compiles an UPDATE statement and returns it as a string. + Compiles an ``UPDATE`` statement and returns it as a string. - .. php:method:: getCompiledDelete([$reset = TRUE]) + .. php:method:: getCompiledDelete([$reset = true]) - :param bool $reset: Whether to reset the current QB values or not - :returns: The compiled SQL statement as a string + :param bool $reset: Whether to reset the current QB values or not + :returns: The compiled SQL statement as a string :rtype: string - Compiles a DELETE statement and returns it as a string. + Compiles a ``DELETE`` statement and returns it as a string. From 7340ea558fc12a5a96df827239f4c136f7f91203 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 28 Aug 2020 19:29:52 +0800 Subject: [PATCH 042/328] Optimize bootstrap files --- qa-bootstrap.php | 4 +++ system/Test/bootstrap.php | 26 +++++++++--------- system/bootstrap.php | 57 +++++++++++++++++---------------------- 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/qa-bootstrap.php b/qa-bootstrap.php index f44c5019f411..885aa75820ef 100644 --- a/qa-bootstrap.php +++ b/qa-bootstrap.php @@ -1,5 +1,9 @@ appDirectory) . DIRECTORY_SEPARATOR); -defined('WRITEPATH') || define('WRITEPATH', realpath($paths->writableDirectory) . DIRECTORY_SEPARATOR); -defined('SYSTEMPATH') || define('SYSTEMPATH', realpath($paths->systemDirectory) . DIRECTORY_SEPARATOR); +defined('APPPATH') || define('APPPATH', realpath(rtrim($paths->appDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); +defined('WRITEPATH') || define('WRITEPATH', realpath(rtrim($paths->writableDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); +defined('SYSTEMPATH') || define('SYSTEMPATH', realpath(rtrim($paths->systemDirectory, '\\/')) . DIRECTORY_SEPARATOR); defined('ROOTPATH') || define('ROOTPATH', realpath(APPPATH . '../') . DIRECTORY_SEPARATOR); defined('CIPATH') || define('CIPATH', realpath(SYSTEMPATH . '../') . DIRECTORY_SEPARATOR); -// @phpstan-ignore-next-line defined('FCPATH') || define('FCPATH', realpath(PUBLICPATH) . DIRECTORY_SEPARATOR); -// @phpstan-ignore-next-line defined('TESTPATH') || define('TESTPATH', realpath(HOMEPATH . 'tests/') . DIRECTORY_SEPARATOR); defined('SUPPORTPATH') || define('SUPPORTPATH', realpath(TESTPATH . '_support/') . DIRECTORY_SEPARATOR); -// @phpstan-ignore-next-line defined('COMPOSER_PATH') || define('COMPOSER_PATH', realpath(HOMEPATH . 'vendor/autoload.php')); -// @phpstan-ignore-next-line defined('VENDORPATH') || define('VENDORPATH', realpath(HOMEPATH . 'vendor') . DIRECTORY_SEPARATOR); // Load Common.php from App then System @@ -61,11 +57,13 @@ class_alias('Config\Services', 'CodeIgniter\Services'); } // Launch the autoloader to gather namespaces (includes composer.json's "autoload-dev") -$loader = \CodeIgniter\Services::autoloader(); +$loader = CodeIgniter\Services::autoloader(); $loader->initialize(new Config\Autoload(), new Config\Modules()); - -// Register the loader with the SPL autoloader stack. -$loader->register(); +$loader->register(); // Register the loader with the SPL autoloader stack. require_once APPPATH . 'Config/Routes.php'; -$routes->getRoutes('*'); // @phpstan-ignore-line + +/** + * @var \CodeIgniter\Router\RouteCollection $routes + */ +$routes->getRoutes('*'); diff --git a/system/bootstrap.php b/system/bootstrap.php index e658ac73d302..3ebe95102072 100644 --- a/system/bootstrap.php +++ b/system/bootstrap.php @@ -37,11 +37,6 @@ * @filesource */ -namespace CodeIgniter; - -use CodeIgniter; -use Config; - /* * --------------------------------------------------------------- * SETUP OUR PATH CONSTANTS @@ -52,48 +47,46 @@ * so they are available in the config files that are loaded. */ -/** - * The path to the application directory. - */ +// The path to the application directory. if (! defined('APPPATH')) { - // @phpstan-ignore-next-line - define('APPPATH', realpath($paths->appDirectory) . DIRECTORY_SEPARATOR); + /** + * @var \Config\Paths $paths + */ + define('APPPATH', realpath(rtrim($paths->appDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); } -/** - * The path to the project root directory. Just above APPPATH. - */ +// The path to the project root directory. Just above APPPATH. if (! defined('ROOTPATH')) { define('ROOTPATH', realpath(APPPATH . '../') . DIRECTORY_SEPARATOR); } -/** - * The path to the system directory. - */ +// The path to the system directory. if (! defined('SYSTEMPATH')) { - // @phpstan-ignore-next-line - define('SYSTEMPATH', realpath($paths->systemDirectory) . DIRECTORY_SEPARATOR); + /** + * @var \Config\Paths $paths + */ + define('SYSTEMPATH', realpath(rtrim($paths->systemDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); } -/** - * The path to the writable directory. - */ +// The path to the writable directory. if (! defined('WRITEPATH')) { - // @phpstan-ignore-next-line - define('WRITEPATH', realpath($paths->writableDirectory) . DIRECTORY_SEPARATOR); + /** + * @var \Config\Paths $paths + */ + define('WRITEPATH', realpath(rtrim($paths->writableDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); } -/** - * The path to the tests directory - */ +// The path to the tests directory if (! defined('TESTPATH')) { - // @phpstan-ignore-next-line - define('TESTPATH', realpath($paths->testsDirectory) . DIRECTORY_SEPARATOR); + /** + * @var \Config\Paths $paths + */ + define('TESTPATH', realpath(rtrim($paths->testsDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); } /* @@ -125,7 +118,7 @@ * that the config files can use the path constants. */ -if (! class_exists(Config\Autoload::class, false)) +if (! class_exists('Config\Autoload', false)) { require_once SYSTEMPATH . 'Config/AutoloadConfig.php'; require_once APPPATH . 'Config/Autoload.php'; @@ -146,12 +139,12 @@ class_alias('Config\Services', 'CodeIgniter\Services'); $loader = CodeIgniter\Services::autoloader(); $loader->initialize(new Config\Autoload(), new Config\Modules()); -$loader->register(); // Register the loader with the SPL autoloader stack. +$loader->register(); // Register the loader with the SPL autoloader stack. // 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. @@ -185,7 +178,7 @@ class_alias('Config\Services', 'CodeIgniter\Services'); * the pieces all working together. */ -$appConfig = config(Config\App::class); +$appConfig = config('Config\App'); $app = new CodeIgniter\CodeIgniter($appConfig); $app->initialize(); From 4dbb3c2aa4e5b9783c3597e1c87f0b69f6e4f355 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 28 Aug 2020 19:58:58 +0800 Subject: [PATCH 043/328] Update phpstan.neon and resolve some errors --- .github/workflows/test-phpstan.yml | 2 +- app/Config/Autoload.php | 1 - phpstan.neon | 91 +++++++++++++++--------------- system/Config/AutoloadConfig.php | 32 ++++++++++- system/Config/View.php | 16 ++++++ 5 files changed, 95 insertions(+), 47 deletions(-) diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml index ebfe14c3202d..45bb5ff395a2 100644 --- a/.github/workflows/test-phpstan.yml +++ b/.github/workflows/test-phpstan.yml @@ -36,4 +36,4 @@ jobs: run: composer install --no-progress --no-suggest --no-interaction - name: Run static analysis - run: vendor/bin/phpstan analyze --level=5 app system + run: vendor/bin/phpstan analyse diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index eaecfdf01bd9..835cf30cea72 100644 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -16,7 +16,6 @@ */ class Autoload extends AutoloadConfig { - /** * ------------------------------------------------------------------- * Namespaces diff --git a/phpstan.neon b/phpstan.neon index 5f3c09eff005..33525595ee64 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,45 +1,48 @@ parameters: - treatPhpDocTypesAsCertain: false - bootstrapFiles: - - qa-bootstrap.php - excludes_analyse: - - app/Views/errors/cli/error_404.php - - app/Views/errors/cli/error_exception.php - - app/Views/errors/html/error_exception.php - - system/Commands/Generators/Views/migration.tpl.php - - system/Config/Routes.php - - system/Debug/Toolbar/Views/toolbar.tpl.php - - system/Validation/Views/single.php - - system/ThirdParty/* - ignoreErrors: - - '#Unsafe usage of new static\(\)*#' - - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::_(disable|enable)ForeignKeyChecks\(\)#' - - '#Access to an undefined property CodeIgniter\\Config\\AutoloadConfig::(\$psr4|\$classmap)#' - - '#Access to an undefined property CodeIgniter\\Database\\Forge::\$dropConstraintStr#' - - '#Access to an undefined property CodeIgniter\\Config\\View::(\$filters|\$plugins)#' - - '#Access to protected property CodeIgniter\\Database\\BaseConnection::(\$DBDebug|\$DBPrefix|\$swapPre|\$charset|\$DBCollat)#' - - '#Access to an undefined property CodeIgniter\\Database\\BaseConnection::\$mysqli#' - - '#Access to an undefined property CodeIgniter\\Database\\ConnectionInterface::(\$DBDriver|\$connID|\$likeEscapeStr|\$likeEscapeChar|\$escapeChar|\$protectIdentifiers)#' - - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::supportsForeignKeys\(\)#' - - '#Cannot call method [a-zA-Z_]+\(\) on ((bool\|)?object\|resource)#' - - '#Cannot access property [\$a-z_]+ on ((bool\|)?object\|resource)#' - - '#Call to an undefined method CodeIgniter\\HTTP\\Request::(getPath|getSegments|getMethod|setLocale|getPost)\(\)#' - - '#Access to an undefined property CodeIgniter\\HTTP\\Request::\$uri#' - - '#Call to an undefined method CodeIgniter\\HTTP\\Message::setStatusCode\(\)#' - - '#Call to an undefined method CodeIgniter\\Database\\ConnectionInterface::(tableExists|protectIdentifiers|setAliasedTables|escapeIdentifiers|affectedRows|addTableAlias)\(\)#' - - '#Method CodeIgniter\\Database\\ConnectionInterface::query\(\) invoked with 3 parameters, 1-2 required#' - - '#Return type \(bool\) of method CodeIgniter\\Test\\TestLogger::log\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::log\(\)#' - - '#Call to an undefined method CodeIgniter\\Router\\RouteCollectionInterface::(getDefaultNamespace|isFiltered|getFilterForRoute|getRoutesOptions)\(\)#' - - '#Method CodeIgniter\\Router\\RouteCollectionInterface::getRoutes\(\) invoked with 1 parameter, 0 required#' - - '#Call to an undefined method CodeIgniter\\Validation\\ValidationInterface::loadRuleGroup\(\)#' - - '#Method CodeIgniter\\Validation\\ValidationInterface::run\(\) invoked with 3 parameters, 0-2 required#' - - '#Return type \(bool\) of method CodeIgniter\\Log\\Logger::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\)#' - - '#Return type \(bool\) of method CodeIgniter\\HTTP\\Files\\UploadedFile::move\(\) should be compatible with return type \(CodeIgniter\\Files\\File\) of method CodeIgniter\\Files\\File::move\(\)#' - - '#Call to function is_null\(\) with mysqli_stmt\|resource will always evaluate to false#' - - '#Negated boolean expression is always (true|false)#' - - '#Parameter \#1 \$db of class CodeIgniter\\Database\\SQLite3\\Table constructor expects CodeIgniter\\Database\\SQLite3\\Connection, CodeIgniter\\Database\\BaseConnection given#' - scanDirectories: - - system/Helpers - dynamicConstantNames: - - ENVIRONMENT - - CI_DEBUG \ No newline at end of file + level: 5 + paths: + - app + - system + treatPhpDocTypesAsCertain: false + bootstrapFiles: + - qa-bootstrap.php + excludes_analyse: + - app/Views/errors/cli/* + - app/Views/errors/html/* + - system/Commands/Generators/Views/* + - system/Config/Routes.php + - system/Debug/Toolbar/Views/toolbar.tpl.php + - system/Validation/Views/single.php + - system/ThirdParty/* + ignoreErrors: + - '#Unsafe usage of new static\(\)*#' + - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::_(disable|enable)ForeignKeyChecks\(\)#' + - '#Access to an undefined property CodeIgniter\\Database\\Forge::\$dropConstraintStr#' + - '#Access to protected property CodeIgniter\\Database\\BaseConnection::(\$DBDebug|\$DBPrefix|\$swapPre|\$charset|\$DBCollat)#' + - '#Access to an undefined property CodeIgniter\\Database\\BaseConnection::\$mysqli#' + - '#Access to an undefined property CodeIgniter\\Database\\ConnectionInterface::(\$DBDriver|\$connID|\$likeEscapeStr|\$likeEscapeChar|\$escapeChar|\$protectIdentifiers)#' + - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::supportsForeignKeys\(\)#' + - '#Cannot call method [a-zA-Z_]+\(\) on ((bool\|)?object\|resource)#' + - '#Cannot access property [\$a-z_]+ on ((bool\|)?object\|resource)#' + - '#Call to an undefined method CodeIgniter\\HTTP\\Request::(getPath|getSegments|getMethod|setLocale|getPost)\(\)#' + - '#Access to an undefined property CodeIgniter\\HTTP\\Request::\$uri#' + - '#Call to an undefined method CodeIgniter\\HTTP\\Message::setStatusCode\(\)#' + - '#Call to an undefined method CodeIgniter\\Database\\ConnectionInterface::(tableExists|protectIdentifiers|setAliasedTables|escapeIdentifiers|affectedRows|addTableAlias)\(\)#' + - '#Method CodeIgniter\\Database\\ConnectionInterface::query\(\) invoked with 3 parameters, 1-2 required#' + - '#Return type \(bool\) of method CodeIgniter\\Test\\TestLogger::log\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::log\(\)#' + - '#Call to an undefined method CodeIgniter\\Router\\RouteCollectionInterface::(getDefaultNamespace|isFiltered|getFilterForRoute|getRoutesOptions)\(\)#' + - '#Method CodeIgniter\\Router\\RouteCollectionInterface::getRoutes\(\) invoked with 1 parameter, 0 required#' + - '#Call to an undefined method CodeIgniter\\Validation\\ValidationInterface::loadRuleGroup\(\)#' + - '#Method CodeIgniter\\Validation\\ValidationInterface::run\(\) invoked with 3 parameters, 0-2 required#' + - '#Return type \(bool\) of method CodeIgniter\\Log\\Logger::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\)#' + - '#Return type \(bool\) of method CodeIgniter\\HTTP\\Files\\UploadedFile::move\(\) should be compatible with return type \(CodeIgniter\\Files\\File\) of method CodeIgniter\\Files\\File::move\(\)#' + - '#Call to function is_null\(\) with mysqli_stmt\|resource will always evaluate to false#' + - '#Negated boolean expression is always (true|false)#' + - '#Parameter \#1 \$db of class CodeIgniter\\Database\\SQLite3\\Table constructor expects CodeIgniter\\Database\\SQLite3\\Connection, CodeIgniter\\Database\\BaseConnection given#' + parallel: + processTimeout: 300.0 + scanDirectories: + - system/Helpers + dynamicConstantNames: + - ENVIRONMENT + - CI_DEBUG diff --git a/system/Config/AutoloadConfig.php b/system/Config/AutoloadConfig.php index 4c0786f5c7a9..65ba4c0123d5 100644 --- a/system/Config/AutoloadConfig.php +++ b/system/Config/AutoloadConfig.php @@ -40,13 +40,43 @@ namespace CodeIgniter\Config; /** - * AUTO-LOADER + * AUTOLOADER * * This file defines the namespaces and class maps so the Autoloader * can find the files as needed. */ class AutoloadConfig { + /** + * ------------------------------------------------------------------- + * Namespaces + * ------------------------------------------------------------------- + * This maps the locations of any namespaces in your application to + * their location on the file system. These are used by the autoloader + * to locate files the first time they have been instantiated. + * + * The '/app' and '/system' directories are already mapped for you. + * you may change the name of the 'App' namespace if you wish, + * but this should be done prior to creating any namespaced classes, + * else you will need to modify all of those classes for this to work. + * + * @var array + */ + public $psr4 = []; + + /** + * ------------------------------------------------------------------- + * Class Map + * ------------------------------------------------------------------- + * The class map provides a map of class names and their exact + * location on the drive. Classes loaded in this manner will have + * slightly faster performance because they will not have to be + * searched for within one or more directories as they would if they + * were being autoloaded through a namespace. + * + * @var array + */ + public $classmap = []; /** * ------------------------------------------------------------------- diff --git a/system/Config/View.php b/system/Config/View.php index 0de8cd40d534..5a35b50f761c 100644 --- a/system/Config/View.php +++ b/system/Config/View.php @@ -44,6 +44,22 @@ */ class View extends BaseConfig { + /** + * Parser Filters map a filter name with any PHP callable. When the + * Parser prepares a variable for display, it will chain it + * through the filters in the order defined, inserting any parameters. + * + * To prevent potential abuse, all filters MUST be defined here + * in order for them to be available for use within the Parser. + */ + public $filters = []; + + /** + * Parser Plugins provide a way to extend the functionality provided + * by the core Parser by creating aliases that will be replaced with + * any callable. Can be single or tag pair. + */ + public $plugins = []; /** * Built-in View filters. From aad5f2210bf44d29d5eb513b35a1e817cd1949bc Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" <51850998+paulbalandan@users.noreply.github.com> Date: Sat, 29 Aug 2020 18:24:11 +0800 Subject: [PATCH 044/328] Add __wakeup method to Time (#3557) Add __wakeup method to Time --- system/I18n/Time.php | 20 +++++++++++++++++++- tests/system/I18n/TimeTest.php | 10 ++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/system/I18n/Time.php b/system/I18n/Time.php index 329b0cf5b835..375e1ec32ef2 100644 --- a/system/I18n/Time.php +++ b/system/I18n/Time.php @@ -56,10 +56,11 @@ * Requires the intl PHP extension. * * @package CodeIgniter\I18n + * + * @property string $date */ class Time extends DateTime { - /** * @var \DateTimeZone */ @@ -1371,4 +1372,21 @@ public function __isset($name): bool return method_exists($this, $method); } + //-------------------------------------------------------------------- + + /** + * This is called when we unserialize the Time object. + */ + public function __wakeup() + { + /** + * Prior to unserialization, this is a string. + * + * @var string $timezone + */ + $timezone = $this->timezone; + + $this->timezone = new DateTimeZone($timezone); + parent::__construct($this->date, $this->timezone); + } } diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index 42b1a786da1c..839e23779f1c 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -1008,4 +1008,14 @@ public function testGetter() $this->assertNull($time->weekOfWeek); } + public function testUnserializeTimeObject() + { + $time1 = new Time('August 28, 2020 10:04:00pm', 'Asia/Manila', 'en'); + $timeCache = serialize($time1); + $time2 = unserialize($timeCache); + + $this->assertInstanceOf(Time::class, $time2); + $this->assertTrue($time2->equals($time1)); + $this->assertEquals($time1, $time2); + } } From 28b3da4ee05fc72b8e8fd83eaf7c9688177204af Mon Sep 17 00:00:00 2001 From: Natan Felles Date: Fri, 28 Aug 2020 21:42:09 -0300 Subject: [PATCH 045/328] Update PHPDoc param from array to mixed --- system/Common.php | 2 +- system/Router/RouteCollection.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Common.php b/system/Common.php index f15453751627..02d2804a2eb5 100644 --- a/system/Common.php +++ b/system/Common.php @@ -996,7 +996,7 @@ function remove_invisible_characters(string $str, bool $urlEncoded = true): stri * have a route defined in the routes Config file. * * @param string $method - * @param array ...$params + * @param mixed ...$params * * @return false|string */ diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index bf00fc553452..63c2a29ac05d 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -1206,7 +1206,7 @@ public function environment(string $env, \Closure $callback): RouteCollectionInt * reverseRoute('Controller::method', $param1, $param2); * * @param string $search - * @param array ...$params + * @param mixed ...$params * * @return string|false */ From 19b816e8856ecd3d497a0121c5c2033ecbdfc69d Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 29 Aug 2020 18:18:11 +0800 Subject: [PATCH 046/328] Deprecate BaseCommand::getPad and add more tests --- system/CLI/BaseCommand.php | 12 ++++++++++-- tests/_support/Commands/AppInfo.php | 1 + tests/system/Commands/HelpCommandTest.php | 6 ++++++ user_guide_src/source/changelogs/v4.0.5.rst | 4 ++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/system/CLI/BaseCommand.php b/system/CLI/BaseCommand.php index 8d47afeadd88..aeb98c561843 100644 --- a/system/CLI/BaseCommand.php +++ b/system/CLI/BaseCommand.php @@ -171,6 +171,7 @@ protected function showError(Throwable $e) public function showHelp() { CLI::write(lang('CLI.helpUsage'), 'yellow'); + if (! empty($this->usage)) { $usage = $this->usage; @@ -184,6 +185,7 @@ public function showHelp() $usage .= ' [arguments]'; } } + CLI::write($this->setPad($usage, 0, 0, 2)); if (! empty($this->description)) @@ -198,6 +200,7 @@ public function showHelp() CLI::newLine(); CLI::write(lang('CLI.helpArguments'), 'yellow'); $length = max(array_map('strlen', array_keys($this->arguments))); + foreach ($this->arguments as $argument => $description) { CLI::write(CLI::color($this->setPad($argument, $length, 2, 2), 'green') . $description); @@ -209,6 +212,7 @@ public function showHelp() CLI::newLine(); CLI::write(lang('CLI.helpOptions'), 'yellow'); $length = max(array_map('strlen', array_keys($this->options))); + foreach ($this->options as $option => $description) { CLI::write(CLI::color($this->setPad($option, $length, 2, 2), 'green') . $description); @@ -223,12 +227,12 @@ public function showHelp() * * @param string $item * @param integer $max - * @param integer $extra // How many extra spaces to add at the end + * @param integer $extra How many extra spaces to add at the end * @param integer $indent * * @return string */ - protected function setPad(string $item, int $max, int $extra = 2, int $indent = 0): string + public function setPad(string $item, int $max, int $extra = 2, int $indent = 0): string { $max += $extra + $indent; @@ -244,6 +248,10 @@ protected function setPad(string $item, int $max, int $extra = 2, int $indent = * @param integer $pad * * @return integer + * + * @deprecated Use setPad() instead. + * + * @codeCoverageIgnore */ public function getPad(array $array, int $pad): int { diff --git a/tests/_support/Commands/AppInfo.php b/tests/_support/Commands/AppInfo.php index 41d26668292a..6e9191c10f05 100644 --- a/tests/_support/Commands/AppInfo.php +++ b/tests/_support/Commands/AppInfo.php @@ -10,6 +10,7 @@ class AppInfo extends BaseCommand protected $group = 'demo'; protected $name = 'app:info'; + protected $arguments = ['draft' => 'unused']; protected $description = 'Displays basic application information.'; public function run(array $params) diff --git a/tests/system/Commands/HelpCommandTest.php b/tests/system/Commands/HelpCommandTest.php index 3af8b113ff31..9b1dc1a9a62d 100644 --- a/tests/system/Commands/HelpCommandTest.php +++ b/tests/system/Commands/HelpCommandTest.php @@ -37,6 +37,12 @@ public function testHelpCommand() $this->assertStringContainsString('command_name', $this->getBuffer()); } + public function testHelpCommandWithMissingUsage() + { + command('help app:info'); + $this->assertStringContainsString('app:info [arguments]', $this->getBuffer()); + } + public function testHelpCommandOnSpecificCommand() { command('help cache:clear'); diff --git a/user_guide_src/source/changelogs/v4.0.5.rst b/user_guide_src/source/changelogs/v4.0.5.rst index 4884b70264d8..4b0df7bcdb7b 100644 --- a/user_guide_src/source/changelogs/v4.0.5.rst +++ b/user_guide_src/source/changelogs/v4.0.5.rst @@ -17,3 +17,7 @@ Bugs Fixed: - Fixed a bug in ``Entity`` class where declaring class parameters was preventing data propagation to the ``attributes`` array. - Handling for the environment variable ``encryption.key`` has changed. Previously, explicit function calls, like ``getenv('encryption.key')`` or ``env('encryption.key')`` where the value has the special prefix ``hex2bin:`` returns an automatically converted binary string. This is now changed to just return the character string with the prefix. This change was due to incompatibility with handling binary strings in environment variables on Windows platforms. However, accessing ``$key`` using ``Encryption`` class config remains unchanged and still returns a binary string. + +Deprecations: + +- Deprecated ``BaseCommand::getPad`` in favor of ``BaseCommand::setPad``. From f0c0933377f5d15854f2ff02b8e6dc4d63034ebd Mon Sep 17 00:00:00 2001 From: Mohammad Date: Sat, 8 Aug 2020 19:48:08 +0430 Subject: [PATCH 047/328] Update Model.php add setAllowedFields method --- system/Model.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/system/Model.php b/system/Model.php index 802d72eda9ba..a8b2a2732506 100644 --- a/system/Model.php +++ b/system/Model.php @@ -1578,6 +1578,19 @@ public function errors(bool $forceDB = false) return $error['message'] ?? null; } + + /** + * Allows to set allowed fields. + * It could be used when you have to change default or override current allowed fields. + * + * @param array $allowedFields + * + * @return void + */ + public function setAllowedFields(array $allowedFields) + { + $this->$allowedFields = $allowedFields; + } //-------------------------------------------------------------------- //-------------------------------------------------------------------- From 428cd0c242e4b3582aaeb1abe3ad3df612af322e Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 30 Aug 2020 01:02:48 +0800 Subject: [PATCH 048/328] Add tests for Model::setAllowedFields --- system/Model.php | 7 +++--- tests/system/Database/Live/ModelTest.php | 27 +++++++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/system/Model.php b/system/Model.php index a8b2a2732506..0c300d329c4d 100644 --- a/system/Model.php +++ b/system/Model.php @@ -1578,9 +1578,10 @@ public function errors(bool $forceDB = false) return $error['message'] ?? null; } - + + //-------------------------------------------------------------------- + /** - * Allows to set allowed fields. * It could be used when you have to change default or override current allowed fields. * * @param array $allowedFields @@ -1589,7 +1590,7 @@ public function errors(bool $forceDB = false) */ public function setAllowedFields(array $allowedFields) { - $this->$allowedFields = $allowedFields; + $this->allowedFields = $allowedFields; } //-------------------------------------------------------------------- diff --git a/tests/system/Database/Live/ModelTest.php b/tests/system/Database/Live/ModelTest.php index 5b6eabe3f397..748d6b264a9f 100644 --- a/tests/system/Database/Live/ModelTest.php +++ b/tests/system/Database/Live/ModelTest.php @@ -1,4 +1,6 @@ -assertFalse($this->getPrivateProperty($model, 'tempUseSoftDeletes')); } + public function testSetAllowedFields() + { + $allowed1 = [ + 'id', + 'created_at', + ]; + $allowed2 = [ + 'id', + 'updated_at', + ]; + + $model = new class extends Model { + protected $allowedFields = [ + 'id', + 'created_at', + ]; + }; + + $this->assertSame($allowed1, $this->getPrivateProperty($model, 'allowedFields')); + + $model->setAllowedFields($allowed2); + $this->assertSame($allowed2, $this->getPrivateProperty($model, 'allowedFields')); + } } From c0bb249048d04dc843e8b4b8426bfb5065149413 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 30 Aug 2020 03:13:54 +0700 Subject: [PATCH 049/328] apply strict in_array with pass true to 3rd parameter --- system/API/ResponseTrait.php | 2 +- system/Commands/Database/MigrateStatus.php | 2 +- system/Common.php | 2 +- system/Config/BaseService.php | 2 +- system/Database/BaseBuilder.php | 2 +- system/Database/BaseConnection.php | 14 +++---- system/Database/BaseUtils.php | 2 +- system/Database/Forge.php | 6 +-- system/Database/MigrationRunner.php | 2 +- system/Database/MySQLi/Forge.php | 4 +- system/Database/SQLite3/Forge.php | 2 +- system/Debug/Exceptions.php | 4 +- system/Email/Email.php | 4 +- system/Encryption/Encryption.php | 2 +- system/Entity.php | 6 +-- system/Format/JSONFormatter.php | 2 +- system/HTTP/ContentSecurityPolicy.php | 4 +- system/HTTP/IncomingRequest.php | 2 +- system/HTTP/Message.php | 2 +- system/HTTP/URI.php | 4 +- system/Helpers/form_helper.php | 6 +-- system/Helpers/html_helper.php | 2 +- system/Helpers/inflector_helper.php | 2 +- system/Images/Handlers/BaseHandler.php | 8 ++-- system/Images/Handlers/ImageMagickHandler.php | 2 +- system/Language/Language.php | 2 +- system/Log/Handlers/BaseHandler.php | 2 +- system/Log/Logger.php | 4 +- system/Model.php | 4 +- system/Modules/Modules.php | 2 +- system/RESTful/ResourceController.php | 2 +- system/Router/RouteCollection.php | 42 +++++++++---------- system/Test/Fabricator.php | 2 +- system/Test/TestLogger.php | 2 +- system/Typography/Typography.php | 2 +- system/Validation/CreditCardRules.php | 6 ++- system/Validation/FileRules.php | 4 +- system/Validation/FormatRules.php | 2 +- system/Validation/Validation.php | 8 ++-- system/View/View.php | 2 +- 40 files changed, 89 insertions(+), 87 deletions(-) diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index f42e5346a504..7091dbe5f6c0 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -390,7 +390,7 @@ protected function format($data = null) $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'])) + if (empty($this->format) || ! in_array($this->format, ['json', 'xml'], true)) { $format = $this->request->negotiate('media', $config->supportedResponseFormats, false); } diff --git a/system/Commands/Database/MigrateStatus.php b/system/Commands/Database/MigrateStatus.php index e6bcef43068c..021a36198cc1 100644 --- a/system/Commands/Database/MigrateStatus.php +++ b/system/Commands/Database/MigrateStatus.php @@ -136,7 +136,7 @@ public function run(array $params) // Loop for all $namespaces foreach ($namespaces as $namespace => $path) { - if (in_array($namespace, $this->ignoredNamespaces)) + if (in_array($namespace, $this->ignoredNamespaces, true)) { continue; } diff --git a/system/Common.php b/system/Common.php index 02d2804a2eb5..6fa2b1cf4080 100644 --- a/system/Common.php +++ b/system/Common.php @@ -467,7 +467,7 @@ function esc($data, string $context = 'html', string $encoding = null) return $data; } - if (! in_array($context, ['html', 'js', 'css', 'url', 'attr'])) + if (! in_array($context, ['html', 'js', 'css', 'url', 'attr'], true)) { throw new InvalidArgumentException('Invalid escape context provided.'); } diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index 3ea86e32a8e7..98fab42db0e5 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -267,7 +267,7 @@ protected static function discoverServices(string $name, array $arguments) { $classname = $locator->getClassname($file); - if (! in_array($classname, ['CodeIgniter\\Config\\Services'])) + if (! in_array($classname, ['CodeIgniter\\Config\\Services'], true)) { static::$services[] = new $classname(); } diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 5e79b0b9b0c3..2cda667ae767 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -513,7 +513,7 @@ protected function maxMinAvgSum(string $select = '', string $alias = '', string $type = strtoupper($type); - if (! in_array($type, ['MAX', 'MIN', 'AVG', 'SUM', 'COUNT'])) + if (! in_array($type, ['MAX', 'MIN', 'AVG', 'SUM', 'COUNT'], true)) { throw new DatabaseException('Invalid function type: ' . $type); } diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index c30aafa1bfa2..17c28b946ea2 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -602,7 +602,7 @@ public function setAliasedTables(array $aliases) */ public function addTableAlias(string $table) { - if (! in_array($table, $this->aliasedTables)) + if (! in_array($table, $this->aliasedTables, true)) { $this->aliasedTables[] = $table; } @@ -1171,13 +1171,13 @@ public function protectIdentifiers($item, bool $prefixSingle = false, bool $prot // // NOTE: The ! empty() condition prevents this method // from breaking when QB isn't enabled. - if (! empty($this->aliasedTables) && in_array($parts[0], $this->aliasedTables)) + if (! empty($this->aliasedTables) && in_array($parts[0], $this->aliasedTables, true)) { if ($protectIdentifiers === true) { foreach ($parts as $key => $val) { - if (! in_array($val, $this->reservedIdentifiers)) + if (! in_array($val, $this->reservedIdentifiers, true)) { $parts[$key] = $this->escapeIdentifiers($val); } @@ -1262,7 +1262,7 @@ public function protectIdentifiers($item, bool $prefixSingle = false, bool $prot } } - if ($protectIdentifiers === true && ! in_array($item, $this->reservedIdentifiers)) + if ($protectIdentifiers === true && ! in_array($item, $this->reservedIdentifiers, true)) { $item = $this->escapeIdentifiers($item); } @@ -1283,7 +1283,7 @@ public function protectIdentifiers($item, bool $prefixSingle = false, bool $prot */ public function escapeIdentifiers($item) { - if ($this->escapeChar === '' || empty($item) || in_array($item, $this->reservedIdentifiers)) + if ($this->escapeChar === '' || empty($item) || in_array($item, $this->reservedIdentifiers, true)) { return $item; } @@ -1587,7 +1587,7 @@ public function listTables(bool $constrainByPrefix = false) */ public function tableExists(string $tableName): bool { - return in_array($this->protectIdentifiers($tableName, true, false, false), $this->listTables()); + return in_array($this->protectIdentifiers($tableName, true, false, false), $this->listTables(), true); } //-------------------------------------------------------------------- @@ -1662,7 +1662,7 @@ public function getFieldNames(string $table) */ public function fieldExists(string $fieldName, string $tableName): bool { - return in_array($fieldName, $this->getFieldNames($tableName)); + return in_array($fieldName, $this->getFieldNames($tableName), true); } //-------------------------------------------------------------------- diff --git a/system/Database/BaseUtils.php b/system/Database/BaseUtils.php index 8261fabcda76..34c1962eec12 100644 --- a/system/Database/BaseUtils.php +++ b/system/Database/BaseUtils.php @@ -139,7 +139,7 @@ public function listDatabases() */ public function databaseExists(string $database_name): bool { - return in_array($database_name, $this->listDatabases()); + return in_array($database_name, $this->listDatabases(), true); } //-------------------------------------------------------------------- diff --git a/system/Database/Forge.php b/system/Database/Forge.php index a87103eaf595..ba0880af791d 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1256,7 +1256,7 @@ protected function _processIndexes(string $table) continue; } - if (in_array($i, $this->uniqueKeys)) + if (in_array($i, $this->uniqueKeys, true)) { $sqls[] = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table) . ' ADD CONSTRAINT ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) @@ -1302,12 +1302,12 @@ protected function _processForeignKeys(string $table): string $sql .= ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers($name_index) . ' FOREIGN KEY(' . $this->db->escapeIdentifiers($field) . ') REFERENCES ' . $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['table']) . ' (' . $this->db->escapeIdentifiers($fkey['field']) . ')'; - if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions)) + if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { $sql .= ' ON DELETE ' . $fkey['onDelete']; } - if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions)) + if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { $sql .= ' ON UPDATE ' . $fkey['onUpdate']; } diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index baecdbf597f6..68e771373cd4 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -293,7 +293,7 @@ public function regress(int $targetBatch = 0, string $group = null) } // Make sure $targetBatch is found - if ($targetBatch !== 0 && ! in_array($targetBatch, $batches)) + if ($targetBatch !== 0 && ! in_array($targetBatch, $batches, true)) { $message = lang('Migrations.batchNotFound') . $targetBatch; diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index 27a82e12757d..5755c1aecf20 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -136,7 +136,7 @@ protected function _createTableAttributes(array $attributes): string { $sql .= ' ' . strtoupper($key) . ' = '; - if (in_array(strtoupper($key), $this->_quoted_table_options)) + if (in_array(strtoupper($key), $this->_quoted_table_options, true)) { $sql .= $this->db->escape($attributes[$key]); } @@ -265,7 +265,7 @@ protected function _processIndexes(string $table): string // @phpstan-ignore-next-line is_array($this->keys[$i]) || $this->keys[$i] = [$this->keys[$i]]; - $unique = in_array($i, $this->uniqueKeys) ? 'UNIQUE ' : ''; + $unique = in_array($i, $this->uniqueKeys, true) ? 'UNIQUE ' : ''; $sql .= ",\n\t{$unique}KEY " . $this->db->escapeIdentifiers(implode('_', $this->keys[$i])) . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')'; diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index 4c175a2dc91b..3591b05595e6 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -234,7 +234,7 @@ protected function _processIndexes(string $table): array continue; } - if (in_array($i, $this->uniqueKeys)) + if (in_array($i, $this->uniqueKeys, true)) { $sqls[] = 'CREATE UNIQUE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) . ' ON ' . $this->db->escapeIdentifiers($table) diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index 5e4b824a978e..c2332bdaa9d5 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -150,7 +150,7 @@ public function exceptionHandler(Throwable $exception) ] = $this->determineCodes($exception); // Log it - if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes)) + if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes, true)) { log_message('critical', $exception->getMessage() . "\n{trace}", [ 'trace' => $exception->getTraceAsString(), @@ -219,7 +219,7 @@ public function shutdownHandler() if (! is_null($error)) { // Fatal Error? - if (in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) + if (in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE], true)) { $this->exceptionHandler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); } diff --git a/system/Email/Email.php b/system/Email/Email.php index 57cf79e248f2..3aa068cb4606 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -813,7 +813,7 @@ public function setPriority($n = 3) */ public function setNewline($newline = "\n") { - $this->newline = in_array($newline, ["\n", "\r\n", "\r"]) ? $newline : "\n"; + $this->newline = in_array($newline, ["\n", "\r\n", "\r"], true) ? $newline : "\n"; return $this; } //-------------------------------------------------------------------- @@ -860,7 +860,7 @@ protected function getProtocol() */ protected function getEncoding() { - in_array($this->encoding, $this->bitDepths) || $this->encoding = '8bit'; // @phpstan-ignore-line + in_array($this->encoding, $this->bitDepths, true) || $this->encoding = '8bit'; // @phpstan-ignore-line foreach ($this->baseCharsets as $charset) { if (strpos($this->charset, $charset) === 0) diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index 61301c6e6f80..aa31e5f95c55 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -140,7 +140,7 @@ public function initialize(EncryptionConfig $config = null) } // Check for an unknown driver - if (! in_array($this->driver, $this->drivers)) + if (! in_array($this->driver, $this->drivers, true)) { throw EncryptionException::forUnKnownHandler($this->driver); } diff --git a/system/Entity.php b/system/Entity.php index 6a588e715602..65ad75375e20 100644 --- a/system/Entity.php +++ b/system/Entity.php @@ -331,7 +331,7 @@ public function __get(string $key) } // Do we need to mutate this into a date? - if (in_array($key, $this->dates)) + if (in_array($key, $this->dates, true)) { $result = $this->mutateDate($result); } @@ -366,7 +366,7 @@ public function __set(string $key, $value = null) $key = $this->mapProperty($key); // Check if the field should be mutated into a date - if (in_array($key, $this->dates)) + if (in_array($key, $this->dates, true)) { $value = $this->mutateDate($value); } @@ -623,7 +623,7 @@ private function castAsJson($value, bool $asArray = false) $tmp = ! is_null($value) ? ($asArray ? [] : new \stdClass) : null; if (function_exists('json_decode')) { - if ((is_string($value) && strlen($value) > 1 && in_array($value[0], ['[', '{', '"'])) || is_numeric($value)) + if ((is_string($value) && strlen($value) > 1 && in_array($value[0], ['[', '{', '"'], true)) || is_numeric($value)) { $tmp = json_decode($value, $asArray); diff --git a/system/Format/JSONFormatter.php b/system/Format/JSONFormatter.php index c26ca5b58b4e..bff2aa1ea249 100644 --- a/system/Format/JSONFormatter.php +++ b/system/Format/JSONFormatter.php @@ -66,7 +66,7 @@ public function format($data) $result = json_encode($data, $options, 512); - if (! in_array(json_last_error(), [JSON_ERROR_NONE, JSON_ERROR_RECURSION])) + if (! in_array(json_last_error(), [JSON_ERROR_NONE, JSON_ERROR_RECURSION], true)) { throw FormatException::forInvalidJSON(json_last_error_msg()); } diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 9a05fdbe6488..6b392f8d5afe 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -819,7 +819,7 @@ protected function addToHeader(string $name, $values = null) if ($reportOnly === true) { - $reportSources[] = in_array($value, $this->validSources) ? "'{$value}'" : $value; + $reportSources[] = in_array($value, $this->validSources, true) ? "'{$value}'" : $value; } else { @@ -829,7 +829,7 @@ protected function addToHeader(string $name, $values = null) } else { - $sources[] = in_array($value, $this->validSources) ? "'{$value}'" : $value; + $sources[] = in_array($value, $this->validSources, true) ? "'{$value}'" : $value; } } } diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index 071f76c2236c..3a95940b51d7 100755 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -246,7 +246,7 @@ public function setLocale(string $locale) { // If it's not a valid locale, set it // to the default locale for the site. - if (! in_array($locale, $this->validLocales)) + if (! in_array($locale, $this->validLocales, true)) { $locale = $this->defaultLocale; } diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 9cc4594efb59..60f88ccb8701 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -379,7 +379,7 @@ public function setProtocolVersion(string $version) $version = substr($version, strpos($version, '/') + 1); } - if (! in_array($version, $this->validProtocolVersions)) + if (! in_array($version, $this->validProtocolVersions, true)) { throw HTTPException::forInvalidHTTPProtocol(implode(', ', $this->validProtocolVersions)); } diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 5158929ae3fe..00ee8f0f2c51 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -896,7 +896,7 @@ public function stripQuery(...$params) * Filters the query variables so that only the keys passed in * are kept. The rest are removed from the object. * - * @param array ...$params + * @param string ...$params * * @return $this */ @@ -906,7 +906,7 @@ public function keepQuery(...$params) foreach ($this->query as $key => $value) { - if (! in_array($key, $params)) + if (! in_array($key, $params, true)) { continue; } diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 60eff5498ad6..554c54940984 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -102,7 +102,7 @@ function form_open(string $action = '', $attributes = [], array $hidden = []): s $before = Services::filters() ->getFilters()['before']; - if ((in_array('csrf', $before) || array_key_exists('csrf', $before)) && strpos($action, base_url()) !== false && ! stripos($form, 'method="get"')) + if ((in_array('csrf', $before, true) || array_key_exists('csrf', $before)) && strpos($action, base_url()) !== false && ! stripos($form, 'method="get"')) { $form .= csrf_field($csrfId ?? null); } @@ -423,7 +423,7 @@ function form_dropdown($data = '', $options = [], $selected = [], $extra = ''): $form .= '\n"; foreach ($val as $optgroup_key => $optgroup_val) { - $sel = in_array($optgroup_key, $selected) ? ' selected="selected"' : ''; + $sel = in_array($optgroup_key, $selected, true) ? ' selected="selected"' : ''; $form .= '\n"; } @@ -432,7 +432,7 @@ function form_dropdown($data = '', $options = [], $selected = [], $extra = ''): else { $form .= '\n"; } } diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 5350a02e1f67..53ba7b0b7747 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -323,7 +323,7 @@ function link_tag($href = '', string $rel = 'stylesheet', string $type = 'text/c $link .= 'rel="' . $rel . '" '; - if (! in_array($rel, ['alternate', 'canonical'])) + if (! in_array($rel, ['alternate', 'canonical'], true)) { $link .= 'type="' . $type . '" '; } diff --git a/system/Helpers/inflector_helper.php b/system/Helpers/inflector_helper.php index d72b9f6e8093..7afc7c87bd48 100755 --- a/system/Helpers/inflector_helper.php +++ b/system/Helpers/inflector_helper.php @@ -339,7 +339,7 @@ function is_pluralizable(string $word): bool 'weather', 'wisdom', 'work', - ]); + ], true); return ! $uncountables; } diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 2f12d744d57e..2cb60679afdf 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -361,12 +361,12 @@ public function rotate(float $angle) { // Allowed rotation values $degs = [ - 90, - 180, - 270, + (float) 90, + (float) 180, + (float) 270, ]; - if (! in_array($angle, $degs)) + if (! in_array($angle, $degs, true)) { throw ImageException::forMissingAngle(); } diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index 3529ed7fd01b..5d0aa29fe703 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -388,7 +388,7 @@ protected function supportedFormatCheck() switch ($this->image()->imageType) { case IMAGETYPE_WEBP: - if (! in_array('WEBP', \Imagick::queryFormats())) + if (! in_array('WEBP', \Imagick::queryFormats(), true)) { throw ImageException::forInvalidImageCreate(lang('images.webpNotSupported')); } diff --git a/system/Language/Language.php b/system/Language/Language.php index 7648c45bd43c..ac01fd9ae4aa 100644 --- a/system/Language/Language.php +++ b/system/Language/Language.php @@ -295,7 +295,7 @@ protected function load(string $file, string $locale, bool $return = false) $this->loadedFiles[$locale] = []; } - if (in_array($file, $this->loadedFiles[$locale])) + if (in_array($file, $this->loadedFiles[$locale], true)) { // Don't load it more than once. return []; diff --git a/system/Log/Handlers/BaseHandler.php b/system/Log/Handlers/BaseHandler.php index a68e2946638f..39de171466d2 100644 --- a/system/Log/Handlers/BaseHandler.php +++ b/system/Log/Handlers/BaseHandler.php @@ -83,7 +83,7 @@ public function __construct(array $config) */ public function canHandle(string $level): bool { - return in_array($level, $this->handles); + return in_array($level, $this->handles, true); } //-------------------------------------------------------------------- diff --git a/system/Log/Logger.php b/system/Log/Logger.php index f6655648b70b..5d4c14d544f7 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -338,7 +338,7 @@ public function log($level, $message, array $context = []): bool } // Does the app want to log this right now? - if (! in_array($level, $this->loggableLevels)) + if (! in_array($level, $this->loggableLevels, true)) { return false; } @@ -498,7 +498,7 @@ public function determineFile(): array // Find the first reference to a Logger class method foreach ($stackFrames as $frame) { - if (\in_array($frame['function'], $logFunctions)) + if (\in_array($frame['function'], $logFunctions, true)) { $file = isset($frame['file']) ? $this->cleanFileNames($frame['file']) : 'unknown'; $line = $frame['line'] ?? 'unknown'; diff --git a/system/Model.php b/system/Model.php index 0c300d329c4d..85de387acf85 100644 --- a/system/Model.php +++ b/system/Model.php @@ -691,7 +691,7 @@ public static function classToArray($data, $primaryKey = null, string $dateForma $properties = $data->toRawArray($onlyChanged); // Always grab the primary key otherwise updates will fail. - if (! empty($properties) && ! empty($primaryKey) && ! in_array($primaryKey, $properties) && ! empty($data->{$primaryKey})) + if (! empty($properties) && ! empty($primaryKey) && ! in_array($primaryKey, $properties, true) && ! empty($data->{$primaryKey})) { $properties[$primaryKey] = $data->{$primaryKey}; } @@ -1489,7 +1489,7 @@ protected function doProtectFields(array $data): array foreach ($data as $key => $val) { - if (! in_array($key, $this->allowedFields)) + if (! in_array($key, $this->allowedFields, true)) { unset($data[$key]); } diff --git a/system/Modules/Modules.php b/system/Modules/Modules.php index 590693501c90..ee8308efcf30 100644 --- a/system/Modules/Modules.php +++ b/system/Modules/Modules.php @@ -83,6 +83,6 @@ public function shouldDiscover(string $alias): bool return false; } - return in_array(strtolower($alias), $this->aliases); + return in_array(strtolower($alias), $this->aliases, true); } } diff --git a/system/RESTful/ResourceController.php b/system/RESTful/ResourceController.php index d13218cabb1c..5dec51cb9e04 100644 --- a/system/RESTful/ResourceController.php +++ b/system/RESTful/ResourceController.php @@ -194,7 +194,7 @@ public function setModel($which = null) */ public function setFormat(string $format = 'json') { - if (in_array($format, ['json', 'xml'])) + if (in_array($format, ['json', 'xml'], true)) { $this->format = $format; } diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 63c2a29ac05d..77491b1fe580 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -835,39 +835,39 @@ public function resource(string $name, array $options = null): RouteCollectionIn $options['except'] = is_array($options['except']) ? $options['except'] : explode(',', $options['except']); foreach ($methods as $i => $method) { - if (in_array($method, $options['except'])) + if (in_array($method, $options['except'], true)) { unset($methods[$i]); } } } - if (in_array('index', $methods)) + if (in_array('index', $methods, true)) { $this->get($name, $new_name . '::index', $options); } - if (in_array('new', $methods)) + if (in_array('new', $methods, true)) { $this->get($name . '/new', $new_name . '::new', $options); } - if (in_array('edit', $methods)) + if (in_array('edit', $methods, true)) { $this->get($name . '/' . $id . '/edit', $new_name . '::edit/$1', $options); } - if (in_array('show', $methods)) + if (in_array('show', $methods, true)) { $this->get($name . '/' . $id, $new_name . '::show/$1', $options); } - if (in_array('create', $methods)) + if (in_array('create', $methods, true)) { $this->post($name, $new_name . '::create', $options); } - if (in_array('update', $methods)) + if (in_array('update', $methods, true)) { $this->put($name . '/' . $id, $new_name . '::update/$1', $options); $this->patch($name . '/' . $id, $new_name . '::update/$1', $options); } - if (in_array('delete', $methods)) + if (in_array('delete', $methods, true)) { $this->delete($name . '/' . $id, $new_name . '::delete/$1', $options); } @@ -875,11 +875,11 @@ public function resource(string $name, array $options = null): RouteCollectionIn // Web Safe? delete needs checking before update because of method name if (isset($options['websafe'])) { - if (in_array('delete', $methods)) + if (in_array('delete', $methods, true)) { $this->post($name . '/' . $id . '/delete', $new_name . '::delete/$1', $options); } - if (in_array('update', $methods)) + if (in_array('update', $methods, true)) { $this->post($name . '/' . $id, $new_name . '::update/$1', $options); } @@ -948,50 +948,50 @@ public function presenter(string $name, array $options = null): RouteCollectionI $options['except'] = is_array($options['except']) ? $options['except'] : explode(',', $options['except']); foreach ($methods as $i => $method) { - if (in_array($method, $options['except'])) + if (in_array($method, $options['except'], true)) { unset($methods[$i]); } } } - if (in_array('index', $methods)) + if (in_array('index', $methods, true)) { $this->get($name, $newName . '::index', $options); } - if (in_array('show', $methods)) + if (in_array('show', $methods, true)) { $this->get($name . '/show/' . $id, $newName . '::show/$1', $options); } - if (in_array('new', $methods)) + if (in_array('new', $methods, true)) { $this->get($name . '/new', $newName . '::new', $options); } - if (in_array('create', $methods)) + if (in_array('create', $methods, true)) { $this->post($name . '/create', $newName . '::create', $options); } - if (in_array('edit', $methods)) + if (in_array('edit', $methods, true)) { $this->get($name . '/edit/' . $id, $newName . '::edit/$1', $options); } - if (in_array('update', $methods)) + if (in_array('update', $methods, true)) { $this->post($name . '/update/' . $id, $newName . '::update/$1', $options); } - if (in_array('remove', $methods)) + if (in_array('remove', $methods, true)) { $this->get($name . '/remove/' . $id, $newName . '::remove/$1', $options); } - if (in_array('delete', $methods)) + if (in_array('delete', $methods, true)) { $this->post($name . '/delete/' . $id, $newName . '::delete/$1', $options); } - if (in_array('show', $methods)) + if (in_array('show', $methods, true)) { $this->get($name . '/' . $id, $newName . '::show/$1', $options); } - if (in_array('create', $methods)) + if (in_array('create', $methods, true)) { $this->post($name, $newName . '::create', $options); } diff --git a/system/Test/Fabricator.php b/system/Test/Fabricator.php index 554a9492cce1..ded15dfd042c 100644 --- a/system/Test/Fabricator.php +++ b/system/Test/Fabricator.php @@ -370,7 +370,7 @@ protected function guessFormatter($field): string } // Next look for known model fields - if (in_array($field, $this->dateFields)) + if (in_array($field, $this->dateFields, true)) { switch ($this->model->dateFormat) { diff --git a/system/Test/TestLogger.php b/system/Test/TestLogger.php index f0c1c94b1694..450d15b8f021 100644 --- a/system/Test/TestLogger.php +++ b/system/Test/TestLogger.php @@ -32,7 +32,7 @@ public function log($level, $message, array $context = []): bool foreach ($trace as $row) { - if (! in_array($row['function'], ['log', 'log_message'])) + if (! in_array($row['function'], ['log', 'log_message'], true)) { $file = basename($row['file'] ?? ''); break; diff --git a/system/Typography/Typography.php b/system/Typography/Typography.php index 4e632d238766..8b60a57793cb 100644 --- a/system/Typography/Typography.php +++ b/system/Typography/Typography.php @@ -339,7 +339,7 @@ public function formatCharacters(string $str): string */ protected function formatNewLines(string $str): string { - if ($str === '' || ( strpos($str, "\n") === false && ! in_array($this->lastBlockElement, $this->innerBlockRequired))) + if ($str === '' || ( strpos($str, "\n") === false && ! in_array($this->lastBlockElement, $this->innerBlockRequired, true))) { return $str; } diff --git a/system/Validation/CreditCardRules.php b/system/Validation/CreditCardRules.php index 53234e9ef0d3..4a24d3887b42 100644 --- a/system/Validation/CreditCardRules.php +++ b/system/Validation/CreditCardRules.php @@ -245,9 +245,11 @@ public function valid_cc_number(string $ccNumber = null, string $type): bool } // Make sure it's a valid length for this card - $lengths = explode(',', $info['length']); + $lengths = array_map(function ($value) { + return (int) $value; + }, explode(',', $info['length'])); - if (! in_array(strlen($ccNumber), $lengths)) + if (! in_array(strlen($ccNumber), $lengths, true)) { return false; } diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index 61bedb9e8bdb..bed8102ca5b5 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -248,7 +248,7 @@ public function mime_in(string $blank = null, string $params): bool return true; } - if (! in_array($file->getMimeType(), $params)) + if (! in_array($file->getMimeType(), $params, true)) { return false; } @@ -291,7 +291,7 @@ public function ext_in(string $blank = null, string $params): bool return true; } - if (! in_array($file->getExtension(), $params)) + if (! in_array($file->getExtension(), $params, true)) { return false; } diff --git a/system/Validation/FormatRules.php b/system/Validation/FormatRules.php index f121b702c401..555c09244005 100644 --- a/system/Validation/FormatRules.php +++ b/system/Validation/FormatRules.php @@ -243,7 +243,7 @@ public function regex_match(string $str = null, string $pattern): bool */ public function timezone(string $str = null): bool { - return in_array($str, timezone_identifiers_list()); + return in_array($str, timezone_identifiers_list(), true); } /** diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index f481997f0e77..ae1664992731 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -234,7 +234,7 @@ public function check($value, string $rule, array $errors = []): bool protected function processRules(string $field, string $label = null, $value, $rules = null, array $data): bool { // If the if_exist rule is defined... - if (in_array('if_exist', $rules)) + if (in_array('if_exist', $rules, true)) { // and the current field does not exists in the input data // we can return true. Ignoring all other rules to this field. @@ -248,7 +248,7 @@ protected function processRules(string $field, string $label = null, $value, $ru if (in_array('permit_empty', $rules)) { - if (! in_array('required', $rules) && (is_array($value) ? empty($value) : (trim($value) === ''))) + if (! in_array('required', $rules, true) && (is_array($value) ? empty($value) : (trim($value) === ''))) { $passed = true; @@ -259,7 +259,7 @@ protected function processRules(string $field, string $label = null, $value, $ru $rule = $match[1]; $param = $match[2]; - if (! in_array($rule, ['required_with', 'required_without'])) + if (! in_array($rule, ['required_with', 'required_without'], true)) { continue; } @@ -365,7 +365,7 @@ protected function processRules(string $field, string $label = null, $value, $ru */ public function withRequest(RequestInterface $request): ValidationInterface { - if (in_array($request->getMethod(), ['put', 'patch', 'delete'])) + if (in_array($request->getMethod(), ['put', 'patch', 'delete'], true)) { $this->data = $request->getRawInput(); } diff --git a/system/View/View.php b/system/View/View.php index 523ffcb8e10d..f388a0bee7ee 100644 --- a/system/View/View.php +++ b/system/View/View.php @@ -265,7 +265,7 @@ public function render(string $view, array $options = null, bool $saveData = nul { $toolbarCollectors = config(\Config\Toolbar::class)->collectors; - if (in_array(\CodeIgniter\Debug\Toolbar\Collectors\Views::class, $toolbarCollectors)) + if (in_array(\CodeIgniter\Debug\Toolbar\Collectors\Views::class, $toolbarCollectors, true)) { // Clean up our path names to make them a little cleaner $this->renderVars['file'] = clean_path($this->renderVars['file']); From 84a31192b0dd03406fb7db598c07d6b1e90d0e6f Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 30 Aug 2020 03:43:51 +0700 Subject: [PATCH 050/328] using 2.0 for valid HTTP protocol version --- system/HTTP/Message.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 60f88ccb8701..0121e89528b8 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -78,7 +78,7 @@ class Message protected $validProtocolVersions = [ '1.0', '1.1', - '2', + '2.0', ]; /** From 3e7f445a2dfa18d4a6c99ede23ec22e27c7719de Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 30 Aug 2020 03:55:39 +0700 Subject: [PATCH 051/328] cast strlen() to string to match $lengths in_array from explode() --- system/Validation/CreditCardRules.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/system/Validation/CreditCardRules.php b/system/Validation/CreditCardRules.php index 4a24d3887b42..3faeffb5dc65 100644 --- a/system/Validation/CreditCardRules.php +++ b/system/Validation/CreditCardRules.php @@ -245,11 +245,9 @@ public function valid_cc_number(string $ccNumber = null, string $type): bool } // Make sure it's a valid length for this card - $lengths = array_map(function ($value) { - return (int) $value; - }, explode(',', $info['length'])); + $lengths = explode(',', $info['length']); - if (! in_array(strlen($ccNumber), $lengths, true)) + if (! in_array((string) strlen($ccNumber), $lengths, true)) { return false; } From 905bfe8c9117ce9a55fb27a35eb9ea616e272fd6 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 30 Aug 2020 07:54:12 +0700 Subject: [PATCH 052/328] floating to 90.0, 180.0, 270.0 --- system/Images/Handlers/BaseHandler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 2cb60679afdf..2928c2d2bb52 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -361,9 +361,9 @@ public function rotate(float $angle) { // Allowed rotation values $degs = [ - (float) 90, - (float) 180, - (float) 270, + 90.0, + 180.0, + 270.0, ]; if (! in_array($angle, $degs, true)) From 956584ceb43b2aa88d7602f1246fa18dbf5ce56b Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 30 Aug 2020 07:56:23 +0700 Subject: [PATCH 053/328] Fixes CodeIgniterTest::testTransfersCorrectHTTPVersion(), use 2.0 instead of 2 for server protocol --- tests/system/CodeIgniterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index baacda948642..7063e863dddd 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -230,7 +230,7 @@ public function testTransfersCorrectHTTPVersion() '/', ]; $_SERVER['argc'] = 2; - $_SERVER['SERVER_PROTOCOL'] = 'HTTP/2'; + $_SERVER['SERVER_PROTOCOL'] = 'HTTP/2.0'; ob_start(); $this->codeigniter->useSafeOutput(true)->run(); From 5c9f29b8d5932cd09f1f320c29ad93dcb0dcb594 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 30 Aug 2020 20:49:06 +0800 Subject: [PATCH 054/328] Update BaseHandler::getEXIF --- system/Images/Handlers/BaseHandler.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 2928c2d2bb52..7191cd1ad25b 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -597,6 +597,8 @@ public function reorient(bool $silent = false) * @param string|null $key If specified, will only return this piece of EXIF data. * @param boolean $silent If true, will not throw our own exceptions. * + * @throws \CodeIgniter\Images\Exceptions\ImageException + * * @return mixed */ public function getEXIF(string $key = null, bool $silent = false) @@ -607,6 +609,8 @@ public function getEXIF(string $key = null, bool $silent = false) { return null; } + + throw ImageException::forEXIFUnsupported(); // @codeCoverageIgnore } $exif = null; // default @@ -614,7 +618,7 @@ public function getEXIF(string $key = null, bool $silent = false) { case IMAGETYPE_JPEG: case IMAGETYPE_TIFF_II: - $exif = exif_read_data($this->image()->getPathname()); + $exif = @exif_read_data($this->image()->getPathname()); if (! is_null($key) && is_array($exif)) { $exif = $exif[$key] ?? false; From 80edcb73118a4969730b320eee5cabeb861352e5 Mon Sep 17 00:00:00 2001 From: Mostafa Khudair <59371810+mostafakhudair@users.noreply.github.com> Date: Sat, 11 Jul 2020 22:57:27 +0200 Subject: [PATCH 055/328] Update RedirectResponse.php Remove unuseful method and clean some lines --- system/HTTP/RedirectResponse.php | 70 ++++++++++++++------------------ 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/system/HTTP/RedirectResponse.php b/system/HTTP/RedirectResponse.php index 7c1629bd52e9..49c22f7657e6 100644 --- a/system/HTTP/RedirectResponse.php +++ b/system/HTTP/RedirectResponse.php @@ -51,25 +51,25 @@ class RedirectResponse extends Response * Sets the URI to redirect to and, optionally, the HTTP status code to use. * If no code is provided it will be automatically determined. * - * @param string $uri The URI to redirect to - * @param integer|null $code HTTP status code + * @param string $uri + * @param integer|null $code * @param string $method * * @return $this */ public function to(string $uri, int $code = null, string $method = 'auto') { - // If it appears to be a relative URL, then convert to full URL - // for better security. + // If its a relative URL, convert to full URL for a better security. if (strpos($uri, 'http') !== 0) { - $url = current_url(true)->resolveRelativeURI($uri); - $uri = (string)$url; + $uri = current_url(true)->resolveRelativeURI($uri); } return $this->redirect($uri, $method, $code); } + //-------------------------------------------------------------------- + /** * Sets the URI to redirect to but as a reverse-routed or named route * instead of a raw URI. @@ -83,9 +83,7 @@ public function to(string $uri, int $code = null, string $method = 'auto') */ public function route(string $route, array $params = [], int $code = 302, string $method = 'auto') { - $routes = Services::routes(true); - - $route = $routes->reverseRoute($route, ...$params); + $route = Services::routes()->reverseRoute($route, ...$params); if (! $route) { @@ -95,6 +93,8 @@ public function route(string $route, array $params = [], int $code = 302, string return $this->redirect(site_url($route), $method, $code); } + //-------------------------------------------------------------------- + /** * Helper function to return to previous page. * @@ -108,41 +108,41 @@ public function route(string $route, array $params = [], int $code = 302, string */ public function back(int $code = null, string $method = 'auto') { - $this->ensureSession(); - return $this->redirect(previous_url(), $method, $code); } + //-------------------------------------------------------------------- + /** * Specifies that the current $_GET and $_POST arrays should be - * packaged up with the response. It will then be available via - * the 'old()' helper function. + * packaged up with the response. + * It will then be available via the 'old()' helper function. * * @return $this */ public function withInput() { - $session = $this->ensureSession(); + $session = Services::session(); - $input = [ + $session->setFlashdata('_ci_old_input', [ 'get' => $_GET ?? [], - 'post' => $_POST ?? [], - ]; - - $session->setFlashdata('_ci_old_input', $input); + 'post' => $_POST ?? [] + ]); // If the validator has any errors, transmit those back - // so they can be displayed when the validation is - // handled within a method different than displaying the form. - $validator = Services::validation(); - if (! empty($validator->getErrors())) + // so they can be displayed when the validation is handled + // within a method different than displaying the form. + $validation = Services::validation(); + if (! empty($validation->getErrors())) { - $session->setFlashdata('_ci_validation_errors', serialize($validator->getErrors())); + $session->setFlashdata('_ci_validation_errors', serialize($validation->getErrors())); } return $this; } + //-------------------------------------------------------------------- + /** * Adds a key and message to the session as Flashdata. * @@ -153,13 +153,13 @@ public function withInput() */ public function with(string $key, $message) { - $session = $this->ensureSession(); - - $session->setFlashdata($key, $message); + Services::session()->setFlashdata($key, $message); return $this; } + //-------------------------------------------------------------------- + /** * Copies any cookies from the global Response instance * into this RedirectResponse. Useful when you've just @@ -170,7 +170,7 @@ public function with(string $key, $message) */ public function withCookies() { - $cookies = service('response')->getCookies(); + $cookies = Services::response()->getCookies(); if (empty($cookies)) { @@ -185,6 +185,8 @@ public function withCookies() return $this; } + //-------------------------------------------------------------------- + /** * Copies any headers from the global Response instance * into this RedirectResponse. Useful when you've just @@ -195,7 +197,7 @@ public function withCookies() */ public function withHeaders() { - $headers = service('response')->getHeaders(); + $headers = Services::response()->getHeaders(); if (empty($headers)) { @@ -209,14 +211,4 @@ public function withHeaders() return $this; } - - /** - * Ensures the session is loaded and started. - * - * @return \CodeIgniter\Session\Session - */ - protected function ensureSession() - { - return Services::session(); - } } From 2e600e1344f352f8c0487e7b23f6784a01e4954d Mon Sep 17 00:00:00 2001 From: Mostafa Khudair <59371810+mostafakhudair@users.noreply.github.com> Date: Sun, 12 Jul 2020 20:03:22 +0200 Subject: [PATCH 056/328] Update RedirectResponse.php --- system/HTTP/RedirectResponse.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/HTTP/RedirectResponse.php b/system/HTTP/RedirectResponse.php index 49c22f7657e6..4dc0a00da51a 100644 --- a/system/HTTP/RedirectResponse.php +++ b/system/HTTP/RedirectResponse.php @@ -62,7 +62,7 @@ public function to(string $uri, int $code = null, string $method = 'auto') // If its a relative URL, convert to full URL for a better security. if (strpos($uri, 'http') !== 0) { - $uri = current_url(true)->resolveRelativeURI($uri); + $uri = (string) current_url(true)->resolveRelativeURI($uri); } return $this->redirect($uri, $method, $code); @@ -108,6 +108,8 @@ public function route(string $route, array $params = [], int $code = 302, string */ public function back(int $code = null, string $method = 'auto') { + Services::session(); + return $this->redirect(previous_url(), $method, $code); } From 22a8dd5ffd2d7e35a5e61fc58bea9980a84e6d11 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 30 Aug 2020 19:50:49 +0800 Subject: [PATCH 057/328] Refactor RedirectResponse --- system/HTTP/RedirectResponse.php | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/system/HTTP/RedirectResponse.php b/system/HTTP/RedirectResponse.php index 4dc0a00da51a..1b0fdaca554b 100644 --- a/system/HTTP/RedirectResponse.php +++ b/system/HTTP/RedirectResponse.php @@ -51,15 +51,16 @@ class RedirectResponse extends Response * Sets the URI to redirect to and, optionally, the HTTP status code to use. * If no code is provided it will be automatically determined. * - * @param string $uri - * @param integer|null $code + * @param string $uri The URI to redirect to + * @param integer|null $code HTTP status code * @param string $method * * @return $this */ public function to(string $uri, int $code = null, string $method = 'auto') { - // If its a relative URL, convert to full URL for a better security. + // If it appears to be a relative URL, then convert to full URL + // for better security. if (strpos($uri, 'http') !== 0) { $uri = (string) current_url(true)->resolveRelativeURI($uri); @@ -79,6 +80,8 @@ public function to(string $uri, int $code = null, string $method = 'auto') * @param integer $code * @param string $method * + * @throws \CodeIgniter\HTTP\Exceptions\HTTPException + * * @return $this */ public function route(string $route, array $params = [], int $code = 302, string $method = 'auto') @@ -109,7 +112,7 @@ public function route(string $route, array $params = [], int $code = 302, string public function back(int $code = null, string $method = 'auto') { Services::session(); - + return $this->redirect(previous_url(), $method, $code); } @@ -118,6 +121,7 @@ public function back(int $code = null, string $method = 'auto') /** * Specifies that the current $_GET and $_POST arrays should be * packaged up with the response. + * * It will then be available via the 'old()' helper function. * * @return $this @@ -128,14 +132,15 @@ public function withInput() $session->setFlashdata('_ci_old_input', [ 'get' => $_GET ?? [], - 'post' => $_POST ?? [] + 'post' => $_POST ?? [], ]); - // If the validator has any errors, transmit those back + // If the validation has any errors, transmit those back // so they can be displayed when the validation is handled // within a method different than displaying the form. $validation = Services::validation(); - if (! empty($validation->getErrors())) + + if ($validation->getErrors()) { $session->setFlashdata('_ci_validation_errors', serialize($validation->getErrors())); } @@ -174,11 +179,6 @@ public function withCookies() { $cookies = Services::response()->getCookies(); - if (empty($cookies)) - { - return $this; - } - foreach ($cookies as $cookie) { $this->cookies[] = $cookie; @@ -201,11 +201,6 @@ public function withHeaders() { $headers = Services::response()->getHeaders(); - if (empty($headers)) - { - return $this; - } - foreach ($headers as $name => $header) { $this->setHeader($name, $header->getValue()); From 27c948de9e89600884c26f99d856e0d6c9ba541e Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 30 Aug 2020 20:56:48 +0800 Subject: [PATCH 058/328] Remove unneeded lines --- system/HTTP/RedirectResponse.php | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/system/HTTP/RedirectResponse.php b/system/HTTP/RedirectResponse.php index 1b0fdaca554b..6bf9031c34ee 100644 --- a/system/HTTP/RedirectResponse.php +++ b/system/HTTP/RedirectResponse.php @@ -69,8 +69,6 @@ public function to(string $uri, int $code = null, string $method = 'auto') return $this->redirect($uri, $method, $code); } - //-------------------------------------------------------------------- - /** * Sets the URI to redirect to but as a reverse-routed or named route * instead of a raw URI. @@ -96,8 +94,6 @@ public function route(string $route, array $params = [], int $code = 302, string return $this->redirect(site_url($route), $method, $code); } - //-------------------------------------------------------------------- - /** * Helper function to return to previous page. * @@ -116,8 +112,6 @@ public function back(int $code = null, string $method = 'auto') return $this->redirect(previous_url(), $method, $code); } - //-------------------------------------------------------------------- - /** * Specifies that the current $_GET and $_POST arrays should be * packaged up with the response. @@ -148,8 +142,6 @@ public function withInput() return $this; } - //-------------------------------------------------------------------- - /** * Adds a key and message to the session as Flashdata. * @@ -165,8 +157,6 @@ public function with(string $key, $message) return $this; } - //-------------------------------------------------------------------- - /** * Copies any cookies from the global Response instance * into this RedirectResponse. Useful when you've just @@ -177,9 +167,7 @@ public function with(string $key, $message) */ public function withCookies() { - $cookies = Services::response()->getCookies(); - - foreach ($cookies as $cookie) + foreach (Services::response()->getCookies() as $cookie) { $this->cookies[] = $cookie; } @@ -187,8 +175,6 @@ public function withCookies() return $this; } - //-------------------------------------------------------------------- - /** * Copies any headers from the global Response instance * into this RedirectResponse. Useful when you've just @@ -199,9 +185,7 @@ public function withCookies() */ public function withHeaders() { - $headers = Services::response()->getHeaders(); - - foreach ($headers as $name => $header) + foreach (Services::response()->getHeaders() as $name => $header) { $this->setHeader($name, $header->getValue()); } From 850462d5ea14dfe9e1ca3ac4f36335aeb5a0760a Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 30 Aug 2020 21:38:35 +0800 Subject: [PATCH 059/328] Implement caching of phpstan result cache --- .github/workflows/test-phpstan.yml | 10 ++++++++++ phpstan.neon | 1 + 2 files changed, 11 insertions(+) diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml index 45bb5ff395a2..314b872c2217 100644 --- a/.github/workflows/test-phpstan.yml +++ b/.github/workflows/test-phpstan.yml @@ -35,5 +35,15 @@ jobs: - name: Install dependencies run: composer install --no-progress --no-suggest --no-interaction + - name: Create PHPStan result cache directory + run: mkdir -p build/phpstan + + - name: Cache PHPStan result cache directory + uses: actions/cache@v2 + with: + path: build/phpstan + key: ${{ runner.os }}-phpstan-${{ github.sha }} + restore-keys: ${{ runner.os }}-phpstan- + - name: Run static analysis run: vendor/bin/phpstan analyse diff --git a/phpstan.neon b/phpstan.neon index 33525595ee64..4cb72e5fa010 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,4 +1,5 @@ parameters: + tmpDir: build/phpstan level: 5 paths: - app From 28d760453ef13398df35b584cf2d28cd9f2d2ea1 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 31 Aug 2020 00:44:02 +0700 Subject: [PATCH 060/328] remove get_class() on method_exists --- system/Config/BaseService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index 98fab42db0e5..34c9a3bb35d0 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -286,7 +286,7 @@ protected static function discoverServices(string $name, array $arguments) // Try to find the desired service method foreach (static::$services as $class) { - if (method_exists(get_class($class), $name)) + if (method_exists($class, $name)) { return $class::$name(...$arguments); } From 9143f7b834fb178b346e434fe6075feb1ad803a0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 30 Aug 2020 22:17:16 +0700 Subject: [PATCH 061/328] Allow string for parameter $obj in ReflectionHelper::getAccessibleRefProperty() --- system/Test/ReflectionHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Test/ReflectionHelper.php b/system/Test/ReflectionHelper.php index c6c1005feb65..bb52908603b1 100644 --- a/system/Test/ReflectionHelper.php +++ b/system/Test/ReflectionHelper.php @@ -72,8 +72,8 @@ public static function getPrivateMethodInvoker($obj, $method) /** * Find an accessible property. * - * @param object $obj - * @param string $property + * @param object|string $obj + * @param string $property * * @return \ReflectionProperty * @throws \ReflectionException From 7f819e32d51c2b4ce745f06def3ec3cdb78b2f05 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 30 Aug 2020 22:40:40 +0800 Subject: [PATCH 062/328] Use null coalescing to routes resolution --- system/Test/FeatureTestTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Test/FeatureTestTrait.php b/system/Test/FeatureTestTrait.php index 04011ab7100a..d5dfe6182ae3 100644 --- a/system/Test/FeatureTestTrait.php +++ b/system/Test/FeatureTestTrait.php @@ -167,7 +167,7 @@ public function call(string $method, string $path, array $params = null) $request = $this->populateGlobals($method, $request, $params); // Make sure the RouteCollection knows what method we're using... - $routes = $this->routes ?: Services::routes(); // @phpstan-ignore-line + $routes = $this->routes ?? Services::routes(); $routes->setHTTPVerb($method); // Make sure any other classes that might call the request From 0432983b72cd7e6b90758d9e632f41244f31624b Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 30 Aug 2020 23:21:06 +0800 Subject: [PATCH 063/328] Add test demonstrating usability in CIUnitTestCase --- tests/system/HomeTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/system/HomeTest.php diff --git a/tests/system/HomeTest.php b/tests/system/HomeTest.php new file mode 100644 index 000000000000..bd24d3ad99b7 --- /dev/null +++ b/tests/system/HomeTest.php @@ -0,0 +1,17 @@ +get('/'); + $this->assertTrue($response->isOK()); + } +} From 0c5d363211b1d651f5cb1f6e60e75c7d01958aa4 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 31 Aug 2020 22:55:43 +0800 Subject: [PATCH 064/328] Fix test for CI --- tests/system/HomeTest.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/system/HomeTest.php b/tests/system/HomeTest.php index bd24d3ad99b7..793688309886 100644 --- a/tests/system/HomeTest.php +++ b/tests/system/HomeTest.php @@ -11,7 +11,16 @@ class HomeTest extends CIUnitTestCase public function testPageLoadsSuccessfully() { - $response = $this->get('/'); + $this->withRoutes([ + [ + 'get', + 'home', + '\App\Controllers\Home::index', + ], + ]); + + $response = $this->get('home'); + $this->assertInstanceOf('CodeIgniter\Test\FeatureResponse', $response); $this->assertTrue($response->isOK()); } } From cd66a704a90b879b199e4d926968e6fdb3c215f1 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 29 Aug 2020 20:50:35 +0800 Subject: [PATCH 065/328] Refactor Format class --- app/Config/Format.php | 98 ++++++++--------- system/API/ResponseTrait.php | 20 ++-- system/Config/Services.php | 24 ++++ system/Format/Exceptions/FormatException.php | 86 ++++++++++++++- system/Format/Format.php | 109 +++++++++++++++++++ system/HTTP/Response.php | 32 ++---- system/Language/en/Format.php | 2 + system/Test/FeatureResponse.php | 8 +- 8 files changed, 283 insertions(+), 96 deletions(-) create mode 100644 system/Format/Format.php diff --git a/app/Config/Format.php b/app/Config/Format.php index b79faa28192e..8de909ec9a9f 100644 --- a/app/Config/Format.php +++ b/app/Config/Format.php @@ -1,54 +1,59 @@ - + */ public $formatters = [ - 'application/json' => \CodeIgniter\Format\JSONFormatter::class, - 'application/xml' => \CodeIgniter\Format\XMLFormatter::class, - 'text/xml' => \CodeIgniter\Format\XMLFormatter::class, + 'application/json' => 'CodeIgniter\Format\JSONFormatter', + 'application/xml' => 'CodeIgniter\Format\XMLFormatter', + 'text/xml' => 'CodeIgniter\Format\XMLFormatter', ]; - /* - |-------------------------------------------------------------------------- - | Formatters Options - |-------------------------------------------------------------------------- - | - | Additional Options to adjust default formatters behaviour. - | For each mime type, list the additional options that should be used. - | - */ + /** + * -------------------------------------------------------------------------- + * Formatters Options + * -------------------------------------------------------------------------- + * + * Additional Options to adjust default formatters behaviour. + * For each mime type, list the additional options that should be used. + * + * @var array + */ public $formatterOptions = [ 'application/json' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES, 'application/xml' => 0, @@ -63,24 +68,11 @@ class Format extends BaseConfig * @param string $mime * * @return \CodeIgniter\Format\FormatterInterface + * + * @deprecated This is an alias of `\CodeIgniter\Format\Format::getFormatter`. Use that instead. */ public function getFormatter(string $mime) { - if (! array_key_exists($mime, $this->formatters)) - { - throw new \InvalidArgumentException('No Formatter defined for mime type: ' . $mime); - } - - $class = $this->formatters[$mime]; - - if (! class_exists($class)) - { - throw new \BadMethodCallException($class . ' is not a valid Formatter.'); - } - - return new $class(); + return Services::format()->getFormatter($mime); } - - //-------------------------------------------------------------------- - } diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 7091dbe5f6c0..12ff0d172172 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -40,7 +40,7 @@ namespace CodeIgniter\API; use CodeIgniter\HTTP\Response; -use Config\Format; +use Config\Services; /** * Response trait. @@ -123,7 +123,8 @@ public function respond($data = null, int $status = null, string $message = '') // Create the output var here in case of $this->response([]); $output = null; - } // If data is null but status provided, keep the output empty. + } + // If data is null but status provided, keep the output empty. elseif ($data === null && is_numeric($status)) { $output = null; @@ -134,8 +135,7 @@ public function respond($data = null, int $status = null, string $message = '') $output = $this->format($data); } - return $this->response->setBody($output) - ->setStatusCode($status, $message); + return $this->response->setBody($output)->setStatusCode($status, $message); } //-------------------------------------------------------------------- @@ -386,25 +386,25 @@ protected function format($data = null) return $data; } - $config = new Format(); - $format = "application/$this->format"; + $format = Services::format(); + $mime = "application/{$this->format}"; // Determine correct response type through content negotiation if not explicitly declared if (empty($this->format) || ! in_array($this->format, ['json', 'xml'], true)) { - $format = $this->request->negotiate('media', $config->supportedResponseFormats, false); + $mime = $this->request->negotiate('media', $format->getConfig()->supportedResponseFormats, false); } - $this->response->setContentType($format); + $this->response->setContentType($mime); // if we don't have a formatter, make one if (! isset($this->formatter)) { // if no formatter, use the default - $this->formatter = $config->getFormatter($format); // @phpstan-ignore-line + $this->formatter = $format->getFormatter($mime); } - if ($format !== 'application/json') + if ($mime !== 'application/json') { // Recursively convert objects into associative arrays // Conversion not required for JSONFormatter diff --git a/system/Config/Services.php b/system/Config/Services.php index 615739d68bc9..8da343c7edfd 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -51,6 +51,7 @@ use CodeIgniter\Encryption\EncrypterInterface; use CodeIgniter\Encryption\Encryption; use CodeIgniter\Filters\Filters; +use CodeIgniter\Format\Format; use CodeIgniter\Honeypot\Honeypot; use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\CURLRequest; @@ -83,6 +84,7 @@ use Config\Email as EmailConfig; use Config\Encryption as EncryptionConfig; use Config\Exceptions as ExceptionsConfig; +use Config\Format as FormatConfig; use Config\Filters as FiltersConfig; use Config\Honeypot as HoneypotConfig; use Config\Images; @@ -311,6 +313,28 @@ public static function filters(FiltersConfig $config = null, bool $getShared = t //-------------------------------------------------------------------- + /** + * The Format class is a convenient place to create Formatters. + * + * @param \Config\Format|null $config + * @param boolean $getShared + * + * @return \CodeIgniter\Format\Format + */ + public static function format(FormatConfig $config = null, bool $getShared = true) + { + if ($getShared) + { + return static::getSharedInstance('format', $config); + } + + $config = $config ?? config('Format'); + + return new Format($config); + } + + //-------------------------------------------------------------------- + /** * The Honeypot provides a secret input on forms that bots should NOT * fill in, providing an additional safeguard when accepting user input. diff --git a/system/Format/Exceptions/FormatException.php b/system/Format/Exceptions/FormatException.php index 4529fd0fafa0..abaf3d1456a6 100644 --- a/system/Format/Exceptions/FormatException.php +++ b/system/Format/Exceptions/FormatException.php @@ -1,16 +1,95 @@ -config = $config; + } + + /** + * Returns the current configuration instance. + * + * @return \Config\Format + */ + public function getConfig() + { + return $this->config; + } + + /** + * A Factory method to return the appropriate formatter for the given mime type. + * + * @param string $mime + * + * @throws \CodeIgniter\Format\FormatException + * + * @return \CodeIgniter\Format\FormatterInterface + */ + public function getFormatter(string $mime): FormatterInterface + { + if (! array_key_exists($mime, $this->config->formatters)) + { + throw FormatException::forInvalidMime($mime); + } + + $className = $this->config->formatters[$mime]; + + if (! class_exists($className)) + { + throw FormatException::forInvalidFormatter($className); + } + + $class = new $className(); + + if (! $class instanceof FormatterInterface) + { + throw FormatException::forInvalidFormatter($className); + } + + return $class; + } +} diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index 383649f8b5ea..05e918a11a2d 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -41,7 +41,9 @@ use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\Pager\PagerInterface; -use Config\Format; +use Config\Services; +use DateTime; +use DateTimeZone; /** * Representation of an outgoing, getServer-side response. @@ -376,9 +378,9 @@ public function getReason(): string * * @return Response */ - public function setDate(\DateTime $date) + public function setDate(DateTime $date) { - $date->setTimezone(new \DateTimeZone('UTC')); + $date->setTimezone(new DateTimeZone('UTC')); $this->setHeader('Date', $date->format('D, d M Y H:i:s') . ' GMT'); @@ -479,13 +481,7 @@ public function getJSON() if ($this->bodyFormat !== 'json') { - /** - * @var Format $config - */ - $config = config(Format::class); - $formatter = $config->getFormatter('application/json'); - - $body = $formatter->format($body); + $body = Services::format()->getFormatter('application/json')->format($body); } return $body ?: null; @@ -521,13 +517,7 @@ public function getXML() if ($this->bodyFormat !== 'xml') { - /** - * @var Format $config - */ - $config = config(Format::class); - $formatter = $config->getFormatter('application/xml'); - - $body = $formatter->format($body); + $body = Services::format()->getFormatter('application/xml')->format($body); } return $body; @@ -554,13 +544,7 @@ protected function formatBody($body, string $format) // Nothing much to do for a string... if (! is_string($body) || $format === 'json-unencoded') { - /** - * @var Format $config - */ - $config = config(Format::class); - $formatter = $config->getFormatter($mime); - - $body = $formatter->format($body); + $body = Services::format()->getFormatter($mime)->format($body); } return $body; diff --git a/system/Language/en/Format.php b/system/Language/en/Format.php index c0070c8534b5..e1a3406c994e 100644 --- a/system/Language/en/Format.php +++ b/system/Language/en/Format.php @@ -15,6 +15,8 @@ */ return [ + 'invalidFormatter' => '"{0}" is not a valid Formatter class.', 'invalidJSON' => 'Failed to parse json string, error: "{0}".', + 'invalidMime' => 'No Formatter defined for mime type: "{0}".', 'missingExtension' => 'The SimpleXML extension is required to format XML.', ]; diff --git a/system/Test/FeatureResponse.php b/system/Test/FeatureResponse.php index a99a9dd5dd73..88414ecce1cf 100644 --- a/system/Test/FeatureResponse.php +++ b/system/Test/FeatureResponse.php @@ -1,4 +1,5 @@ getFormatter('application/json'); - $test = $formatter->format($test); + $test = Services::format()->getFormatter('application/json')->format($test); } $this->assertJsonStringEqualsJsonString($test, $json, 'Response does not contain matching JSON.'); From e1cc63e396dcd22e9a099822d21f1cffc7965f0e Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 29 Aug 2020 20:53:14 +0800 Subject: [PATCH 066/328] Add and modify tests --- tests/system/Config/ServicesTest.php | 15 +++++- tests/system/Format/FormatTest.php | 64 +++++++++++++++++++++++ tests/system/HTTP/ResponseTest.php | 45 +++++++--------- tests/system/Test/FeatureResponseTest.php | 52 +++++++----------- 4 files changed, 114 insertions(+), 62 deletions(-) create mode 100644 tests/system/Format/FormatTest.php diff --git a/tests/system/Config/ServicesTest.php b/tests/system/Config/ServicesTest.php index c8503ec2ff5e..472f7ed3a208 100644 --- a/tests/system/Config/ServicesTest.php +++ b/tests/system/Config/ServicesTest.php @@ -1,9 +1,12 @@ assertInstanceOf(\CodeIgniter\Filters\Filters::class, $result); } + public function testFormat() + { + $this->assertInstanceOf(Format::class, Services::format()); + } + + public function testUnsharedFormat() + { + $this->assertInstanceOf(Format::class, Services::format(null, false)); + } + public function testHoneypot() { $result = Services::honeypot(); diff --git a/tests/system/Format/FormatTest.php b/tests/system/Format/FormatTest.php new file mode 100644 index 000000000000..da46ebc5e771 --- /dev/null +++ b/tests/system/Format/FormatTest.php @@ -0,0 +1,64 @@ +format = new Format(new \Config\Format()); + } + + public function testFormatConfigType() + { + $config = new \Config\Format(); + $format = new Format($config); + + $this->assertInstanceOf('Config\Format', $format->getConfig()); + $this->assertSame($config, $format->getConfig()); + } + + public function testGetFormatter() + { + $this->assertInstanceof(FormatterInterface::class, $this->format->getFormatter('application/json')); + } + + public function testGetFormatterExpectsExceptionOnUndefinedMime() + { + $this->expectException(FormatException::class); + $this->expectExceptionMessage('No Formatter defined for mime type: "application/x-httpd-php".'); + $this->format->getFormatter('application/x-httpd-php'); + } + + public function testGetFormatterExpectsExceptionOnUndefinedClass() + { + $this->format->getConfig()->formatters = array_merge( + $this->format->getConfig()->formatters, + ['text/xml' => 'App\Foo'] + ); + + $this->expectException(FormatException::class); + $this->expectExceptionMessage('"App\Foo" is not a valid Formatter class.'); + $this->format->getFormatter('text/xml'); + } + + public function testGetFormatterExpectsExceptionOnClassNotImplementingFormatterInterface() + { + $this->format->getConfig()->formatters = array_merge( + $this->format->getConfig()->formatters, + ['text/xml' => 'CodeIgniter\HTTP\URI'] + ); + + $this->expectException(FormatException::class); + $this->expectExceptionMessage('"CodeIgniter\HTTP\URI" is not a valid Formatter class.'); + $this->format->getFormatter('text/xml'); + } +} diff --git a/tests/system/HTTP/ResponseTest.php b/tests/system/HTTP/ResponseTest.php index 0d2d8ed5bfc6..ea68efae70aa 100644 --- a/tests/system/HTTP/ResponseTest.php +++ b/tests/system/HTTP/ResponseTest.php @@ -1,11 +1,12 @@ setLink($pager); $this->assertEquals( - '; rel="first",; rel="prev",; rel="next",; rel="last"', $response->getHeader('Link')->getValue() + '; rel="first",; rel="prev",; rel="next",; rel="last"', + $response->getHeader('Link')->getValue() ); $pager->store('default', 1, 10, 200); $response->setLink($pager); $this->assertEquals( - '; rel="next",; rel="last"', $response->getHeader('Link')->getValue() + '; rel="next",; rel="last"', + $response->getHeader('Link')->getValue() ); $pager->store('default', 20, 10, 200); $response->setLink($pager); $this->assertEquals( - '; rel="first",; rel="prev"', $response->getHeader('Link')->getValue() + '; rel="first",; rel="prev"', + $response->getHeader('Link')->getValue() ); } @@ -340,10 +344,6 @@ public function testSetCookieSuccessOnPrefix() public function testJSONWithArray() { - $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/json'); - $body = [ 'foo' => 'bar', 'bar' => [ @@ -352,8 +352,9 @@ public function testJSONWithArray() 3, ], ]; - $expected = $formatter->format($body); + $expected = Services::format()->getFormatter('application/json')->format($body); + $response = new Response(new App()); $response->setJSON($body); $this->assertEquals($expected, $response->getJSON()); @@ -362,10 +363,6 @@ public function testJSONWithArray() public function testJSONGetFromNormalBody() { - $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/json'); - $body = [ 'foo' => 'bar', 'bar' => [ @@ -374,8 +371,9 @@ public function testJSONGetFromNormalBody() 3, ], ]; - $expected = $formatter->format($body); + $expected = Services::format()->getFormatter('application/json')->format($body); + $response = new Response(new App()); $response->setBody($body); $this->assertEquals($expected, $response->getJSON()); @@ -385,10 +383,6 @@ public function testJSONGetFromNormalBody() public function testXMLWithArray() { - $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/xml'); - $body = [ 'foo' => 'bar', 'bar' => [ @@ -397,8 +391,9 @@ public function testXMLWithArray() 3, ], ]; - $expected = $formatter->format($body); + $expected = Services::format()->getFormatter('application/xml')->format($body); + $response = new Response(new App()); $response->setXML($body); $this->assertEquals($expected, $response->getXML()); @@ -407,10 +402,6 @@ public function testXMLWithArray() public function testXMLGetFromNormalBody() { - $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/xml'); - $body = [ 'foo' => 'bar', 'bar' => [ @@ -419,8 +410,9 @@ public function testXMLGetFromNormalBody() 3, ], ]; - $expected = $formatter->format($body); + $expected = Services::format()->getFormatter('application/xml')->format($body); + $response = new Response(new App()); $response->setBody($body); $this->assertEquals($expected, $response->getXML()); @@ -520,6 +512,7 @@ public function testTemporaryRedirectGet11() } //-------------------------------------------------------------------- + // Make sure cookies are set by RedirectResponse this way // See https://github.com/codeigniter4/CodeIgniter4/issues/1393 public function testRedirectResponseCookies() @@ -536,6 +529,7 @@ public function testRedirectResponseCookies() } //-------------------------------------------------------------------- + // Make sure we don't blow up if pretending to send headers public function testPretendOutput() { @@ -559,8 +553,5 @@ public function testInvalidSameSiteCookie() $this->expectException(HTTPException::class); $this->expectExceptionMessage(lang('HTTP.invalidSameSiteSetting', ['Invalid'])); - - $response = new Response($config); } - } diff --git a/tests/system/Test/FeatureResponseTest.php b/tests/system/Test/FeatureResponseTest.php index 75e6aa9ae66a..35e56548baa0 100644 --- a/tests/system/Test/FeatureResponseTest.php +++ b/tests/system/Test/FeatureResponseTest.php @@ -2,11 +2,13 @@ use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\Response; +use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\FeatureResponse; +use Config\App; +use Config\Services; -class FeatureResponseTest extends \CodeIgniter\Test\CIUnitTestCase +class FeatureResponseTest extends CIUnitTestCase { - /** * @var FeatureResponse */ @@ -115,7 +117,7 @@ public function testAssertRedirectFail() public function testAssertRedirectSuccess() { $this->getFeatureResponse('

Hello World

'); - $this->feature->response = new RedirectResponse(new Config\App()); + $this->feature->response = new RedirectResponse(new App()); $this->assertTrue($this->feature->response instanceof RedirectResponse); $this->assertTrue($this->feature->isRedirect()); @@ -125,7 +127,7 @@ public function testAssertRedirectSuccess() public function testGetRedirectUrlReturnsUrl() { $this->getFeatureResponse('

Hello World

'); - $this->feature->response = new RedirectResponse(new Config\App()); + $this->feature->response = new RedirectResponse(new App()); $this->feature->response->redirect('foo/bar'); $this->assertEquals('foo/bar', $this->feature->getRedirectUrl()); @@ -221,8 +223,7 @@ public function testAssertCookieExpired() public function testGetJSON() { $this->getFeatureResponse(['foo' => 'bar']); - $config = new \Config\Format(); - $formatter = $config->getFormatter('application/json'); + $formatter = Services::format()->getFormatter('application/json'); $this->assertEquals($formatter->format(['foo' => 'bar']), $this->feature->getJSON()); } @@ -231,8 +232,6 @@ public function testEmptyJSON() { $this->getFeatureResponse('

Hello World

'); $this->response->setJSON('', true); - $config = new \Config\Format(); - $formatter = $config->getFormatter('application/json'); // this should be "" - json_encode(''); $this->assertEquals('""', $this->feature->getJSON()); @@ -242,8 +241,6 @@ public function testFalseJSON() { $this->getFeatureResponse('

Hello World

'); $this->response->setJSON(false, true); - $config = new \Config\Format(); - $formatter = $config->getFormatter('application/json'); // this should be FALSE - json_encode(false) $this->assertEquals('false', $this->feature->getJSON()); @@ -253,8 +250,6 @@ public function testTrueJSON() { $this->getFeatureResponse('

Hello World

'); $this->response->setJSON(true, true); - $config = new \Config\Format(); - $formatter = $config->getFormatter('application/json'); // this should be TRUE - json_encode(true) $this->assertEquals('true', $this->feature->getJSON()); @@ -273,8 +268,7 @@ public function testInvalidJSON() public function testGetXML() { $this->getFeatureResponse(['foo' => 'bar']); - $config = new \Config\Format(); - $formatter = $config->getFormatter('application/xml'); + $formatter = Services::format()->getFormatter('application/xml'); $this->assertEquals($formatter->format(['foo' => 'bar']), $this->feature->getXML()); } @@ -315,41 +309,31 @@ public function testJsonExactString() ]; $this->getFeatureResponse($data); + $formatter = Services::format()->getFormatter('application/json'); - $config = new \Config\Format(); - $formatter = $config->getFormatter('application/json'); - $expected = $formatter->format($data); - - $this->feature->assertJSONExact($expected); + $this->feature->assertJSONExact($formatter->format($data)); } protected function getFeatureResponse($body = null, array $responseOptions = [], array $headers = []) { - $this->response = new Response(new \Config\App()); + $this->response = new Response(new App()); $this->response->setBody($body); - if (count($responseOptions)) + foreach ($responseOptions as $key => $value) { - foreach ($responseOptions as $key => $value) - { - $method = 'set' . ucfirst($key); + $method = 'set' . ucfirst($key); - if (method_exists($this->response, $method)) - { - $this->response = $this->response->$method($value); - } + if (method_exists($this->response, $method)) + { + $this->response = $this->response->$method($value); } } - if (count($headers)) + foreach ($headers as $key => $value) { - foreach ($headers as $key => $value) - { - $this->response = $this->response->setHeader($key, $value); - } + $this->response = $this->response->setHeader($key, $value); } $this->feature = new FeatureResponse($this->response); } - } From a05616a9bc4c829787ebba265c4d003e53403037 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 29 Aug 2020 21:30:00 +0800 Subject: [PATCH 067/328] Fix phpstan related errors --- system/API/ResponseTrait.php | 9 ++++++++- system/Format/Format.php | 2 -- system/RESTful/ResourceController.php | 9 +++++---- system/Test/Mock/MockResourcePresenter.php | 8 +++++--- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 12ff0d172172..daee90ca45fd 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -102,6 +102,13 @@ trait ResponseTrait */ protected $format = 'json'; + /** + * Current Formatter instance. This is usually set by ResponseTrait::format + * + * @var \CodeIgniter\Format\FormatterInterface + */ + protected $formatter; + //-------------------------------------------------------------------- /** @@ -159,7 +166,7 @@ public function fail($messages, int $status = 400, string $code = null, string $ $response = [ 'status' => $status, - 'error' => $code === null ? $status : $code, + 'error' => $code ?? $status, 'messages' => $messages, ]; diff --git a/system/Format/Format.php b/system/Format/Format.php index 4504352a9208..a1c057624d5b 100644 --- a/system/Format/Format.php +++ b/system/Format/Format.php @@ -79,8 +79,6 @@ public function getConfig() * * @param string $mime * - * @throws \CodeIgniter\Format\FormatException - * * @return \CodeIgniter\Format\FormatterInterface */ public function getFormatter(string $mime): FormatterInterface diff --git a/system/RESTful/ResourceController.php b/system/RESTful/ResourceController.php index 5dec51cb9e04..efcd3f828e65 100644 --- a/system/RESTful/ResourceController.php +++ b/system/RESTful/ResourceController.php @@ -1,4 +1,5 @@ format = $format; } } - } diff --git a/system/Test/Mock/MockResourcePresenter.php b/system/Test/Mock/MockResourcePresenter.php index 5358feacf499..cc70d302cb3f 100644 --- a/system/Test/Mock/MockResourcePresenter.php +++ b/system/Test/Mock/MockResourcePresenter.php @@ -1,10 +1,13 @@ -format; } - } From 4a248fbbf57b9a49f46b3c28f728497530cfbd90 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 29 Aug 2020 21:40:29 +0800 Subject: [PATCH 068/328] Add deprecation notice to changelog --- user_guide_src/source/changelogs/v4.0.5.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/changelogs/v4.0.5.rst b/user_guide_src/source/changelogs/v4.0.5.rst index 4b0df7bcdb7b..4342a1914efd 100644 --- a/user_guide_src/source/changelogs/v4.0.5.rst +++ b/user_guide_src/source/changelogs/v4.0.5.rst @@ -21,3 +21,4 @@ Bugs Fixed: Deprecations: - Deprecated ``BaseCommand::getPad`` in favor of ``BaseCommand::setPad``. +- Deprecated ``Config\Format::getFormatter`` in favor of ``CodeIgniter\Format\Format::getFormatter`` From d51d4c1dc637656a565c59155acc622f6e07e335 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 29 Aug 2020 22:56:55 +0800 Subject: [PATCH 069/328] Return accidentally deleted code in test --- tests/system/HTTP/ResponseTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/system/HTTP/ResponseTest.php b/tests/system/HTTP/ResponseTest.php index ea68efae70aa..3eba93b1fc91 100644 --- a/tests/system/HTTP/ResponseTest.php +++ b/tests/system/HTTP/ResponseTest.php @@ -553,5 +553,6 @@ public function testInvalidSameSiteCookie() $this->expectException(HTTPException::class); $this->expectExceptionMessage(lang('HTTP.invalidSameSiteSetting', ['Invalid'])); + new Response($config); } } From 69e6d30d9dc4a185d95a32453d218d24d5279ed0 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 31 Aug 2020 22:43:27 +0800 Subject: [PATCH 070/328] Add @throws block --- system/Format/Format.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/Format/Format.php b/system/Format/Format.php index a1c057624d5b..b9b205ce1b0b 100644 --- a/system/Format/Format.php +++ b/system/Format/Format.php @@ -79,6 +79,8 @@ public function getConfig() * * @param string $mime * + * @throws \CodeIgniter\Format\Exceptions\FormatException + * * @return \CodeIgniter\Format\FormatterInterface */ public function getFormatter(string $mime): FormatterInterface From ab4a9d1f8727e2631d52462d903ec338798a3b20 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 1 Sep 2020 00:17:38 +0800 Subject: [PATCH 071/328] Fixes deprecation of assertArraySubset --- system/Test/FeatureResponse.php | 17 +++++++++++++---- tests/system/Test/FeatureResponseTest.php | 11 +++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/system/Test/FeatureResponse.php b/system/Test/FeatureResponse.php index 88414ecce1cf..996105ab2857 100644 --- a/system/Test/FeatureResponse.php +++ b/system/Test/FeatureResponse.php @@ -389,15 +389,24 @@ public function getJSON() /** * Test that the response contains a matching JSON fragment. * - * @param array $fragment + * @param array $fragment + * @param boolean $strict * * @throws \Exception */ - public function assertJSONFragment(array $fragment) + public function assertJSONFragment(array $fragment, bool $strict = false) { - $json = json_decode($this->getJSON(), true); + $json = json_decode($this->getJSON(), true); + $patched = array_replace_recursive($json, $fragment); - $this->assertArraySubset($fragment, $json, false, 'Response does not contain a matching JSON fragment.'); + if ($strict) + { + $this->assertSame($json, $patched, 'Response does not contain a matching JSON fragment.'); + } + else + { + $this->assertEquals($json, $patched, 'Response does not contain a matching JSON fragment.'); + } } /** diff --git a/tests/system/Test/FeatureResponseTest.php b/tests/system/Test/FeatureResponseTest.php index 35e56548baa0..7da891656879 100644 --- a/tests/system/Test/FeatureResponseTest.php +++ b/tests/system/Test/FeatureResponseTest.php @@ -283,6 +283,17 @@ public function testJsonFragment() ]); $this->feature->assertJSONFragment(['config' => ['key-a']]); + $this->feature->assertJSONFragment(['config' => ['key-a']], true); + } + + public function testAssertJSONFragmentFollowingAssertArraySubset() + { + $this->getFeatureResponse([ + 'config' => '124', + ]); + + $this->feature->assertJSONFragment(['config' => 124]); // must fail on strict + $this->feature->assertJSONFragment(['config' => '124'], true); } public function testJsonExact() From 8ffbc3aa3f76d747cca0837f7fe2c6ef8b9328a8 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 31 Aug 2020 19:11:53 +0700 Subject: [PATCH 072/328] remove `else` on return/throw/continue early --- system/Cache/Handlers/RedisHandler.php | 3 +- system/Common.php | 3 +- system/Database/BaseBuilder.php | 6 ++-- system/Database/BaseConnection.php | 20 +++++++++----- system/Database/BaseResult.php | 9 ++++-- system/Database/BaseUtils.php | 3 +- system/Database/Forge.php | 13 +++++---- system/Database/Postgre/Connection.php | 3 +- system/Database/SQLite3/Result.php | 3 +- system/Email/Email.php | 6 ++-- system/Filters/Filters.php | 29 ++++++++++---------- system/HTTP/DownloadResponse.php | 8 ++++-- system/HTTP/IncomingRequest.php | 9 ++++-- system/Model.php | 12 +++++--- system/Session/Handlers/DatabaseHandler.php | 8 ++++-- system/Session/Handlers/FileHandler.php | 6 ++-- system/Session/Handlers/MemcachedHandler.php | 3 +- system/Session/Handlers/RedisHandler.php | 9 ++++-- system/Session/Session.php | 12 +++++--- system/Test/FeatureResponse.php | 3 +- system/Validation/FormatRules.php | 3 +- 21 files changed, 108 insertions(+), 63 deletions(-) diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index c997979667b4..d392a7959742 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -223,7 +223,8 @@ public function save(string $key, $value, int $ttl = 60) { return false; } - elseif ($ttl) + + if ($ttl) { $this->redis->expireAt($key, time() + $ttl); } diff --git a/system/Common.php b/system/Common.php index 6fa2b1cf4080..f652ab49f667 100644 --- a/system/Common.php +++ b/system/Common.php @@ -786,7 +786,8 @@ function is_really_writable(string $file): bool return true; } - elseif (! is_file($file) || ( $fp = @fopen($file, 'ab')) === false) + + if (! is_file($file) || ( $fp = @fopen($file, 'ab')) === false) { return false; } diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 2cda667ae767..2b851992be1e 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -3069,7 +3069,8 @@ protected function compileWhereHaving(string $qb_key): string { continue; } - elseif ($qbkey['escape'] === false) + + if ($qbkey['escape'] === false) { $qbkey = $qbkey['condition']; continue; @@ -3191,7 +3192,8 @@ protected function compileOrderBy(): string return $this->QBOrderBy = "\nORDER BY " . implode(', ', $this->QBOrderBy); } - elseif (is_string($this->QBOrderBy)) + + if (is_string($this->QBOrderBy)) { return $this->QBOrderBy; } diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 17c28b946ea2..aeb72daeb0d8 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -853,8 +853,9 @@ public function transBegin(bool $test_mode = false): bool { return false; } + // When transactions are nested we only begin/commit/rollback the outermost ones - elseif ($this->transDepth > 0) + if ($this->transDepth > 0) { $this->transDepth ++; return true; @@ -892,8 +893,9 @@ public function transCommit(): bool { return false; } + // When transactions are nested we only begin/commit/rollback the outermost ones - elseif ($this->transDepth > 1 || $this->_transCommit()) + if ($this->transDepth > 1 || $this->_transCommit()) { $this->transDepth --; return true; @@ -915,8 +917,9 @@ public function transRollback(): bool { return false; } + // When transactions are nested we only begin/commit/rollback the outermost ones - elseif ($this->transDepth > 1 || $this->_transRollback()) + if ($this->transDepth > 1 || $this->_transRollback()) { $this->transDepth --; return true; @@ -1287,7 +1290,8 @@ public function escapeIdentifiers($item) { return $item; } - elseif (is_array($item)) + + if (is_array($item)) { foreach ($item as $key => $value) { @@ -1296,10 +1300,12 @@ public function escapeIdentifiers($item) return $item; } + // Avoid breaking functions and literal values inside queries - elseif (ctype_digit($item) || $item[0] === "'" || ( $this->escapeChar !== '"' && $item[0] === '"') || - strpos($item, '(') !== false - ) + if (ctype_digit($item) + || $item[0] === "'" + || ( $this->escapeChar !== '"' && $item[0] === '"') + || strpos($item, '(') !== false) { return $item; } diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index 40ee09e85139..10d2c7f444cd 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -134,7 +134,8 @@ public function getResult(string $type = 'object'): array { return $this->getResultArray(); } - elseif ($type === 'object') + + if ($type === 'object') { return $this->getResultObject(); } @@ -331,7 +332,8 @@ public function getRow($n = 0, string $type = 'object') { return $this->getRowObject($n); } - elseif ($type === 'array') + + if ($type === 'array') { return $this->getRowArray($n); } @@ -548,7 +550,8 @@ public function getUnbufferedRow(string $type = 'object') { return $this->fetchAssoc(); } - elseif ($type === 'object') + + if ($type === 'object') { return $this->fetchObject(); } diff --git a/system/Database/BaseUtils.php b/system/Database/BaseUtils.php index 34c1962eec12..1c5fff1dcc19 100644 --- a/system/Database/BaseUtils.php +++ b/system/Database/BaseUtils.php @@ -104,7 +104,8 @@ public function listDatabases() { return $this->db->dataCache['db_names']; } - elseif ($this->listDatabases === false) + + if ($this->listDatabases === false) { if ($this->db->DBDebug) { diff --git a/system/Database/Forge.php b/system/Database/Forge.php index ba0880af791d..ac0235e81bcc 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -240,8 +240,8 @@ public function createDatabase(string $dbName, bool $ifNotExists = false): bool return false; } - elseif (! $this->db->query(sprintf($ifNotExists ? $this->createDatabaseIfStr : $this->createDatabaseStr, $dbName, $this->db->charset, $this->db->DBCollat)) - ) + + if (! $this->db->query(sprintf($ifNotExists ? $this->createDatabaseIfStr : $this->createDatabaseStr, $dbName, $this->db->charset, $this->db->DBCollat))) { if ($this->db->DBDebug) { @@ -305,7 +305,8 @@ public function dropDatabase(string $dbName): bool return false; } - elseif (! $this->db->query(sprintf($this->dropDatabaseStr, $dbName))) + + if (! $this->db->query(sprintf($this->dropDatabaseStr, $dbName))) { if ($this->db->DBDebug) { @@ -741,7 +742,8 @@ public function renameTable(string $table_name, string $new_table_name) { throw new \InvalidArgumentException('A table name is required for that operation.'); } - elseif ($this->renameTableStr === false) + + if ($this->renameTableStr === false) { if ($this->db->DBDebug) { @@ -1111,7 +1113,8 @@ protected function _attributeUnsigned(array &$attributes, array &$field) return; } - elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0) + + if (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0) { $field['type'] = $key; diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index d96d52296eca..40fdb2b8a881 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -240,7 +240,8 @@ public function escape($str) { return pg_escape_literal($this->connID, $str); } - elseif (is_bool($str)) + + if (is_bool($str)) { return $str ? 'TRUE' : 'FALSE'; } diff --git a/system/Database/SQLite3/Result.php b/system/Database/SQLite3/Result.php index 2fe310ed90d2..f6b96638639d 100644 --- a/system/Database/SQLite3/Result.php +++ b/system/Database/SQLite3/Result.php @@ -182,7 +182,8 @@ protected function fetchObject(string $className = 'stdClass') { return false; } - elseif ($className === 'stdClass') + + if ($className === 'stdClass') { return (object) $row; } diff --git a/system/Email/Email.php b/system/Email/Email.php index 3aa068cb4606..14ea1eb665d6 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -883,7 +883,8 @@ protected function getContentType() { return empty($this->attachments) ? 'html' : 'html-attach'; } - elseif ($this->mailType === 'text' && ! empty($this->attachments)) + + if ($this->mailType === 'text' && ! empty($this->attachments)) { return 'plain-attach'; } @@ -1989,7 +1990,8 @@ protected function SMTPAuthenticate() { return true; } - elseif (strpos($reply, '334') !== 0) + + if (strpos($reply, '334') !== 0) { $this->setErrorMessage(lang('Email.failedSMTPLogin', [$reply])); return false; diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 552a5e744e78..31e96432a714 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -97,7 +97,7 @@ class Filters * @var array */ protected $arguments = []; - + /** * Handle to the modules config. * @@ -110,9 +110,9 @@ class Filters /** * Constructor. * - * @param \Config\Filters $config - * @param RequestInterface $request - * @param ResponseInterface $response + * @param \Config\Filters $config + * @param RequestInterface $request + * @param ResponseInterface $response * @param \Config\Modules|null $moduleConfig */ public function __construct($config, RequestInterface $request, ResponseInterface $response, Modules $moduleConfig = null) @@ -123,22 +123,22 @@ public function __construct($config, RequestInterface $request, ResponseInterfac $this->moduleConfig = $moduleConfig ?? config('Modules'); } - + //-------------------------------------------------------------------- /** * If discoverFilters is enabled in Config then system will try to auto - * Discovery custom filters files in Namespaces and allow access to + * Discovery custom filters files in Namespaces and allow access to * The config object via the variable $customfilters as with the routes file - * Sample : + * Sample : * $filters->aliases['custom-auth'] = \Acme\Blob\Filters\BlobAuth::class; - */ + */ private function discoverFilters() { $locater = Services::locator(); - + $filters = $this->config; - + $files = $locater->search('Config/Filters.php'); foreach ($files as $file) @@ -151,7 +151,7 @@ private function discoverFilters() include $file; } - } + } /** * Set the response explicity. @@ -235,7 +235,8 @@ public function run(string $uri, string $position = 'before') return $result; } - elseif ($position === 'after') + + if ($position === 'after') { $result = $class->after($this->request, $this->response, $this->arguments[$alias] ?? null); @@ -279,8 +280,8 @@ public function initialize(string $uri = null) if ($this->moduleConfig->shouldDiscover('filters')) { $this->discoverFilters(); - } - + } + $this->processGlobals($uri); $this->processMethods(); $this->processFilters($uri); diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index ffa82d707a19..37ae9bd17e4d 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -58,7 +58,7 @@ class DownloadResponse extends Message implements ResponseInterface /** * Download for file * - * @var File + * @var File|null */ private $file; @@ -163,7 +163,8 @@ public function getContentLength() : int { return strlen($this->binary); } - elseif ($this->file instanceof File) + + if ($this->file instanceof File) { return $this->file->getSize(); } @@ -509,7 +510,8 @@ public function sendBody() { return $this->sendBodyByBinary(); } - elseif ($this->file !== null) + + if ($this->file !== null) { return $this->sendBodyByFilePath(); } diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index 3a95940b51d7..1e3922e68d8b 100755 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -295,11 +295,13 @@ public function isSecure(): bool { return true; } - elseif ($this->hasHeader('X-Forwarded-Proto') && $this->getHeader('X-Forwarded-Proto')->getValue() === 'https') + + if ($this->hasHeader('X-Forwarded-Proto') && $this->getHeader('X-Forwarded-Proto')->getValue() === 'https') { return true; } - elseif ($this->hasHeader('Front-End-Https') && ! empty($this->getHeader('Front-End-Https')->getValue()) && strtolower($this->getHeader('Front-End-Https')->getValue()) !== 'off') + + if ($this->hasHeader('Front-End-Https') && ! empty($this->getHeader('Front-End-Https')->getValue()) && strtolower($this->getHeader('Front-End-Https')->getValue()) !== 'off') { return true; } @@ -763,7 +765,8 @@ protected function parseQueryString(): string { return ''; } - elseif (strncmp($uri, '/', 1) === 0) + + if (strncmp($uri, '/', 1) === 0) { $uri = explode('?', $uri, 2); $_SERVER['QUERY_STRING'] = $uri[1] ?? ''; diff --git a/system/Model.php b/system/Model.php index 85de387acf85..862e4a344c13 100644 --- a/system/Model.php +++ b/system/Model.php @@ -1973,11 +1973,13 @@ public function __get(string $name) { return $this->{$name}; } - elseif (isset($this->db->$name)) + + if (isset($this->db->$name)) { return $this->db->$name; } - elseif (isset($this->builder()->$name)) + + if (isset($this->builder()->$name)) { return $this->builder()->$name; } @@ -1998,11 +2000,13 @@ public function __isset(string $name): bool { return true; } - elseif (isset($this->db->$name)) + + if (isset($this->db->$name)) { return true; } - elseif (isset($this->builder()->$name)) + + if (isset($this->builder()->$name)) { return true; } diff --git a/system/Session/Handlers/DatabaseHandler.php b/system/Session/Handlers/DatabaseHandler.php index 575d32f8fe5b..dd8070e52a98 100644 --- a/system/Session/Handlers/DatabaseHandler.php +++ b/system/Session/Handlers/DatabaseHandler.php @@ -231,7 +231,7 @@ public function write($sessionID, $sessionData): bool } // Was the ID regenerated? - elseif ($sessionID !== $this->sessionID) + if ($sessionID !== $this->sessionID) { $this->rowExists = false; $this->sessionID = $sessionID; @@ -372,7 +372,8 @@ protected function lockSession(string $sessionID): bool return $this->fail(); } - elseif ($this->platform === 'postgre') + + if ($this->platform === 'postgre') { $arg = "hashtext('{$sessionID}')" . ($this->matchIP ? ", hashtext('{$this->ipAddress}')" : ''); if ($this->db->simpleQuery("SELECT pg_advisory_lock({$arg})")) @@ -412,7 +413,8 @@ protected function releaseLock(): bool return $this->fail(); } - elseif ($this->platform === 'postgre') + + if ($this->platform === 'postgre') { if ($this->db->simpleQuery("SELECT pg_advisory_unlock({$this->lock})")) { diff --git a/system/Session/Handlers/FileHandler.php b/system/Session/Handlers/FileHandler.php index 16c36af783ba..a63c39cff514 100644 --- a/system/Session/Handlers/FileHandler.php +++ b/system/Session/Handlers/FileHandler.php @@ -254,7 +254,8 @@ public function write($sessionID, $sessionData): bool { return false; } - elseif ($this->fingerprint === md5($sessionData)) + + if ($this->fingerprint === md5($sessionData)) { return ($this->fileNew) ? true : touch($this->filePath . $sessionID); } @@ -332,7 +333,8 @@ public function destroy($session_id): bool return is_file($this->filePath . $session_id) ? (unlink($this->filePath . $session_id) && $this->destroyCookie()) : true; } - elseif ($this->filePath !== null) + + if ($this->filePath !== null) { clearstatcache(); diff --git a/system/Session/Handlers/MemcachedHandler.php b/system/Session/Handlers/MemcachedHandler.php index d9db2e27e0ed..51537982274f 100644 --- a/system/Session/Handlers/MemcachedHandler.php +++ b/system/Session/Handlers/MemcachedHandler.php @@ -216,8 +216,9 @@ public function write($sessionID, $sessionData): bool { return false; } + // Was the ID regenerated? - elseif ($sessionID !== $this->sessionID) + if ($sessionID !== $this->sessionID) { if (! $this->releaseLock() || ! $this->lockSession($sessionID)) { diff --git a/system/Session/Handlers/RedisHandler.php b/system/Session/Handlers/RedisHandler.php index db2707511f10..1b4370f9350d 100644 --- a/system/Session/Handlers/RedisHandler.php +++ b/system/Session/Handlers/RedisHandler.php @@ -101,7 +101,8 @@ public function __construct(AppConfig $config, string $ipAddress) { throw SessionException::forEmptySavepath(); } - elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->savePath, $matches)) + + if (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->savePath, $matches)) { // @phpstan-ignore-next-line isset($matches[3]) || $matches[3] = ''; // Just to avoid undefined index notices below @@ -222,8 +223,9 @@ public function write($sessionID, $sessionData): bool { return false; } + // Was the ID regenerated? - elseif ($sessionID !== $this->sessionID) + if ($sessionID !== $this->sessionID) { if (! $this->releaseLock() || ! $this->lockSession($sessionID)) { @@ -387,7 +389,8 @@ protected function lockSession(string $sessionID): bool log_message('error', 'Session: Unable to obtain lock for ' . $this->keyPrefix . $sessionID . ' after 30 attempts, aborting.'); return false; } - elseif ($ttl === -1) + + if ($ttl === -1) { log_message('debug', 'Session: Lock for ' . $this->keyPrefix . $sessionID . ' had no TTL, overriding.'); } diff --git a/system/Session/Session.php b/system/Session/Session.php index 64560662d07b..f5ce19b75b64 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -220,13 +220,15 @@ public function start() return; // @codeCoverageIgnoreEnd } - elseif ((bool) ini_get('session.auto_start')) + + if ((bool) ini_get('session.auto_start')) { $this->logger->error('Session: session.auto_start is enabled in php.ini. Aborting.'); return; } - elseif (session_status() === PHP_SESSION_ACTIVE) + + if (session_status() === PHP_SESSION_ACTIVE) { $this->logger->warning('Session: Sessions is enabled, and one exists.Please don\'t $session->start();'); @@ -544,7 +546,8 @@ public function get(string $key = null) { return $value; } - elseif (empty($_SESSION)) + + if (empty($_SESSION)) { return $key === null ? [] : null; } @@ -661,7 +664,8 @@ public function __get(string $key) { return $_SESSION[$key]; } - elseif ($key === 'session_id') + + if ($key === 'session_id') { return session_id(); } diff --git a/system/Test/FeatureResponse.php b/system/Test/FeatureResponse.php index 996105ab2857..9e51339ca117 100644 --- a/system/Test/FeatureResponse.php +++ b/system/Test/FeatureResponse.php @@ -145,7 +145,8 @@ public function getRedirectUrl(): ?string { return $this->response->getHeaderLine('Location'); } - elseif ($this->response->hasHeader('Refresh')) + + if ($this->response->hasHeader('Refresh')) { return str_replace('0;url=', '', $this->response->getHeaderLine('Refresh')); } diff --git a/system/Validation/FormatRules.php b/system/Validation/FormatRules.php index 555c09244005..4e09749b493b 100644 --- a/system/Validation/FormatRules.php +++ b/system/Validation/FormatRules.php @@ -362,7 +362,8 @@ public function valid_url(string $str = null): bool { return false; } - elseif (preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches)) + + if (preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches)) { if (! in_array($matches[1], ['http', 'https'], true)) { From 9f874fc4ead5d4ec4a04d5b5026128bdd7c5a571 Mon Sep 17 00:00:00 2001 From: mrr000 Date: Tue, 1 Sep 2020 15:59:06 +0300 Subject: [PATCH 073/328] Fix Toolbar event collector number formatting --- system/Debug/Toolbar/Collectors/Events.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/system/Debug/Toolbar/Collectors/Events.php b/system/Debug/Toolbar/Collectors/Events.php index 111675ee2311..1e14c79d0481 100644 --- a/system/Debug/Toolbar/Collectors/Events.php +++ b/system/Debug/Toolbar/Collectors/Events.php @@ -145,17 +145,22 @@ public function display(): array { $data['events'][$key] = [ 'event' => $key, - 'duration' => number_format(($row['end'] - $row['start']) * 1000, 2), + 'duration' => ($row['end'] - $row['start']) * 1000, 'count' => 1, ]; continue; } - $data['events'][$key]['duration'] += number_format(($row['end'] - $row['start']) * 1000, 2); + $data['events'][$key]['duration'] += ($row['end'] - $row['start']) * 1000; $data['events'][$key]['count']++; } + foreach ($data['events'] as &$row) + { + $row['duration'] = number_format($row['duration'], 2); + } + return $data; } From 214455868d54e19bb7ab2d3f7714e3cfaac568ed Mon Sep 17 00:00:00 2001 From: michalsn Date: Tue, 1 Sep 2020 21:03:45 +0200 Subject: [PATCH 074/328] Fix for setBinds() method to sets the proper format of array --- system/Database/Query.php | 15 ++++++- .../Database/Live/PreparedQueryTest.php | 43 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/system/Database/Query.php b/system/Database/Query.php index f0b44c514d37..5cdf7fe0c1d9 100644 --- a/system/Database/Query.php +++ b/system/Database/Query.php @@ -168,12 +168,23 @@ public function setQuery(string $sql, $binds = null, bool $setEscape = true) /** * Will store the variables to bind into the query later. * - * @param array $binds + * @param array $binds + * @param boolean $setEscape * * @return $this */ - public function setBinds(array $binds) + public function setBinds(array $binds, bool $setEscape = true) { + if ($setEscape) + { + array_walk($binds, function (&$item) { + $item = [ + $item, + true, + ]; + }); + } + $this->binds = $binds; return $this; diff --git a/tests/system/Database/Live/PreparedQueryTest.php b/tests/system/Database/Live/PreparedQueryTest.php index da72b7459b1a..003662526cb7 100644 --- a/tests/system/Database/Live/PreparedQueryTest.php +++ b/tests/system/Database/Live/PreparedQueryTest.php @@ -1,6 +1,7 @@ close(); } + public function testPrepareReturnsManualPreparedQuery() + { + $query = $this->db->prepare(function ($db) { + $sql = "INSERT INTO {$db->DBPrefix}user (name, email, country) VALUES (?, ?, ?)"; + + return (new Query($db))->setQuery($sql); + }); + + $this->assertInstanceOf(BasePreparedQuery::class, $query); + + $pre = $this->db->DBPrefix; + + $placeholders = '?, ?, ?'; + + if ($this->db->DBDriver === 'Postgre') + { + $placeholders = '$1, $2, $3'; + } + + $expected = "INSERT INTO {$pre}user (name, email, country) VALUES ({$placeholders})"; + $this->assertEquals($expected, $query->getQueryString()); + + $query->close(); + } + //-------------------------------------------------------------------- public function testExecuteRunsQueryAndReturnsResultObject() @@ -62,6 +88,23 @@ public function testExecuteRunsQueryAndReturnsResultObject() $query->close(); } + public function testExecuteRunsQueryAndReturnsManualResultObject() + { + $query = $this->db->prepare(function ($db) { + $sql = "INSERT INTO {$db->DBPrefix}user (name, email, country) VALUES (?, ?, ?)"; + + return (new Query($db))->setQuery($sql); + }); + + $query->execute('foo', 'foo@example.com', ''); + $query->execute('bar', 'bar@example.com', ''); + + $this->seeInDatabase($this->db->DBPrefix . 'user', ['name' => 'foo', 'email' => 'foo@example.com']); + $this->seeInDatabase($this->db->DBPrefix . 'user', ['name' => 'bar', 'email' => 'bar@example.com']); + + $query->close(); + } + //-------------------------------------------------------------------- } From 135180ce6ae01b620046aa2139022f7d28fbf2d5 Mon Sep 17 00:00:00 2001 From: Kang Jing Date: Thu, 3 Sep 2020 13:01:35 +0800 Subject: [PATCH 075/328] fix. toolbar disable,view comment not remove (#3010) fix. toolbar disable, view comment not remove --- system/Filters/Filters.php | 154 +++++++++++++++++---------- system/View/View.php | 2 +- tests/system/Filters/FiltersTest.php | 120 +++++++++++++++++++++ 3 files changed, 221 insertions(+), 55 deletions(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 31e96432a714..5cef2f08ad1a 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -62,6 +62,24 @@ class Filters 'after' => [], ]; + /** + * The collection of filters' class names that will + * be used to execute in each position. + * + * @var array + */ + protected $filtersClass = [ + 'before' => [], + 'after' => [], + ]; + + /** + * Any arguments to be passed to filtersClass. + * + * @var array + */ + protected $argumentsClass = []; + /** * The original config file * @@ -179,72 +197,49 @@ public function run(string $uri, string $position = 'before') { $this->initialize(strtolower($uri)); - foreach ($this->filters[$position] as $alias => $rules) + foreach ($this->filtersClass[$position] as $className) { - if (is_numeric($alias) && is_string($rules)) - { - $alias = $rules; - } + $class = new $className(); - if (! array_key_exists($alias, $this->config->aliases)) + if (! $class instanceof FilterInterface) { - throw FilterException::forNoAlias($alias); + throw FilterException::forIncorrectInterface(get_class($class)); } - if (is_array($this->config->aliases[$alias])) + if ($position === 'before') { - $classNames = $this->config->aliases[$alias]; - } - else - { - $classNames = [$this->config->aliases[$alias]]; - } + $result = $class->before($this->request, $this->argumentsClass[$className] ?? null); - foreach ($classNames as $className) - { - $class = new $className(); - - if (! $class instanceof FilterInterface) + if ($result instanceof RequestInterface) { - throw FilterException::forIncorrectInterface(get_class($class)); + $this->request = $result; + continue; } - if ($position === 'before') + // If the response object was sent back, + // then send it and quit. + if ($result instanceof ResponseInterface) { - $result = $class->before($this->request, $this->arguments[$alias] ?? null); - - if ($result instanceof RequestInterface) - { - $this->request = $result; - continue; - } - - // 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; - } - + // short circuit - bypass any other filters return $result; } - - if ($position === 'after') + // Ignore an empty result + if (empty($result)) { - $result = $class->after($this->request, $this->response, $this->arguments[$alias] ?? null); + continue; + } - if ($result instanceof ResponseInterface) - { - $this->response = $result; - continue; - } + return $result; + } + + if ($position === 'after') + { + $result = $class->after($this->request, $this->response, $this->argumentsClass[$className] ?? null); + + if ($result instanceof ResponseInterface) + { + $this->response = $result; + continue; } } } @@ -296,6 +291,9 @@ public function initialize(string $uri = null) $this->filters['after'][] = 'toolbar'; } + $this->processAliasesToClass('before'); + $this->processAliasesToClass('after'); + $this->initialized = true; return $this; @@ -313,6 +311,16 @@ public function getFilters(): array return $this->filters; } + /** + * Returns the filtersClass array. + * + * @return array + */ + public function getFiltersClass(): array + { + return $this->filtersClass; + } + /** * Adds a new alias to the config file. * MUST be called prior to initialize(); @@ -380,16 +388,22 @@ public function enableFilter(string $name, string $when = 'before') throw FilterException::forNoAlias($name); } + $classNames = (array) $this->config->aliases[$name]; + + foreach ($classNames as $className) + { + $this->argumentsClass[$className] = $this->arguments[$name] ?? null; + } + if (! isset($this->filters[$when][$name])) { - $this->filters[$when][] = $name; + $this->filters[$when][] = $name; + $this->filtersClass[$when] = array_merge($this->filtersClass[$when], $classNames); } return $this; } - //-------------------------------------------------------------------- - /** * Returns the arguments for a specified key, or all. * @@ -530,6 +544,38 @@ protected function processFilters(string $uri = null) } } + /** + * Maps filter aliases to the equivalent filter classes + * + * @throws FilterException + * + * @return void + */ + protected function processAliasesToClass(string $position) + { + foreach ($this->filters[$position] as $alias => $rules) + { + if (is_numeric($alias) && is_string($rules)) + { + $alias = $rules; + } + + if (! array_key_exists($alias, $this->config->aliases)) + { + throw FilterException::forNoAlias($alias); + } + + if (is_array($this->config->aliases[$alias])) + { + $this->filtersClass[$position] = array_merge($this->filtersClass[$position], $this->config->aliases[$alias]); + } + else + { + $this->filtersClass[$position][] = $this->config->aliases[$alias]; + } + } + } + /** * Check paths for match for URI * diff --git a/system/View/View.php b/system/View/View.php index f388a0bee7ee..a2f506a39948 100644 --- a/system/View/View.php +++ b/system/View/View.php @@ -261,7 +261,7 @@ public function render(string $view, array $options = null, bool $saveData = nul $this->logPerformance($this->renderVars['start'], microtime(true), $this->renderVars['view']); - if ($this->debug && (! isset($options['debug']) || $options['debug'] === true)) + if (($this->debug && (! isset($options['debug']) || $options['debug'] === true)) && in_array('CodeIgniter\Filters\DebugToolbar', service('filters')->getFiltersClass()['after'], true)) { $toolbarCollectors = config(\Config\Toolbar::class)->collectors; diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index 36efe961d47f..186beb87c4b7 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -45,6 +45,7 @@ protected function setUp(): void public function testProcessMethodDetectsCLI() { $config = [ + 'aliases' => ['foo' => ''], 'methods' => [ 'cli' => ['foo'], ], @@ -66,6 +67,7 @@ public function testProcessMethodDetectsGetRequests() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => ['foo' => ''], 'methods' => [ 'get' => ['foo'], ], @@ -88,6 +90,10 @@ public function testProcessMethodRespectsMethod() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + ], 'methods' => [ 'post' => ['foo'], 'get' => ['bar'], @@ -108,6 +114,10 @@ public function testProcessMethodIgnoresMethod() $_SERVER['REQUEST_METHOD'] = 'DELETE'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + ], 'methods' => [ 'post' => ['foo'], 'get' => ['bar'], @@ -130,6 +140,11 @@ public function testProcessMethodProcessGlobals() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['bar'], // not excluded @@ -175,6 +190,11 @@ public function testProcessMethodProcessGlobalsWithExcept(array $except) $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['except' => $except], @@ -205,6 +225,11 @@ public function testProcessMethodProcessesFiltersBefore() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'filters' => [ 'foo' => [ 'before' => ['admin/*'], @@ -230,6 +255,11 @@ public function testProcessMethodProcessesFiltersAfter() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'filters' => [ 'foo' => [ 'before' => ['admin/*'], @@ -257,6 +287,14 @@ public function testProcessMethodProcessesCombined() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foog' => '', + 'barg' => '', + 'bazg' => '', + 'foo' => '', + 'bar' => '', + 'foof' => '', + ], 'globals' => [ 'before' => [ 'foog' => ['except' => ['admin/*']], @@ -299,6 +337,12 @@ public function testProcessMethodProcessesCombinedAfterForToolbar() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'toolbar' => '', + 'bazg' => '', + 'bar' => '', + 'foof' => '', + ], 'globals' => [ 'after' => [ 'toolbar', @@ -495,6 +539,11 @@ public function testBeforeExceptString() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin/*'], @@ -523,6 +572,11 @@ public function testBeforeExceptInapplicable() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['except' => 'george/*'], @@ -552,6 +606,11 @@ public function testAfterExceptString() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'globals' => [ 'before' => [ 'bar' @@ -580,6 +639,11 @@ public function testAfterExceptInapplicable() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'baz' => '', + ], 'globals' => [ 'before' => [ 'bar' @@ -774,6 +838,12 @@ public function testMatchesURICaseInsensitively() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'frak' => '', + 'baz' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['except' => 'Admin/*'], @@ -816,6 +886,11 @@ public function testFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'bar' => '', + 'frak' => '', + ], 'filters' => [ 'frak' => [ 'before' => ['admin*'], @@ -846,6 +921,11 @@ public function testGlobalFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'one' => '', + 'two' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -883,6 +963,12 @@ public function testCombinedFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'one' => '', + 'frak' => '', + 'two' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -926,6 +1012,11 @@ public function testSegmentedFilterMatching() $_SERVER['REQUEST_METHOD'] = 'GET'; $config = [ + 'aliases' => [ + 'foo' => '', + 'one' => '', + 'frak' => '', + ], 'globals' => [ 'before' => [ 'foo' => ['except' => 'admin*'], @@ -983,4 +1074,33 @@ public function testFilterAlitasMultiple() $this->assertEquals('http://exampleMultipleCSP.com', $request->csp); } + public function testFilterClass() + { + $config = [ + 'aliases' => [ + 'multipleTest' => [ + 'CodeIgniter\Filters\fixtures\Multiple1', + 'CodeIgniter\Filters\fixtures\Multiple2', + ], + ], + 'globals' => [ + 'after' => [ + 'multipleTest', + ], + ], + ]; + $filters = new Filters((object) $config, $this->request, $this->response); + $uri = 'admin/foo/bar'; + + $filters->run($uri, 'before'); + $expected = [ + 'after' => [ + 'CodeIgniter\Filters\fixtures\Multiple1', + 'CodeIgniter\Filters\fixtures\Multiple2', + ], + 'before' => [], + ]; + $this->assertEquals($expected, $filters->getFiltersClass()); + } + } From a0e659fe0657e46ed235aaefa3d76da2c8267ae4 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Wed, 2 Sep 2020 19:25:20 +0800 Subject: [PATCH 076/328] Allow fallback when make:scaffold's argument is missing --- system/Commands/Generators/CreateScaffold.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Commands/Generators/CreateScaffold.php b/system/Commands/Generators/CreateScaffold.php index 93f5ba06387b..d2166cab2139 100644 --- a/system/Commands/Generators/CreateScaffold.php +++ b/system/Commands/Generators/CreateScaffold.php @@ -129,7 +129,7 @@ public function run(array $params) ]; // Call those commands! - $class = $params[0]; + $class = $params[0] ?? CLI::getSegment(2); $this->call('make:controller', array_merge([$class], $controllerOpts, $genOptions)); $this->call('make:model', array_merge([$class], $modelOpts, $genOptions)); $this->call('make:entity', array_merge([$class], $genOptions)); From a9311beb4c5d004be9cd97b0498ddd15d8beb914 Mon Sep 17 00:00:00 2001 From: Kang Jing Date: Thu, 3 Sep 2020 17:04:32 +0800 Subject: [PATCH 077/328] [CI SKIP] Fix. Migration userguide parameter error --- user_guide_src/source/dbmgmt/migration.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/dbmgmt/migration.rst b/user_guide_src/source/dbmgmt/migration.rst index c4405f0f0ee7..9574e0e960e5 100644 --- a/user_guide_src/source/dbmgmt/migration.rst +++ b/user_guide_src/source/dbmgmt/migration.rst @@ -323,9 +323,9 @@ Class Reference :returns: The current MigrationRunner instance :rtype: CodeIgniter\\Database\\MigrationRunner - Sets the path the library should look for migration files:: + Sets the namespace the library should look for migration files:: - $migration->setNamespace($path) + $migration->setNamespace($namespace) ->latest(); .. php:method:: setGroup($group) @@ -333,7 +333,7 @@ Class Reference :returns: The current MigrationRunner instance :rtype: CodeIgniter\\Database\\MigrationRunner - Sets the path the library should look for migration files:: + Sets the group the library should look for migration files:: - $migration->setNamespace($path) + $migration->setGroup($group) ->latest(); From c3384ec4df5bbb5f895cfc00d6e7135cd6f14926 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Wed, 2 Sep 2020 19:48:37 +0800 Subject: [PATCH 078/328] Use getSegment(2) to refer to the first argument of generator command --- system/CLI/CLI.php | 2 ++ system/CLI/GeneratorCommand.php | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 8da7814aee0b..3f982c129c22 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -947,6 +947,8 @@ public static function getURI(): string * // segment(3) is 'three', not '-f' or 'anOption' * > php spark one two -f anOption three * + * **IMPORTANT:** The index here is one-based instead of zero-based. + * * @param integer $index * * @return mixed|null diff --git a/system/CLI/GeneratorCommand.php b/system/CLI/GeneratorCommand.php index 0bf96cac8bd0..0b0ba192ade8 100644 --- a/system/CLI/GeneratorCommand.php +++ b/system/CLI/GeneratorCommand.php @@ -152,7 +152,8 @@ public function run(array $params) */ protected function getClassName(): string { - $name = $this->params[0] ?? CLI::getSegment(0); + $name = $this->params[0] ?? CLI::getSegment(2); + return $name ?? ''; } @@ -203,6 +204,7 @@ protected function qualifyClassName(string $class): string protected function getRootNamespace(): string { $rootNamespace = $this->params['n'] ?? CLI::getOption('n') ?? APP_NAMESPACE; + return trim(str_replace('/', '\\', $rootNamespace), '\\'); } @@ -238,6 +240,7 @@ protected function buildPath(string $class): string $path = $base . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $name) . '.php'; $filename = $this->modifyBasename(basename($path)); + return implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $path), 0, -1)) . DIRECTORY_SEPARATOR . $filename; } @@ -310,6 +313,7 @@ protected function setReplacements(string $template, string $class): string $template = str_replace($namespaces, $this->getNamespace($class), $template); $class = str_replace($this->getNamespace($class) . '\\', '', $class); + return str_replace($classes, $class, $template); } @@ -326,11 +330,10 @@ protected function sortImports(string $template): string { $imports = explode("\n", trim($match['imports'])); sort($imports); + return str_replace(trim($match['imports']), implode("\n", $imports), $template); } - // @codeCoverageIgnoreStart - return $template; - // @codeCoverageIgnoreEnd + return $template; // @codeCoverageIgnore } } From ca87e5e94139eacab830e300ac57999e8e3a0b68 Mon Sep 17 00:00:00 2001 From: Luciano Fuentes Date: Thu, 3 Sep 2020 13:12:06 -0300 Subject: [PATCH 079/328] fix(Router): reset filter info before get defined filters (#3592) fix(Router): reset filter info before get defined filters --- system/Router/Router.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/Router/Router.php b/system/Router/Router.php index 7ec367c48fe6..abf91e26379f 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -127,7 +127,7 @@ class Router implements RouterInterface * The filter info from Route Collection * if the matched route should be filtered. * - * @var string + * @var string|null */ protected $filterInfo; @@ -175,6 +175,9 @@ public function handle(string $uri = null) // Decode URL-encoded string $uri = urldecode($uri); + // Restart filterInfo + $this->filterInfo = null; + if ($this->checkRoutes($uri)) { if ($this->collection->isFiltered($this->matchedRoute[0])) From f76cb2bd433ee10dd8c72cc39399aaac6d81fab2 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Wed, 2 Sep 2020 23:33:40 +0800 Subject: [PATCH 080/328] Refinements to Autoloader --- system/Autoloader/Autoloader.php | 24 ++-- tests/system/Autoloader/AutoloaderTest.php | 125 ++++++++------------- 2 files changed, 64 insertions(+), 85 deletions(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 411e6e7dd3e5..a5ce8a4525b9 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -39,6 +39,9 @@ namespace CodeIgniter\Autoloader; +use Config\Autoload; +use Config\Modules; + /** * CodeIgniter Autoloader * @@ -100,11 +103,11 @@ class Autoloader * the valid parts that we'll need. * * @param \Config\Autoload $config - * @param \Config\Modules $moduleConfig + * @param \Config\Modules $modules * * @return $this */ - public function initialize(\Config\Autoload $config, \Config\Modules $moduleConfig) + public function initialize(Autoload $config, Modules $modules) { // We have to have one or the other, though we don't enforce the need // to have both present in order to work. @@ -124,7 +127,7 @@ public function initialize(\Config\Autoload $config, \Config\Modules $moduleConf } // Should we load through Composer's namespaces, also? - if ($moduleConfig->discoverInComposer) + if ($modules->discoverInComposer) { $this->discoverComposerNamespaces(); } @@ -173,7 +176,7 @@ public function register() * @param array|string $namespace * @param string $path * - * @return Autoloader + * @return $this */ public function addNamespace($namespace, string $path = null) { @@ -187,18 +190,18 @@ public function addNamespace($namespace, string $path = null) { foreach ($path as $dir) { - $this->prefixes[$prefix][] = rtrim($dir, '/') . '/'; + $this->prefixes[$prefix][] = rtrim($dir, '\\/') . DIRECTORY_SEPARATOR; } continue; } - $this->prefixes[$prefix][] = rtrim($path, '/') . '/'; + $this->prefixes[$prefix][] = rtrim($path, '\\/') . DIRECTORY_SEPARATOR; } } else { - $this->prefixes[trim($namespace, '\\')][] = rtrim($path, '/') . '/'; + $this->prefixes[trim($namespace, '\\')][] = rtrim($path, '\\/') . DIRECTORY_SEPARATOR; } return $this; @@ -232,11 +235,14 @@ public function getNamespace(string $prefix = null) * * @param string $namespace * - * @return Autoloader + * @return $this */ public function removeNamespace(string $namespace) { - unset($this->prefixes[trim($namespace, '\\')]); + if (isset($this->prefixes[trim($namespace, '\\')])) + { + unset($this->prefixes[trim($namespace, '\\')]); + } return $this; } diff --git a/tests/system/Autoloader/AutoloaderTest.php b/tests/system/Autoloader/AutoloaderTest.php index a097849e594e..fd998bf16457 100644 --- a/tests/system/Autoloader/AutoloaderTest.php +++ b/tests/system/Autoloader/AutoloaderTest.php @@ -1,9 +1,13 @@ -discoverInComposer = false; + $config = new Autoload(); + $modules = new Modules(); + $modules->discoverInComposer = false; $config->classmap = [ 'UnnamespacedClass' => SUPPORTPATH . 'Autoloader/UnnamespacedClass.php', @@ -33,7 +35,7 @@ protected function setUp(): void ]; $this->loader = new Autoloader(); - $this->loader->initialize($config, $moduleConfig)->register(); + $this->loader->initialize($config, $modules)->register(); } public function testLoadStoredClass() @@ -43,15 +45,16 @@ public function testLoadStoredClass() public function testInitializeWithInvalidArguments() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage("Config array must contain either the 'psr4' key or the 'classmap' key."); - $config = new Autoload(); - $config->classmap = []; - $config->psr4 = []; - $moduleConfig = new Modules(); - $moduleConfig->discoverInComposer = false; + $config = new Autoload(); + $config->classmap = []; + $config->psr4 = []; + $modules = new Modules(); + $modules->discoverInComposer = false; - (new Autoloader())->initialize($config, $moduleConfig); + (new Autoloader())->initialize($config, $modules); } //-------------------------------------------------------------------- @@ -60,30 +63,24 @@ public function testInitializeWithInvalidArguments() public function testServiceAutoLoaderFromShareInstances() { - $auto_loader = \CodeIgniter\Config\Services::autoloader(); - // $auto_loader->register(); + $autoloader = Services::autoloader(); // look for Home controller, as that should be in base repo - $actual = $auto_loader->loadClass('App\Controllers\Home'); + $actual = $autoloader->loadClass('App\Controllers\Home'); $expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php'; $this->assertSame($expected, $actual); } - //-------------------------------------------------------------------- - public function testServiceAutoLoader() { - $getShared = false; - $auto_loader = \CodeIgniter\Config\Services::autoloader($getShared); - $auto_loader->initialize(new Autoload(), new Modules()); - $auto_loader->register(); + $autoloader = Services::autoloader(false); + $autoloader->initialize(new Autoload(), new Modules()); + $autoloader->register(); // look for Home controller, as that should be in base repo - $actual = $auto_loader->loadClass('App\Controllers\Home'); + $actual = $autoloader->loadClass('App\Controllers\Home'); $expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php'; $this->assertSame($expected, $actual); } - //-------------------------------------------------------------------- - public function testExistingFile() { $actual = $this->loader->loadClass('App\Controllers\Home'); @@ -95,17 +92,13 @@ public function testExistingFile() $this->assertSame($expected, $actual); } - //-------------------------------------------------------------------- - - public function testMatchesWithPreceedingSlash() + public function testMatchesWithPrecedingSlash() { $actual = $this->loader->loadClass('\App\Controllers\Home'); $expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php'; $this->assertSame($expected, $actual); } - //-------------------------------------------------------------------- - public function testMatchesWithFileExtension() { $actual = $this->loader->loadClass('\App\Controllers\Home.php'); @@ -113,30 +106,11 @@ public function testMatchesWithFileExtension() $this->assertSame($expected, $actual); } - //-------------------------------------------------------------------- - public function testMissingFile() { $this->assertFalse($this->loader->loadClass('\App\Missing\Classname')); } - //-------------------------------------------------------------------- - - public function testInitializeException() - { - $this->expectException('InvalidArgumentException'); - $this->expectExceptionMessage("Config array must contain either the 'psr4' key or the 'classmap' key."); - - $config = new Autoload(); - $config->classmap = []; - $config->psr4 = []; - $moduleConfig = new Modules(); - $moduleConfig->discoverInComposer = false; - - $this->loader = new Autoloader(); - $this->loader->initialize($config, $moduleConfig); - } - public function testAddNamespaceWorks() { $this->assertFalse($this->loader->loadClass('My\App\Class')); @@ -151,8 +125,12 @@ public function testAddNamespaceWorks() public function testAddNamespaceMultiplePathsWorks() { - $this->loader->addNamespace('My\App', APPPATH . 'Config'); - $this->loader->addNamespace('My\App', __DIR__); + $this->loader->addNamespace([ + 'My\App' => [ + APPPATH . 'Config', + __DIR__, + ], + ]); $actual = $this->loader->loadClass('My\App\App'); $expected = APPPATH . 'Config' . DIRECTORY_SEPARATOR . 'App.php'; @@ -173,7 +151,16 @@ public function testAddNamespaceStringToArray() ); } - //-------------------------------------------------------------------- + public function testGetNamespaceGivesArray() + { + $this->assertSame([ + 'App' => [APPPATH], + 'CodeIgniter' => [SYSTEMPATH], + ], $this->loader->getNamespace()); + + $this->assertSame([SYSTEMPATH], $this->loader->getNamespace('CodeIgniter')); + $this->assertSame([], $this->loader->getNamespace('Foo')); + } public function testRemoveNamespace() { @@ -184,8 +171,6 @@ public function testRemoveNamespace() $this->assertFalse((bool) $this->loader->loadClass('My\App\AutoloaderTest')); } - //-------------------------------------------------------------------- - public function testloadClassConfigFound() { $this->loader->addNamespace('Config', APPPATH . 'Config'); @@ -195,16 +180,12 @@ public function testloadClassConfigFound() ); } - //-------------------------------------------------------------------- - public function testloadClassConfigNotFound() { $this->loader->addNamespace('Config', APPPATH . 'Config'); $this->assertFalse($this->loader->loadClass('NotFound')); } - //-------------------------------------------------------------------- - public function testLoadLegacy() { // should not be able to find a folder @@ -218,8 +199,6 @@ public function testLoadLegacy() $this->assertFalse($this->loader->loadClass('Controllers\Home')); } - //-------------------------------------------------------------------- - public function testSanitizationSimply() { $test = '${../path}!#/to/some/file.php_'; @@ -228,8 +207,6 @@ public function testSanitizationSimply() $this->assertEquals($expected, $this->loader->sanitizeFilename($test)); } - //-------------------------------------------------------------------- - public function testSanitizationAllowUnicodeChars() { $test = 'Ä/path/to/some/file.php_'; @@ -238,8 +215,6 @@ public function testSanitizationAllowUnicodeChars() $this->assertEquals($expected, $this->loader->sanitizeFilename($test)); } - //-------------------------------------------------------------------- - public function testSanitizationAllowsWindowsFilepaths() { $test = 'C:\path\to\some/file.php'; @@ -247,16 +222,14 @@ public function testSanitizationAllowsWindowsFilepaths() $this->assertEquals($test, $this->loader->sanitizeFilename($test)); } - //-------------------------------------------------------------------- - public function testFindsComposerRoutes() { - $config = new Autoload(); - $moduleConfig = new Modules(); - $moduleConfig->discoverInComposer = true; + $config = new Autoload(); + $modules = new Modules(); + $modules->discoverInComposer = true; $this->loader = new Autoloader(); - $this->loader->initialize($config, $moduleConfig); + $this->loader->initialize($config, $modules); $namespaces = $this->loader->getNamespace(); $this->assertArrayHasKey('Laminas\\Escaper', $namespaces); @@ -266,14 +239,14 @@ public function testFindsComposerRoutesWithComposerPathNotFound() { $composerPath = COMPOSER_PATH; - $config = new Autoload(); - $moduleConfig = new Modules(); - $moduleConfig->discoverInComposer = true; + $config = new Autoload(); + $modules = new Modules(); + $modules->discoverInComposer = true; $this->loader = new Autoloader(); rename(COMPOSER_PATH, COMPOSER_PATH . '.backup'); - $this->loader->initialize($config, $moduleConfig); + $this->loader->initialize($config, $modules); rename(COMPOSER_PATH . '.backup', $composerPath); $namespaces = $this->loader->getNamespace(); From 43dcc115f9366f17b447ad4537d1b1ae1c6b8a85 Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 3 Sep 2020 17:50:13 +0200 Subject: [PATCH 081/328] Fix current_url() helper function to correctly propagate the baseURL when working in subfolder --- system/Helpers/url_helper.php | 2 +- tests/system/Helpers/URLHelperTest.php | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index dcdb89265f7a..c2511e1c35dc 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -170,7 +170,7 @@ function current_url(bool $returnObject = false) if (! empty($baseUri->getPath())) { - $uri->setPath(rtrim($baseUri->getPath(), '/ ') . '/' . $uri->getPath()); + $uri->setHost($baseUri->getHost() . $baseUri->getPath()); } // Since we're basing off of the IncomingRequest URI, diff --git a/tests/system/Helpers/URLHelperTest.php b/tests/system/Helpers/URLHelperTest.php index c6c08c74fb0e..5815d34edfd4 100644 --- a/tests/system/Helpers/URLHelperTest.php +++ b/tests/system/Helpers/URLHelperTest.php @@ -183,11 +183,17 @@ public function testSiteURLInSubfolder() $config = new App(); $config->baseURL = 'http://example.com/foo/public'; $request = Services::request($config); - $request->uri = new URI('http://example.com/foo/public/bar'); + $request->uri = (new URI())->setScheme('http://')->setHost('example.com/foo/public/')->setPath('bar'); Services::injectMock('request', $request); $this->assertEquals('http://example.com/foo/public/bar', current_url()); + + $uri = current_url(true); + $this->assertEquals(['bar'], $uri->getSegments()); + $this->assertEquals('bar', $uri->getSegment(1)); + $this->assertEquals('example.com/foo/public/', $uri->getHost()); + $this->assertEquals('http', $uri->getScheme()); } /** From a00f418a52be0a6a7b7e93feaa6cf4654c670411 Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 3 Sep 2020 19:31:22 +0200 Subject: [PATCH 082/328] Add slash at the end of host --- system/Helpers/url_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index c2511e1c35dc..0ad9cfad6a28 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -170,7 +170,7 @@ function current_url(bool $returnObject = false) if (! empty($baseUri->getPath())) { - $uri->setHost($baseUri->getHost() . $baseUri->getPath()); + $uri->setHost($baseUri->getHost() . rtrim($baseUri->getPath(), '/ ') . '/'); } // Since we're basing off of the IncomingRequest URI, From f498c502f1ee16ffa4c70286b8e37478dec60954 Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 3 Sep 2020 19:20:29 +0200 Subject: [PATCH 083/328] Make short HTTP protocol version acceptable --- system/HTTP/CURLRequest.php | 2 +- system/HTTP/Message.php | 3 +++ tests/system/HTTP/CURLRequestTest.php | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index c9778ea9c421..336f22bad093 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -604,7 +604,7 @@ protected function setResponseHeaders(array $headers = []) } else if (strpos($header, 'HTTP') === 0) { - preg_match('#^HTTP\/([12]\.[01]) ([0-9]+) (.+)#', $header, $matches); + preg_match('#^HTTP\/([12](?:\.[01])?) ([0-9]+) (.+)#', $header, $matches); if (isset($matches[1])) { diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 0121e89528b8..4defa29c6d8d 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -379,6 +379,9 @@ public function setProtocolVersion(string $version) $version = substr($version, strpos($version, '/') + 1); } + // Make sure that version is in the correct format + $version = number_format($version, 1); + if (! in_array($version, $this->validProtocolVersions, true)) { throw HTTPException::forInvalidHTTPProtocol(implode(', ', $this->validProtocolVersions)); diff --git a/tests/system/HTTP/CURLRequestTest.php b/tests/system/HTTP/CURLRequestTest.php index 49d2a76d9cb9..3aafea0ea5cc 100644 --- a/tests/system/HTTP/CURLRequestTest.php +++ b/tests/system/HTTP/CURLRequestTest.php @@ -814,6 +814,20 @@ public function testResponseHeaders() $this->assertEquals(234, $response->getStatusCode()); } + public function testResponseHeadersShortProtocol() + { + $request = $this->getRequest([ + 'base_uri' => 'http://www.foo.com/api/v1/', + 'delay' => 1000, + ]); + + $request->setOutput("HTTP/2 235 Ohoh\x0d\x0aAccept: text/html\x0d\x0a\x0d\x0aHi there shortie"); + $response = $request->get('bogus'); + + $this->assertEquals('2.0', $response->getProtocolVersion()); + $this->assertEquals(235, $response->getStatusCode()); + } + //-------------------------------------------------------------------- public function testPostFormEncoded() From 1fc7c76f60f5caed39f303924f80a8e913256fc9 Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 3 Sep 2020 19:39:58 +0200 Subject: [PATCH 084/328] Cast to float --- system/HTTP/Message.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 4defa29c6d8d..84168e168653 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -380,7 +380,7 @@ public function setProtocolVersion(string $version) } // Make sure that version is in the correct format - $version = number_format($version, 1); + $version = number_format((float) $version, 1); if (! in_array($version, $this->validProtocolVersions, true)) { From 712a9491b40dc7e9b83649a90279d8a693099d65 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Wed, 2 Sep 2020 20:23:58 +0800 Subject: [PATCH 085/328] Add phpstan to pre-commit --- admin/pre-commit | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/admin/pre-commit b/admin/pre-commit index c655cd04fe25..2d8b61166d92 100644 --- a/admin/pre-commit +++ b/admin/pre-commit @@ -1,7 +1,7 @@ #!/bin/sh PROJECT=`php -r "echo dirname(dirname(dirname(realpath('$0'))));"` -STAGED_FILES_CMD=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php` +STAGED_FILES_CMD=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php$` # Determine if a file list is passed if [ "$#" -eq 1 ] @@ -14,17 +14,35 @@ then fi SFILES=${SFILES:-$STAGED_FILES_CMD} -echo "Checking PHP Lint..." -for FILE in $SFILES -do - php -l -d display_errors=0 "$PROJECT/$FILE" +echo "Starting CodeIgniter precommit..." + +if [ "$SFILES" != "" ] +then + echo "Linting PHP code..." + for FILE in $SFILES + do + php -l -d display_errors=0 "$PROJECT/$FILE" + if [ $? != 0 ] + then + echo "Fix the error(s) before commit." + exit 1 + fi + FILES="$FILES $FILE" + done +fi + +if [ "$FILES" != "" ] +then + echo "Running PHPStan..." + # Run on whole codebase + ./vendor/bin/phpstan analyse + if [ $? != 0 ] then - echo "Fix the error before commit." + echo "Fix the phpstan error(s) before commit." exit 1 fi - FILES="$FILES $FILE" -done +fi if [ "$FILES" != "" ] then From b3bd451ddf07d4f85c4331ac4292873e077d46a0 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 4 Sep 2020 17:46:29 +0800 Subject: [PATCH 086/328] Cache composer cache directory [ci skip] --- .github/workflows/test-phpstan.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml index 314b872c2217..899b24a69727 100644 --- a/.github/workflows/test-phpstan.yml +++ b/.github/workflows/test-phpstan.yml @@ -32,8 +32,25 @@ jobs: php-version: '7.4' extensions: intl - - name: Install dependencies - run: composer install --no-progress --no-suggest --no-interaction + - name: Use latest Composer + run: composer self-update + + - name: Validate composer.json + run: composer validate --strict + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Create composer cache directory + run: mkdir -p ${{ steps.composer-cache.outputs.dir }} + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- - name: Create PHPStan result cache directory run: mkdir -p build/phpstan @@ -45,5 +62,8 @@ jobs: key: ${{ runner.os }}-phpstan-${{ github.sha }} restore-keys: ${{ runner.os }}-phpstan- + - name: Install dependencies + run: composer install --ansi --no-progress --no-suggest --no-interaction + - name: Run static analysis run: vendor/bin/phpstan analyse From c2426d8c276566000798088a1fd2c74e8d10040c Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Thu, 3 Sep 2020 01:34:41 +0800 Subject: [PATCH 087/328] Add XML tags normaliser --- system/Format/XMLFormatter.php | 42 ++++++++++------ tests/system/Format/XMLFormatterTest.php | 62 ++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/system/Format/XMLFormatter.php b/system/Format/XMLFormatter.php index 8dc798d9199a..dfebae6281e0 100644 --- a/system/Format/XMLFormatter.php +++ b/system/Format/XMLFormatter.php @@ -47,7 +47,6 @@ */ class XMLFormatter implements FormatterInterface { - /** * Takes the given data and formats it. * @@ -77,8 +76,6 @@ public function format($data) return $output->asXML(); } - //-------------------------------------------------------------------- - /** * A recursive method to convert an array into a valid XML string. * @@ -95,25 +92,42 @@ protected function arrayToXML(array $data, &$output) { if (is_array($value)) { - if (is_numeric($key)) - { - $key = "item{$key}"; - } - + $key = $this->normalizeXMLTag($key); $subnode = $output->addChild("$key"); $this->arrayToXML($value, $subnode); } else { - if (is_numeric($key)) - { - $key = "item{$key}"; - } - + $key = $this->normalizeXMLTag($key); $output->addChild("$key", htmlspecialchars("$value")); } } } - //-------------------------------------------------------------------- + /** + * Normalizes tags into the allowed by W3C. + * Regex adopted from this StackOverflow answer. + * + * @param string|integer $key + * + * @return string + * + * @see https://stackoverflow.com/questions/60001029/invalid-characters-in-xml-tag-name + */ + protected function normalizeXMLTag($key) + { + $startChar = 'A-Z_a-z' . + '\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}' . + '\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}' . + '\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}' . + '\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}'; + $validName = $startChar . '\\.\\d\\x{B7}\\x{300}-\\x{36F}\\x{203F}-\\x{2040}'; + + $key = trim($key); + $key = preg_replace("/[^{$validName}-]+/u", '', $key); + $key = preg_replace("/^[^{$startChar}]+/u", 'item$0', $key); + $key = preg_replace('/^[xml]+/iu', 'item$0', $key); + + return $key; + } } diff --git a/tests/system/Format/XMLFormatterTest.php b/tests/system/Format/XMLFormatterTest.php index c7d521ae5c55..bfd60d94a67c 100644 --- a/tests/system/Format/XMLFormatterTest.php +++ b/tests/system/Format/XMLFormatterTest.php @@ -66,4 +66,66 @@ public function testStringFormatting() $this->assertEquals($expected, $this->xmlFormatter->format($data)); } + + public function testValidatingXmlTags() + { + $data = [ + 'BBB096630BD' => 'foo', + '096630FR' => 'bar', + ]; + $expected = << +foobar + +EOH; + + $this->assertEquals($expected, $this->xmlFormatter->format($data)); + } + + /** + * @param string $expected + * @param array $input + * + * @dataProvider invalidTagsProvider + */ + public function testValidatingInvalidTags(string $expected, array $input) + { + $expectedXML = << +<{$expected}>bar + +EOH; + + $this->assertEquals($expectedXML, $this->xmlFormatter->format($input)); + } + + public function invalidTagsProvider() + { + return [ + [ + 'foo', + [' foo ' => 'bar'], + ], + [ + 'foobar', + ['foo:bar' => 'bar'], + ], + [ + 'foobar', + ['foo bar' => 'bar'], + ], + [ + 'itemxml', + ['xml' => 'bar'], + ], + [ + 'itemXML', + ['XML' => 'bar'], + ], + [ + 'itemXml', + ['Xml' => 'bar'], + ], + ]; + } } From 4c3c1db39ba1366b086db893c4c08aa1d7155054 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 4 Sep 2020 17:35:36 +0800 Subject: [PATCH 088/328] Add support for deep nested array --- system/Format/XMLFormatter.php | 4 +- tests/system/Format/XMLFormatterTest.php | 95 ++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/system/Format/XMLFormatter.php b/system/Format/XMLFormatter.php index dfebae6281e0..55e5b27587a9 100644 --- a/system/Format/XMLFormatter.php +++ b/system/Format/XMLFormatter.php @@ -71,7 +71,7 @@ public function format($data) $options = $config->formatterOptions['application/xml'] ?? 0; $output = new \SimpleXMLElement('', $options); - $this->arrayToXML((array)$data, $output); + $this->arrayToXML((array) $data, $output); return $output->asXML(); } @@ -126,7 +126,7 @@ protected function normalizeXMLTag($key) $key = trim($key); $key = preg_replace("/[^{$validName}-]+/u", '', $key); $key = preg_replace("/^[^{$startChar}]+/u", 'item$0', $key); - $key = preg_replace('/^[xml]+/iu', 'item$0', $key); + $key = preg_replace('/^(xml).*/iu', 'item$0', $key); // XML is a reserved starting word return $key; } diff --git a/tests/system/Format/XMLFormatterTest.php b/tests/system/Format/XMLFormatterTest.php index bfd60d94a67c..09b53c8a9fd0 100644 --- a/tests/system/Format/XMLFormatterTest.php +++ b/tests/system/Format/XMLFormatterTest.php @@ -115,17 +115,100 @@ public function invalidTagsProvider() ['foo bar' => 'bar'], ], [ - 'itemxml', - ['xml' => 'bar'], + 'itemxmltest', + ['xmltest' => 'bar'], ], [ - 'itemXML', - ['XML' => 'bar'], + 'itemXMLtest', + ['XMLtest' => 'bar'], ], [ - 'itemXml', - ['Xml' => 'bar'], + 'itemXmltest', + ['Xmltest' => 'bar'], ], ]; } + + public function testDeepNestedArrayToXml() + { + $data = [ + 'data' => [ + 'master' => [ + 'name' => 'Foo', + 'email' => 'foo@bar.com', + 'dependents' => [], + ], + 'vote' => [ + 'list' => [], + ], + 'user' => [ + 'account' => [ + 'demo' => [ + 'info' => [ + 'is_banned' => 'true', + 'last_login' => '2020-08-31', + 'last_login_ip' => '127.0.0.1', + ], + ], + ], + ], + 'xml' => [ + 'xml_version' => '1.0', + 'xml_encoding' => 'utf-8', + ], + [ + 'misc' => 'miscellaneous', + ], + [ + 'misc_data' => 'miscellaneous data', + ], + ], + ]; + + // do not change to tabs!! + $expectedXML = << + + + + Foo + foo@bar.com + + + + + + + + + + true + 2020-08-31 + 127.0.0.1 + + + + + + 1.0 + utf-8 + + + miscellaneous + + + miscellaneous data + + + + +EOF; + + $dom = new \DOMDocument('1.0'); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->loadXML($this->xmlFormatter->format($data)); + + $this->assertEquals($expectedXML, $dom->saveXML()); + } } From 18eb0d39b1e65e635441ff20b8a04ec0cee8796e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 5 Sep 2020 15:40:04 +0700 Subject: [PATCH 089/328] using EXIT_ERROR constant in CodeIgniter::bootstrapEnvironment() --- system/CodeIgniter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 5be87374a51c..bd4972c0a11c 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -556,7 +556,7 @@ protected function bootstrapEnvironment() // @codeCoverageIgnoreStart header('HTTP/1.1 503 Service Unavailable.', true, 503); echo 'The application environment is not set correctly.'; - exit(1); // EXIT_ERROR + exit(EXIT_ERROR); // EXIT_ERROR // @codeCoverageIgnoreEnd } } From 94d99d48023716fd0bbff94d640e7811151116ec Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 6 Sep 2020 12:02:26 +0700 Subject: [PATCH 090/328] Fix @param at URI::stripQuery(...$params) --- system/HTTP/URI.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 00ee8f0f2c51..35ed896d1b03 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -876,7 +876,7 @@ public function addQuery(string $key, $value = null) /** * Removes one or more query vars from the URI. * - * @param array ...$params + * @param string ...$params * * @return $this */ @@ -884,7 +884,7 @@ public function stripQuery(...$params) { foreach ($params as $param) { - unset($this->query[$param]); // @phpstan-ignore-line + unset($this->query[$param]); } return $this; From d66c1d375c4ca863af8d9c52d61512cab0c73699 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sat, 5 Sep 2020 21:53:41 +0200 Subject: [PATCH 091/328] Remove all code and docs related to the database cache feature --- app/Config/Database.php | 4 ---- system/Database/BaseConnection.php | 14 -------------- tests/system/Database/BaseConnectionTest.php | 6 ------ tests/system/Database/ConfigTest.php | 8 -------- tests/travis/Database.php | 6 ------ user_guide_src/source/database/configuration.rst | 10 ---------- user_guide_src/source/database/connecting.rst | 2 -- user_guide_src/source/libraries/sessions.rst | 1 - 8 files changed, 51 deletions(-) diff --git a/app/Config/Database.php b/app/Config/Database.php index b31541a83bcd..530634dace61 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -39,8 +39,6 @@ class Database extends \CodeIgniter\Database\Config 'DBPrefix' => '', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -67,8 +65,6 @@ class Database extends \CodeIgniter\Database\Config 'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index aeb72daeb0d8..c7a3bb202cb6 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -128,20 +128,6 @@ abstract class BaseConnection implements ConnectionInterface */ protected $DBDebug = false; - /** - * Should we cache results? - * - * @var boolean - */ - protected $cacheOn = false; - - /** - * Path to store cache files. - * - * @var string - */ - protected $cacheDir; - /** * Character set * diff --git a/tests/system/Database/BaseConnectionTest.php b/tests/system/Database/BaseConnectionTest.php index d05925122fe7..ca0f6b5adf87 100644 --- a/tests/system/Database/BaseConnectionTest.php +++ b/tests/system/Database/BaseConnectionTest.php @@ -14,8 +14,6 @@ class BaseConnectionTest extends \CodeIgniter\Test\CIUnitTestCase 'DBPrefix' => 'test_', 'pConnect' => true, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => 'my/cacheDir', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -35,8 +33,6 @@ class BaseConnectionTest extends \CodeIgniter\Test\CIUnitTestCase 'DBPrefix' => 'test_', 'pConnect' => true, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => 'my/cacheDir', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -59,8 +55,6 @@ public function testSavesConfigOptions() $this->assertSame('MockDriver', $db->DBDriver); $this->assertTrue($db->pConnect); $this->assertTrue($db->DBDebug); - $this->assertFalse($db->cacheOn); - $this->assertSame('my/cacheDir', $db->cacheDir); $this->assertSame('utf8', $db->charset); $this->assertSame('utf8_general_ci', $db->DBCollat); $this->assertSame('', $db->swapPre); diff --git a/tests/system/Database/ConfigTest.php b/tests/system/Database/ConfigTest.php index f7fcf71f8ede..de38a5a4c0e0 100644 --- a/tests/system/Database/ConfigTest.php +++ b/tests/system/Database/ConfigTest.php @@ -19,8 +19,6 @@ class DatabaseConfig extends \CodeIgniter\Test\CIUnitTestCase 'DBPrefix' => 'test_', 'pConnect' => true, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => 'my/cacheDir', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -41,8 +39,6 @@ class DatabaseConfig extends \CodeIgniter\Test\CIUnitTestCase 'DBPrefix' => 't_', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => 'my/cacheDir', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -63,8 +59,6 @@ class DatabaseConfig extends \CodeIgniter\Test\CIUnitTestCase 'DBPrefix' => 't_', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => 'my/cacheDir', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -85,8 +79,6 @@ class DatabaseConfig extends \CodeIgniter\Test\CIUnitTestCase 'DBPrefix' => 't_', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => 'my/cacheDir', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', diff --git a/tests/travis/Database.php b/tests/travis/Database.php index 943e70bc51b1..93a618013e0c 100644 --- a/tests/travis/Database.php +++ b/tests/travis/Database.php @@ -12,8 +12,6 @@ 'DBPrefix' => 'db_', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -33,8 +31,6 @@ 'DBPrefix' => 'db_', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -54,8 +50,6 @@ 'DBPrefix' => 'db_', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index ebea2b5eea38..ffe0bdeed842 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -24,8 +24,6 @@ prototype:: 'DBPrefix' => '', 'pConnect' => TRUE, 'DBDebug' => TRUE, - 'cacheOn' => FALSE, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -84,8 +82,6 @@ These failovers can be specified by setting the failover for a connection like t 'DBPrefix' => '', 'pConnect' => TRUE, 'DBDebug' => TRUE, - 'cacheOn' => FALSE, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -102,8 +98,6 @@ These failovers can be specified by setting the failover for a connection like t 'DBPrefix' => '', 'pConnect' => TRUE, 'DBDebug' => TRUE, - 'cacheOn' => FALSE, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -131,8 +125,6 @@ example, to set up a "test" environment you would do this:: 'DBPrefix' => '', 'pConnect' => TRUE, 'DBDebug' => TRUE, - 'cacheOn' => FALSE, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', @@ -198,8 +190,6 @@ Explanation of Values: installations to share one database. **pConnect** TRUE/FALSE (boolean) - Whether to use a persistent connection. **DBDebug** TRUE/FALSE (boolean) - Whether database errors should be displayed. -**cacheOn** TRUE/FALSE (boolean) - Whether database query caching is enabled. -**cacheDir** The absolute server path to your database query cache directory. **charset** The character set used in communicating with the database. **DBCollat** The character collation used in communicating with the database diff --git a/user_guide_src/source/database/connecting.rst b/user_guide_src/source/database/connecting.rst index 6b53071eddf2..1839f2265812 100644 --- a/user_guide_src/source/database/connecting.rst +++ b/user_guide_src/source/database/connecting.rst @@ -83,8 +83,6 @@ the same format as the groups are defined in the configuration file:: 'DBPrefix' => '', 'pConnect' => false, 'DBDebug' => (ENVIRONMENT !== 'production'), - 'cacheOn' => false, - 'cacheDir' => '', 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst index 9f2655e0cbf9..6e0620467003 100644 --- a/user_guide_src/source/libraries/sessions.rst +++ b/user_guide_src/source/libraries/sessions.rst @@ -568,7 +568,6 @@ an application - it is just another table in your database. However, there are some conditions that must be met: - You can NOT use a persistent connection. - - You can NOT use a connection with the *cacheOn* setting enabled. In order to use the 'DatabaseHandler' session driver, you must also create this table that we already mentioned and then set it as your From f8bb13f89a670872f8be018573e44f8e2f095f40 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sat, 5 Sep 2020 11:14:31 +0200 Subject: [PATCH 092/328] URI class update for better handling of URLs with sub-folders --- system/HTTP/URI.php | 14 +++++- system/Helpers/url_helper.php | 10 ----- tests/system/Helpers/URLHelperTest.php | 62 ++++++++++++++++---------- 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 35ed896d1b03..915002690f1a 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -593,8 +593,20 @@ public function getTotalSegments(): int */ public function __toString(): string { + // If hosted in a sub-folder, we will have additional + // segments that show up prior to the URI path we just + // grabbed from the request, so add it on if necessary. + $baseUri = new URI(config(\Config\App::class)->baseURL); + $basePath = trim($baseUri->getPath(), '/') . '/'; + $path = $this->getPath(); + + if ($basePath !== '/' && strpos($path, $basePath) !== 0) + { + $path = $basePath . $path; + } + return static::createURIString( - $this->getScheme(), $this->getAuthority(), $this->getPath(), // Absolute URIs should use a "/" for an empty path + $this->getScheme(), $this->getAuthority(), $path, // Absolute URIs should use a "/" for an empty path $this->getQuery(), $this->getFragment() ); } diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index 0ad9cfad6a28..562f98b4bcb1 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -163,16 +163,6 @@ function current_url(bool $returnObject = false) { $uri = clone \Config\Services::request()->uri; - // If hosted in a sub-folder, we will have additional - // segments that show up prior to the URI path we just - // grabbed from the request, so add it on if necessary. - $baseUri = new \CodeIgniter\HTTP\URI(config(\Config\App::class)->baseURL); - - if (! empty($baseUri->getPath())) - { - $uri->setHost($baseUri->getHost() . rtrim($baseUri->getPath(), '/ ') . '/'); - } - // Since we're basing off of the IncomingRequest URI, // we are guaranteed to have a host based on our own configs. return $returnObject ? $uri : (string) $uri->setQuery(''); diff --git a/tests/system/Helpers/URLHelperTest.php b/tests/system/Helpers/URLHelperTest.php index 5815d34edfd4..c223706dac96 100644 --- a/tests/system/Helpers/URLHelperTest.php +++ b/tests/system/Helpers/URLHelperTest.php @@ -1,6 +1,7 @@ assertEquals('http://example.com/index.php/news/local/123', site_url(['news', 'local', '123'], null, $config)); } - public function testSiteURLInSubfolder() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/foo/public/bar?baz=quip'; - - // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/foo/public'; - $request = Services::request($config); - $request->uri = (new URI())->setScheme('http://')->setHost('example.com/foo/public/')->setPath('bar'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/foo/public/bar', current_url()); - - $uri = current_url(true); - $this->assertEquals(['bar'], $uri->getSegments()); - $this->assertEquals('bar', $uri->getSegment(1)); - $this->assertEquals('example.com/foo/public/', $uri->getHost()); - $this->assertEquals('http', $uri->getScheme()); - } - /** * @see https://github.com/codeigniter4/CodeIgniter4/issues/240 */ @@ -420,16 +400,50 @@ public function testCurrentURLInSubfolder() { $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/foo/public/bar?baz=quip'; + $_SERVER['SCRIPT_NAME'] = '/foo/public/index.php'; // Since we're on a CLI, we must provide our own URI $config = new App(); $config->baseURL = 'http://example.com/foo/public'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/foo/public/bar'); + Config::injectMock('App', $config); + $request = Services::request($config); Services::injectMock('request', $request); $this->assertEquals('http://example.com/foo/public/bar', current_url()); + $this->assertEquals('http://example.com/foo/public/bar?baz=quip', (string) current_url(true)); + + $uri = current_url(true); + $this->assertEquals(['bar'], $uri->getSegments()); + $this->assertEquals('bar', $uri->getSegment(1)); + $this->assertEquals('example.com', $uri->getHost()); + $this->assertEquals('http', $uri->getScheme()); + } + + public function testCurrentURLWithPortInSubfolder() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['SERVER_PORT'] = '8080'; + $_SERVER['REQUEST_URI'] = '/foo/public/bar?baz=quip'; + $_SERVER['SCRIPT_NAME'] = '/foo/public/index.php'; + + // Since we're on a CLI, we must provide our own URI + $config = new App(); + $config->baseURL = 'http://example.com:8080/foo/public'; + Config::injectMock('App', $config); + + $request = Services::request($config); + Services::injectMock('request', $request); + + $this->assertEquals('http://example.com:8080/foo/public/bar', current_url()); + $this->assertEquals('http://example.com:8080/foo/public/bar?baz=quip', (string) current_url(true)); + + $uri = current_url(true); + $this->assertEquals(['bar'], $uri->getSegments()); + $this->assertEquals('bar', $uri->getSegment(1)); + $this->assertEquals('example.com', $uri->getHost()); + $this->assertEquals('http', $uri->getScheme()); + $this->assertEquals('8080', $uri->getPort()); } //-------------------------------------------------------------------- From ca54fb3caf90bcb5b245cf1aa3d8f0ff1a002698 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sat, 5 Sep 2020 15:05:26 +0200 Subject: [PATCH 093/328] Parsing path changes --- system/HTTP/URI.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 915002690f1a..2056afa64f4c 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -596,11 +596,11 @@ public function __toString(): string // If hosted in a sub-folder, we will have additional // segments that show up prior to the URI path we just // grabbed from the request, so add it on if necessary. - $baseUri = new URI(config(\Config\App::class)->baseURL); - $basePath = trim($baseUri->getPath(), '/') . '/'; + $baseUri = new self(config(\Config\App::class)->baseURL); + $basePath = rtrim($baseUri->getPath(), '/') . '/'; $path = $this->getPath(); - if ($basePath !== '/' && strpos($path, $basePath) !== 0) + if (! empty($basePath) && $basePath !== '/' && strpos($path, $basePath) !== 0) { $path = $basePath . $path; } From 6d06b43ab8e062b0285ff7468a939660c7cb470d Mon Sep 17 00:00:00 2001 From: Michal Sniatala Date: Sun, 6 Sep 2020 07:50:57 +0200 Subject: [PATCH 094/328] Update system/HTTP/URI.php Co-authored-by: Abdul Malik Ikhsan --- system/HTTP/URI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 2056afa64f4c..d536f4cf374d 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -600,7 +600,7 @@ public function __toString(): string $basePath = rtrim($baseUri->getPath(), '/') . '/'; $path = $this->getPath(); - if (! empty($basePath) && $basePath !== '/' && strpos($path, $basePath) !== 0) + if ($basePath !== '/' && strpos($path, $basePath) !== 0) { $path = $basePath . $path; } From 8603cd9f27e5715928968d1649d1c28d13bfd742 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Wed, 2 Sep 2020 19:39:42 +0800 Subject: [PATCH 095/328] Use view files for generator commands --- .../Commands/Generators/CreateMigration.php | 29 ++-------- system/Commands/Generators/CreateSeeder.php | 22 ++------ .../Generators/CreateSessionMigration.php | 5 +- .../Generators/Views/migration.tpl.php | 40 +------------- system/Commands/Generators/Views/seed.tpl.php | 13 +++++ .../Views/session_migration.tpl.php | 54 +++++++++++++++++++ 6 files changed, 80 insertions(+), 83 deletions(-) create mode 100644 system/Commands/Generators/Views/seed.tpl.php create mode 100644 system/Commands/Generators/Views/session_migration.tpl.php diff --git a/system/Commands/Generators/CreateMigration.php b/system/Commands/Generators/CreateMigration.php index 4c4b57b93fb4..8a46be5531b3 100644 --- a/system/Commands/Generators/CreateMigration.php +++ b/system/Commands/Generators/CreateMigration.php @@ -87,11 +87,10 @@ class CreateMigration extends GeneratorCommand protected function getClassName(): string { $class = parent::getClassName(); + if (empty($class)) { - // @codeCoverageIgnoreStart - $class = CLI::prompt(lang('Migrations.nameMigration'), null, 'required'); - // @codeCoverageIgnoreEnd + $class = CLI::prompt(lang('Migrations.nameMigration'), null, 'required'); // @codeCoverageIgnore } return $class; @@ -122,28 +121,8 @@ protected function modifyBasename(string $filename): string */ protected function getTemplate(): string { - return << false]); -EOD; + return str_replace('<@php', ' false]); -EOD; + return str_replace('<@php', ' config('App')->sessionMatchIP ?? false, ]; - $template = view('\\CodeIgniter\\Commands\\Generators\\Views\\migration.tpl.php', $data, ['debug' => false]); - return str_replace('@php', '?php', $template); + $template = view('CodeIgniter\\Commands\\Generators\\Views\\session_migration.tpl.php', $data, ['debug' => false]); + + return str_replace('<@php', ' - protected $DBGroup = ''; - - public function up() { - $this->forge->addField([ - 'id' => [ - 'type' => 'VARCHAR', - 'constraint' => 128, - 'null' => false, - ], - 'ip_address' => [ - 'type' => 'VARCHAR', - 'constraint' => 45, - 'null' => false, - ], - 'timestamp' => [ - 'type' => 'INT', - 'constraint' => 10, - 'unsigned' => true, - 'null' => false, - 'default' => 0, - ], - 'data' => [ - 'type' => 'TEXT', - 'null' => false, - 'default' => '', - ], - ]); - - $this->forge->addKey(['id', 'ip_address'], true); - - $this->forge->addKey('id', true); - - $this->forge->addKey('timestamp'); - $this->forge->createTable('', true); + // } - //-------------------------------------------------------------------- - public function down() { - $this->forge->dropTable('', true); + // } } diff --git a/system/Commands/Generators/Views/seed.tpl.php b/system/Commands/Generators/Views/seed.tpl.php new file mode 100644 index 000000000000..bf80c7a7ad9d --- /dev/null +++ b/system/Commands/Generators/Views/seed.tpl.php @@ -0,0 +1,13 @@ +<@php + +namespace {namespace}; + +use CodeIgniter\Database\Seeder; + +class {class} extends Seeder +{ + public function run() + { + // + } +} diff --git a/system/Commands/Generators/Views/session_migration.tpl.php b/system/Commands/Generators/Views/session_migration.tpl.php new file mode 100644 index 000000000000..9bcef3dbc552 --- /dev/null +++ b/system/Commands/Generators/Views/session_migration.tpl.php @@ -0,0 +1,54 @@ +<@php + +namespace {namespace}; + +use CodeIgniter\Database\Migration; + +class {class} extends Migration +{ + + protected $DBGroup = ''; + + + public function up() + { + $this->forge->addField([ + 'id' => [ + 'type' => 'VARCHAR', + 'constraint' => 128, + 'null' => false, + ], + 'ip_address' => [ + 'type' => 'VARCHAR', + 'constraint' => 45, + 'null' => false, + ], + 'timestamp' => [ + 'type' => 'INT', + 'constraint' => 10, + 'unsigned' => true, + 'null' => false, + 'default' => 0, + ], + 'data' => [ + 'type' => 'TEXT', + 'null' => false, + 'default' => '', + ], + ]); + + $this->forge->addKey(['id', 'ip_address'], true); + + $this->forge->addKey('id', true); + + $this->forge->addKey('timestamp'); + $this->forge->createTable('', true); + } + + //-------------------------------------------------------------------- + + public function down() + { + $this->forge->dropTable('', true); + } +} From 9735525a20fba452a0b61a9c0d8871b27467ebf0 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 5 Sep 2020 22:26:31 +0800 Subject: [PATCH 096/328] Add config files for Generators --- app/Config/Generators.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 app/Config/Generators.php diff --git a/app/Config/Generators.php b/app/Config/Generators.php new file mode 100644 index 000000000000..c9add8c8fe76 --- /dev/null +++ b/app/Config/Generators.php @@ -0,0 +1,38 @@ + + */ + public $views = [ + 'make:command' => 'CodeIgniter\\Commands\\Generators\\Views\\command.tpl.php', + 'make:controller' => 'CodeIgniter\\Commands\\Generators\\Views\\controller.tpl.php', + 'make:entity' => 'CodeIgniter\\Commands\\Generators\\Views\\entity.tpl.php', + 'make:filter' => 'CodeIgniter\\Commands\\Generators\\Views\\filter.tpl.php', + 'migrate:create' => 'CodeIgniter\\Commands\\Generators\\Views\\migration.tpl.php', + 'make:model' => 'CodeIgniter\\Commands\\Generators\\Views\\model.tpl.php', + 'make:seeder' => 'CodeIgniter\\Commands\\Generators\\Views\\seed.tpl.php', + 'session:migration' => 'CodeIgniter\\Commands\\Generators\\Views\\session_migration.tpl.php', + ]; +} From d35546052a135166bab6b6b867a73ef7f4d7b9db Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 5 Sep 2020 22:30:06 +0800 Subject: [PATCH 097/328] Allow generator commands' views to be configurable --- system/CLI/GeneratorCommand.php | 37 +++++++++++++- system/Commands/Generators/CreateCommand.php | 14 ++++- .../Commands/Generators/CreateController.php | 23 +++++++-- system/Commands/Generators/CreateEntity.php | 14 ++++- system/Commands/Generators/CreateFilter.php | 51 ++++++++++++++++++- .../Commands/Generators/CreateMigration.php | 22 +++----- system/Commands/Generators/CreateModel.php | 27 ++++++++-- system/Commands/Generators/CreateScaffold.php | 10 ++++ system/Commands/Generators/CreateSeeder.php | 2 +- .../Generators/CreateSessionMigration.php | 15 +++++- 10 files changed, 186 insertions(+), 29 deletions(-) diff --git a/system/CLI/GeneratorCommand.php b/system/CLI/GeneratorCommand.php index 0b0ba192ade8..a22c7001f9f5 100644 --- a/system/CLI/GeneratorCommand.php +++ b/system/CLI/GeneratorCommand.php @@ -84,6 +84,13 @@ abstract class GeneratorCommand extends BaseCommand */ protected $params = []; + /** + * Instance of Config\Generators + * + * @var \Config\Generators + */ + protected $config; + /** * Constructor. * @@ -94,6 +101,7 @@ public function __construct(LoggerInterface $logger, Commands $commands) { $this->arguments = array_merge($this->defaultArguments, $this->arguments); $this->options = array_merge($this->options, $this->defaultOptions); + $this->config = config('Config\Generators'); parent::__construct($logger, $commands); } @@ -120,11 +128,13 @@ public function run(array $params) { CLI::error(lang('CLI.generateFileExists', [clean_path($path)]), 'light_gray', 'red'); CLI::newLine(); + return; } // Next, check if the directory to save the file is existing. $dir = dirname($path); + if (! is_dir($dir)) { mkdir($dir, 0755, true); @@ -137,6 +147,7 @@ public function run(array $params) { CLI::error(lang('CLI.generateFileError') . clean_path($path), 'light_gray', 'red'); CLI::newLine(); + return; } @@ -232,12 +243,13 @@ protected function buildPath(string $class): string // Check if the namespace is actually defined and we are not just typing gibberish. $base = Services::autoloader()->getNamespace($root); + if (! $base = reset($base)) { throw new \RuntimeException(lang('CLI.namespaceNotDefined', [$root])); } - $base = realpath($base) ?: $base; + $base = realpath($base) ?: $base; $path = $base . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $name) . '.php'; $filename = $this->modifyBasename(basename($path)); @@ -336,4 +348,27 @@ protected function sortImports(string $template): string return $template; // @codeCoverageIgnore } + + /** + * Gets a generator view as defined in the `Config\Generators::$views`, + * with fallback to `$default` when the defined view does not exist. + * + * @param string $default Path to the fallback view. + * @param array $data Data to be passed to the view. + * + * @return string + */ + protected function getGeneratorViewFile(string $default, array $data = []): string + { + try + { + return view($this->config->views[$this->name], $data, ['debug' => false]); + } + catch (\Throwable $e) + { + log_message('error', $e->getMessage()); + + return view($default, $data, ['debug' => false]); + } + } } diff --git a/system/Commands/Generators/CreateCommand.php b/system/Commands/Generators/CreateCommand.php index dd4583830284..73b0a52040fb 100644 --- a/system/Commands/Generators/CreateCommand.php +++ b/system/Commands/Generators/CreateCommand.php @@ -85,6 +85,9 @@ class CreateCommand extends GeneratorCommand '-type' => 'Type of command. Whether a basic command or a generator command. Defaults to "basic".', ]; + /** + * {@inheritDoc} + */ protected function getClassName(): string { $className = parent::getClassName(); @@ -97,18 +100,27 @@ protected function getClassName(): string return $className; } + /** + * {@inheritDoc} + */ protected function getNamespacedClass(string $rootNamespace, string $class): string { return $rootNamespace . '\\Commands\\' . $class; } + /** + * {@inheritDoc} + */ protected function getTemplate(): string { - $template = view('CodeIgniter\\Commands\\Generators\\Views\\command.tpl.php', [], ['debug' => false]); + $template = $this->getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\command.tpl.php'); return str_replace('<@php', ' 'Extends from a RESTful resource. Options are \'controller\' or \'presenter\'.', ]; + /** + * {@inheritDoc} + */ protected function getClassName(): string { $className = parent::getClassName(); @@ -99,19 +102,27 @@ protected function getClassName(): string return $className; } + /** + * {@inheritDoc} + */ protected function getNamespacedClass(string $rootNamespace, string $class): string { return $rootNamespace . '\\Controllers\\' . $class; } + /** + * {@inheritDoc} + */ protected function getTemplate(): string { - $template = view('CodeIgniter\\Commands\\Generators\\Views\\controller.tpl.php', [], ['debug' => false]); - $template = str_replace('<@php', 'getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\controller.tpl.php'); - return $template; + return str_replace('<@php', 'params) || CLI::getOption('bare'); @@ -193,6 +204,12 @@ protected function getParentClass(?bool $bare, $rest): array ]; } + /** + * If the controller extends any RESTful controller, this will provide + * the additional REST API methods. + * + * @return string + */ protected function getAdditionalRestfulMethods(): string { return <<<'EOF' diff --git a/system/Commands/Generators/CreateEntity.php b/system/Commands/Generators/CreateEntity.php index d1a526108c71..8aaf6a9a8824 100644 --- a/system/Commands/Generators/CreateEntity.php +++ b/system/Commands/Generators/CreateEntity.php @@ -42,6 +42,9 @@ use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\GeneratorCommand; +/** + * Creates a skeleton Entity file. + */ class CreateEntity extends GeneratorCommand { /** @@ -74,6 +77,9 @@ class CreateEntity extends GeneratorCommand 'name' => 'The model class name', ]; + /** + * {@inheritDoc} + */ protected function getClassName(): string { $className = parent::getClassName(); @@ -86,14 +92,20 @@ protected function getClassName(): string return $className; } + /** + * {@inheritDoc} + */ protected function getNamespacedClass(string $rootNamespace, string $class): string { return $rootNamespace . '\\Entities\\' . $class; } + /** + * {@inheritDoc} + */ protected function getTemplate(): string { - $template = view('CodeIgniter\\Commands\\Generators\\Views\\entity.tpl.php', [], ['debug' => false]); + $template = $this->getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\entity.tpl.php'); return str_replace('<@php', ' 'The filter class name', ]; + /** + * {@inheritDoc} + */ protected function getClassName(): string { $className = parent::getClassName(); @@ -49,14 +92,20 @@ protected function getClassName(): string return $className; } + /** + * {@inheritDoc} + */ protected function getNamespacedClass(string $rootNamespace, string $class): string { return $rootNamespace . '\\Filters\\' . $class; } + /** + * {@inheritDoc} + */ protected function getTemplate(): string { - $template = view('CodeIgniter\\Commands\\Generators\\Views\\filter.tpl.php', [], ['debug' => false]); + $template = $this->getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\filter.tpl.php'); return str_replace('<@php', 'timestampFormat) . $filename; } /** - * Gets the template for this class. - * - * @return string + * {@inheritDoc} */ protected function getTemplate(): string { - $template = view('CodeIgniter\\Commands\\Generators\\Views\\migration.tpl.php', [], ['debug' => false]); + $template = $this->getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\migration.tpl.php'); return str_replace('<@php', ' 'Supply a different table name. Defaults to the pluralized name.', ]; + /** + * {@inheritDoc} + */ protected function getClassName(): string { $className = parent::getClassName(); @@ -97,30 +103,39 @@ protected function getClassName(): string return $className; } + /** + * {@inheritDoc} + */ protected function getNamespacedClass(string $rootNamespace, string $class): string { return $rootNamespace . '\\Models\\' . $class; } + /** + * {@inheritDoc} + */ protected function getTemplate(): string { $dbgroup = $this->params['dbgroup'] ?? CLI::getOption('dbgroup'); + if (! is_string($dbgroup)) { $dbgroup = 'default'; } - $template = view('CodeIgniter\\Commands\\Generators\\Views\\model.tpl.php', [], ['debug' => false]); - $template = str_replace(['<@php', '{dbgroup}'], ['getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\model.tpl.php'); - return $template; + return str_replace(['<@php', '{dbgroup}'], ['params) || CLI::getOption('entity'); - $entity = array_key_exists('entity', $this->params) || CLI::getOption('entity'); if (! $entity) { $entity = 'array'; // default to array return @@ -128,15 +143,17 @@ protected function setReplacements(string $template, string $class): string else { $entity = str_replace('\\Models', '\\Entities', $class); + if ($pos = strripos($entity, 'Model')) { // Strip 'Model' from name $entity = substr($entity, 0, $pos); } } + $template = str_replace('{return}', $entity, $template); + $table = $this->params['table'] ?? CLI::getOption('table'); - $table = $this->params['table'] ?? CLI::getOption('table'); if (! is_string($table)) { $table = str_replace($this->getNamespace($class) . '\\', '', $class); diff --git a/system/Commands/Generators/CreateScaffold.php b/system/Commands/Generators/CreateScaffold.php index d2166cab2139..52dc1f1100ff 100644 --- a/system/Commands/Generators/CreateScaffold.php +++ b/system/Commands/Generators/CreateScaffold.php @@ -82,6 +82,11 @@ class CreateScaffold extends BaseCommand 'name' => 'The class name', ]; + /** + * The Command's options. + * + * @var array + */ protected $options = [ '-bare' => 'Add the \'-bare\' option to controller scaffold.', '-restful' => 'Add the \'-restful\' option to controller scaffold.', @@ -91,6 +96,9 @@ class CreateScaffold extends BaseCommand '-force' => 'Force overwrite existing files.', ]; + /** + * {@inheritDoc} + */ public function run(array $params) { // Resolve options @@ -107,12 +115,14 @@ public function run(array $params) // Sets additional options $genOptions = ['n' => $namespace]; + if ($force) { $genOptions['force'] = null; } $controllerOpts = []; + if ($bare) { $controllerOpts['bare'] = null; diff --git a/system/Commands/Generators/CreateSeeder.php b/system/Commands/Generators/CreateSeeder.php index cf1ffd73f56d..fe734b2920cf 100644 --- a/system/Commands/Generators/CreateSeeder.php +++ b/system/Commands/Generators/CreateSeeder.php @@ -116,7 +116,7 @@ protected function getNamespacedClass(string $rootNamespace, string $class): str */ protected function getTemplate(): string { - $template = view('CodeIgniter\\Commands\\Generators\\Views\\seed.tpl.php', [], ['debug' => false]); + $template = $this->getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\seed.tpl.php'); return str_replace('<@php', ' 'Set table name', ]; + /** + * {@inheritDoc} + */ protected function getClassName(): string { $tableName = $this->params['t'] ?? CLI::getOption('t') ?? 'ci_sessions'; + return "Migration_create_{$tableName}_table"; } + /** + * {@inheritDoc} + */ protected function getNamespacedClass(string $rootNamespace, string $class): string { return $rootNamespace . '\\Database\\Migrations\\' . $class; } + /** + * {@inheritDoc} + */ protected function modifyBasename(string $filename): string { return str_replace('Migration', gmdate(config('Migrations')->timestampFormat), $filename); } + /** + * {@inheritDoc} + */ protected function getTemplate(): string { $data = [ @@ -104,7 +117,7 @@ protected function getTemplate(): string 'matchIP' => config('App')->sessionMatchIP ?? false, ]; - $template = view('CodeIgniter\\Commands\\Generators\\Views\\session_migration.tpl.php', $data, ['debug' => false]); + $template = $this->getGeneratorViewFile('CodeIgniter\\Commands\\Generators\\Views\\session_migration.tpl.php', $data); return str_replace('<@php', ' Date: Sun, 6 Sep 2020 22:58:33 +0800 Subject: [PATCH 098/328] Deprecate migrate:create for make:migration --- app/Config/Generators.php | 2 +- .../Commands/Generators/CreateMigration.php | 4 +- system/Commands/Generators/CreateScaffold.php | 2 +- system/Commands/Generators/MigrateCreate.php | 121 ++++++++++++++++++ tests/system/Commands/CreateMigrationTest.php | 8 +- user_guide_src/source/changelogs/v4.0.5.rst | 1 + user_guide_src/source/cli/cli_generators.rst | 13 +- user_guide_src/source/dbmgmt/migration.rst | 15 ++- 8 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 system/Commands/Generators/MigrateCreate.php diff --git a/app/Config/Generators.php b/app/Config/Generators.php index c9add8c8fe76..5ea633e5a954 100644 --- a/app/Config/Generators.php +++ b/app/Config/Generators.php @@ -30,7 +30,7 @@ class Generators extends BaseConfig 'make:controller' => 'CodeIgniter\\Commands\\Generators\\Views\\controller.tpl.php', 'make:entity' => 'CodeIgniter\\Commands\\Generators\\Views\\entity.tpl.php', 'make:filter' => 'CodeIgniter\\Commands\\Generators\\Views\\filter.tpl.php', - 'migrate:create' => 'CodeIgniter\\Commands\\Generators\\Views\\migration.tpl.php', + 'make:migration' => 'CodeIgniter\\Commands\\Generators\\Views\\migration.tpl.php', 'make:model' => 'CodeIgniter\\Commands\\Generators\\Views\\model.tpl.php', 'make:seeder' => 'CodeIgniter\\Commands\\Generators\\Views\\seed.tpl.php', 'session:migration' => 'CodeIgniter\\Commands\\Generators\\Views\\session_migration.tpl.php', diff --git a/system/Commands/Generators/CreateMigration.php b/system/Commands/Generators/CreateMigration.php index 8ad643a83719..59e5c3c10047 100644 --- a/system/Commands/Generators/CreateMigration.php +++ b/system/Commands/Generators/CreateMigration.php @@ -52,7 +52,7 @@ class CreateMigration extends GeneratorCommand * * @var string */ - protected $name = 'migrate:create'; + protected $name = 'make:migration'; /** * The Command's short description @@ -66,7 +66,7 @@ class CreateMigration extends GeneratorCommand * * @var string */ - protected $usage = 'migrate:create [options]'; + protected $usage = 'make:migration [options]'; /** * The Command's Arguments diff --git a/system/Commands/Generators/CreateScaffold.php b/system/Commands/Generators/CreateScaffold.php index 52dc1f1100ff..6775a3778cb5 100644 --- a/system/Commands/Generators/CreateScaffold.php +++ b/system/Commands/Generators/CreateScaffold.php @@ -143,7 +143,7 @@ public function run(array $params) $this->call('make:controller', array_merge([$class], $controllerOpts, $genOptions)); $this->call('make:model', array_merge([$class], $modelOpts, $genOptions)); $this->call('make:entity', array_merge([$class], $genOptions)); - $this->call('migrate:create', array_merge([$class], $genOptions)); + $this->call('make:migration', array_merge([$class], $genOptions)); $this->call('make:seeder', array_merge([$class], $genOptions)); } } diff --git a/system/Commands/Generators/MigrateCreate.php b/system/Commands/Generators/MigrateCreate.php new file mode 100644 index 000000000000..01f79ae527eb --- /dev/null +++ b/system/Commands/Generators/MigrateCreate.php @@ -0,0 +1,121 @@ + [options]'; + + /** + * The Command's arguments. + * + * @var array + */ + protected $arguments = [ + 'name' => 'The migration file name.', + ]; + + /** + * The Command's options. + * + * @var array + */ + protected $options = [ + '-n' => 'Set root namespace. Defaults to APP_NAMESPACE', + '-force' => 'Force overwrite existing files.', + ]; + + /** + * Actually execute a command. + * + * @param array $params + */ + public function run(array $params) + { + // Resolve arguments before passing to make:migration + $params[0] = $params[0] ?? CLI::getSegment(2); + $params['n'] = $params['n'] ?? CLI::getOption('n') ?? APP_NAMESPACE; + + if (array_key_exists('force', $params) || CLI::getOption('force')) + { + $params['force'] = null; + } + + $this->call('make:migration', $params); + } +} diff --git a/tests/system/Commands/CreateMigrationTest.php b/tests/system/Commands/CreateMigrationTest.php index 26f3630d6308..ff04e410df30 100644 --- a/tests/system/Commands/CreateMigrationTest.php +++ b/tests/system/Commands/CreateMigrationTest.php @@ -33,7 +33,7 @@ protected function getBuffer(): string public function testCreateMigration() { - command('migrate:create databaseMigrator'); + command('make:migration databaseMigrator'); $this->assertStringContainsString('Created file: ', $this->getBuffer()); $this->assertStringContainsString('_DatabaseMigrator.php', $this->getBuffer()); } @@ -47,7 +47,7 @@ public function testCreateMigrationFailsOnUnwritableDirectory() chmod(APPPATH . 'Database/Migrations', 0444); - command('migrate:create migrateOne'); + command('make:migration migrateOne'); $this->assertStringContainsString('Error in creating file: ', $this->getBuffer()); chmod(APPPATH . 'Database/Migrations', 0755); @@ -57,7 +57,7 @@ public function testCreateMigrationFailOnUndefinedNamespace() { try { - command('migrate:create migrateTwo -n CodeIgnite'); + command('make:migration migrateTwo -n CodeIgnite'); } catch (\Throwable $e) { @@ -69,7 +69,7 @@ public function testCreateMigrationFailOnUndefinedNamespace() public function testCreateMigrationOnOtherNamespace() { - command('migrate:create migrateThree -n CodeIgniter'); + command('make:migration migrateThree -n CodeIgniter'); $this->assertStringContainsString('Created file:', $this->getBuffer()); $this->assertStringContainsString('SYSTEMPATH', $this->getBuffer()); diff --git a/user_guide_src/source/changelogs/v4.0.5.rst b/user_guide_src/source/changelogs/v4.0.5.rst index 4342a1914efd..90c97c9da158 100644 --- a/user_guide_src/source/changelogs/v4.0.5.rst +++ b/user_guide_src/source/changelogs/v4.0.5.rst @@ -22,3 +22,4 @@ Deprecations: - Deprecated ``BaseCommand::getPad`` in favor of ``BaseCommand::setPad``. - Deprecated ``Config\Format::getFormatter`` in favor of ``CodeIgniter\Format\Format::getFormatter`` +- Deprecated ``migrate:create`` command in favor of ``make:migration`` command. diff --git a/user_guide_src/source/cli/cli_generators.rst b/user_guide_src/source/cli/cli_generators.rst index 06d74f052a57..810866ed071a 100644 --- a/user_guide_src/source/cli/cli_generators.rst +++ b/user_guide_src/source/cli/cli_generators.rst @@ -154,7 +154,7 @@ Options: * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. * ``-force``: Set this flag to overwrite existing files on destination. -migrate:create +make:migration -------------- Creates a new migration file. @@ -163,7 +163,7 @@ Usage: ====== .. code-block:: none - migrate:create [options] + make:migration [options] Argument: ========= @@ -209,6 +209,9 @@ Options: defined in your ``$psr4`` array in ``Config\Autoload`` or defined in your composer autoload file. Otherwise, a ``RuntimeException`` will be thrown. +.. warning:: Use of ``migrate:create`` to create migration files is now deprecated. It will be removed in + future releases. Please use ``make:migration`` as replacement. + **************************************** Scaffolding a Complete Set of Stock Code **************************************** @@ -221,7 +224,7 @@ command to rule them all. Fret no more! CodeIgniter4 is also shipped with a dedicated ``make:scaffold`` command that is basically a wrapper to the controller, model, entity, migration, and seeder generator commands. All you need is the class name that will be used to name all the generated classes. Also, **individual options** supported by each -generator command is recognized by the scaffold command. +generator command are recognized by the scaffold command. Running this in your terminal:: @@ -232,7 +235,7 @@ will create the following classes: (1) ``App\Controllers\User``; (2) ``App\Models\User``; (3) ``App\Entities\User``; -(4) ``App\Database\Migrations\User``; and +(4) ``App\Database\Migrations\_User``; and (5) ``App\Database\Seeds\User``. **************** @@ -341,7 +344,7 @@ which is public and need not be overridden as it is essentially complete. ``getNamespacedClass`` and ``getTemplate``, or else you will get a PHP fatal error. .. note:: ``GeneratorCommand`` has the default argument of ``['name' => 'Class name']``. You can - override the description by supplying the name in your ``$options`` property, e.g. ``['name' => 'Module class name']``. + override the description by supplying the name in your ``$arguments`` property, e.g. ``['name' => 'Module class name']``. .. note:: ``GeneratorCommand`` has the default options of ``-n`` and ``-force``. Child classes cannot override these two properties as they are crucial in the implementation of the code generation. diff --git a/user_guide_src/source/dbmgmt/migration.rst b/user_guide_src/source/dbmgmt/migration.rst index 9574e0e960e5..9cf443bc69e0 100644 --- a/user_guide_src/source/dbmgmt/migration.rst +++ b/user_guide_src/source/dbmgmt/migration.rst @@ -138,7 +138,7 @@ The migration library can automatically scan all namespaces you have defined wit the ``$psr4`` property for matching directory names. It will include all migrations it finds in Database/Migrations. -Each namespace has it's own version sequence, this will help you upgrade and downgrade each module (namespace) without affecting other namespaces. +Each namespace has its own version sequence, this will help you upgrade and downgrade each module (namespace) without affecting other namespaces. For example, assume that we have the following namespaces defined in our Autoload configuration file:: @@ -203,7 +203,7 @@ This example will migrate Blog namespace with any new migrations on the test dat > php spark migrate -g test -n Blog -When using the `-all` option, it will scan through all namespaces attempting to find any migrations that have +When using the ``-all`` option, it will scan through all namespaces attempting to find any migrations that have not been run. These will all be collected and then sorted as a group by date created. This should help to minimize any potential conflicts between the main application and any modules. @@ -244,7 +244,7 @@ You can use (status) with the following options: - ``-g`` - to choose database group, otherwise default database group will be used. -**create** +**make:migration** Creates a skeleton migration file in **app/Database/Migrations**. It automatically prepends the current timestamp. The class name it @@ -252,11 +252,12 @@ creates is the Pascal case version of the filename. :: - > php spark migrate:create [filename] + > php spark make:migration [options] -You can use (create) with the following options: +You can use (make:migration) with the following options: -- ``-n`` - to choose namespace, otherwise (App) namespace will be used. +- ``-n`` - to choose namespace, otherwise the value of ``APP_NAMESPACE`` will be used. +- ``-force`` - If a similarly named migration file is present in destination, this will be overwritten. ********************* Migration Preferences @@ -269,7 +270,7 @@ Preference Default Options Des ========================== ====================== ========================== ============================================================= **enabled** true true / false Enable or disable migrations. **table** migrations None The table name for storing the schema version number. -**timestampFormat** Y-m-d-His\_ The format to use for timestamps when creating a migration. +**timestampFormat** Y-m-d-His\_ The format to use for timestamps when creating a migration. ========================== ====================== ========================== ============================================================= *************** From 4f48d5fcff0d06d8d435fadf0b7a95d3e5cc3e68 Mon Sep 17 00:00:00 2001 From: michalsn Date: Mon, 7 Sep 2020 19:48:41 +0200 Subject: [PATCH 099/328] Fix URI class for proper subfolders handling --- system/HTTP/URI.php | 7 ++++--- tests/system/Helpers/URLHelperTest.php | 14 ++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index d536f4cf374d..74dd2f75286f 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -597,12 +597,13 @@ public function __toString(): string // segments that show up prior to the URI path we just // grabbed from the request, so add it on if necessary. $baseUri = new self(config(\Config\App::class)->baseURL); - $basePath = rtrim($baseUri->getPath(), '/') . '/'; + $basePath = trim($baseUri->getPath(), '/') . '/'; $path = $this->getPath(); + $trimPath = ltrim($path, '/'); - if ($basePath !== '/' && strpos($path, $basePath) !== 0) + if ($basePath !== '/' && strpos($trimPath, $basePath) !== 0) { - $path = $basePath . $path; + $path = $basePath . $trimPath; } return static::createURIString( diff --git a/tests/system/Helpers/URLHelperTest.php b/tests/system/Helpers/URLHelperTest.php index c223706dac96..86021167940d 100644 --- a/tests/system/Helpers/URLHelperTest.php +++ b/tests/system/Helpers/URLHelperTest.php @@ -328,14 +328,15 @@ public function testBaseURLWithSegmentsAgain() public function testBaseURLHasSubfolder() { $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/test'; + $_SERVER['REQUEST_URI'] = '/subfolder/test'; + $_SERVER['SCRIPT_NAME'] = '/subfolder/index.php'; // Since we're on a CLI, we must provide our own URI $config = new App(); $config->baseURL = 'http://example.com/subfolder'; - $request = Services::request($config, false); - $request->uri = new URI('http://example.com/subfolder/test'); + Config::injectMock('App', $config); + $request = Services::request($config, false); Services::injectMock('request', $request); $this->assertEquals('http://example.com/subfolder/foo', base_url('foo')); @@ -383,14 +384,15 @@ public function testCurrentURLReturnsObject() public function testCurrentURLEquivalence() { $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; + $_SERVER['REQUEST_URI'] = '/public'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; // Since we're on a CLI, we must provide our own URI $config = new App(); $config->baseURL = 'http://example.com/'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/public'); + Config::injectMock('App', $config); + $request = Services::request($config); Services::injectMock('request', $request); $this->assertEquals(base_url(uri_string()), current_url()); From a7e66f53c6a8f7f7417460098fe2993f97480dd5 Mon Sep 17 00:00:00 2001 From: michalsn Date: Tue, 8 Sep 2020 16:18:23 +0200 Subject: [PATCH 100/328] Cast getBatches() result array --- system/Database/MigrationRunner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index 68e771373cd4..c06a1be2b185 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -850,7 +850,7 @@ public function getBatches(): array ->get() ->getResultArray(); - return array_column($batches, 'batch'); + return array_map('intval', array_column($batches, 'batch')); } //-------------------------------------------------------------------- From 1ad0a6a4aa2347797c56d7bbd4a1e34a2a720b05 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 8 Sep 2020 01:52:39 +0800 Subject: [PATCH 101/328] Allow GeneratorCommand's sortImports to be configurable --- system/CLI/GeneratorCommand.php | 28 +++++++++- tests/_support/Commands/Foobar.php | 7 +++ tests/_support/Commands/LanguageCommand.php | 37 +++++++++++++ .../Commands/ConfigurableSortImportsTest.php | 53 +++++++++++++++++++ 4 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 tests/_support/Commands/Foobar.php create mode 100644 tests/_support/Commands/LanguageCommand.php create mode 100644 tests/system/Commands/ConfigurableSortImportsTest.php diff --git a/system/CLI/GeneratorCommand.php b/system/CLI/GeneratorCommand.php index a22c7001f9f5..f035b0192356 100644 --- a/system/CLI/GeneratorCommand.php +++ b/system/CLI/GeneratorCommand.php @@ -77,6 +77,15 @@ abstract class GeneratorCommand extends BaseCommand '-force' => 'Force overwrite existing files.', ]; + /** + * Whether to sort class imports. + * + * @internal + * + * @var boolean + */ + private $sortImports = true; + /** * The params array for easy access by other methods. * @@ -155,6 +164,21 @@ public function run(array $params) CLI::newLine(); } + /** + * Allows child generators to modify + * the internal `$sortImports` flag. + * + * @param boolean $sort + * + * @return $this + */ + protected function setSortImports(bool $sort) + { + $this->sortImports = $sort; + + return $this; + } + /** * Gets the class name from input. This can be overridden * if name is really required by providing a prompt. @@ -338,7 +362,7 @@ protected function setReplacements(string $template, string $class): string */ protected function sortImports(string $template): string { - if (preg_match('/(?P(?:use [^;]+;$\n?)+)/m', $template, $match)) + if ($this->sortImports && preg_match('/(?P(?:use [^;]+;$\n?)+)/m', $template, $match)) { $imports = explode("\n", trim($match['imports'])); sort($imports); @@ -346,7 +370,7 @@ protected function sortImports(string $template): string return str_replace(trim($match['imports']), implode("\n", $imports), $template); } - return $template; // @codeCoverageIgnore + return $template; } /** diff --git a/tests/_support/Commands/Foobar.php b/tests/_support/Commands/Foobar.php new file mode 100644 index 000000000000..b08fab4bd231 --- /dev/null +++ b/tests/_support/Commands/Foobar.php @@ -0,0 +1,7 @@ + 'The command use this as foo.', + 'bar' => 'The command use this as bar.', + 'baz' => 'The baz is here.', +]; diff --git a/tests/_support/Commands/LanguageCommand.php b/tests/_support/Commands/LanguageCommand.php new file mode 100644 index 000000000000..ca264aa51059 --- /dev/null +++ b/tests/_support/Commands/LanguageCommand.php @@ -0,0 +1,37 @@ + 'The language folder to save the file.', + '--sort' => 'Turn on/off the sortImports flag.', + ]; + + public function run(array $params) + { + $params[0] = 'Foobar'; + $params['lang'] = $params['lang'] ?? 'en'; + $sort = (isset($params['sort']) && $params['sort'] === 'off') ? false : true; + + $this->setSortImports($sort); + + parent::run($params); + } + + protected function getNamespacedClass(string $rootNamespace, string $class): string + { + return $rootNamespace . '\\Language\\' . $this->params['lang'] . '\\' . $class; + } + + protected function getTemplate(): string + { + return file_get_contents(__DIR__ . '/Foobar.php') ?: ''; + } +} diff --git a/tests/system/Commands/ConfigurableSortImportsTest.php b/tests/system/Commands/ConfigurableSortImportsTest.php new file mode 100644 index 000000000000..16d2d1fe9944 --- /dev/null +++ b/tests/system/Commands/ConfigurableSortImportsTest.php @@ -0,0 +1,53 @@ +streamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $this->streamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + } + + protected function tearDown(): void + { + parent::tearDown(); + stream_filter_remove($this->streamFilter); + + $result = str_replace(["\033[0;32m", "\033[0m", "\n"], '', CITestStreamFilter::$buffer); + $file = trim(substr($result, 14)); + $file = str_replace('APPPATH' . DIRECTORY_SEPARATOR, APPPATH, $file); + $dir = dirname($file); + file_exists($file) && unlink($file); + is_dir($dir) && rmdir($dir); + } + + public function testEnabledSortImportsWillDisruptLanguageFilePublish() + { + command('publish:language --lang es'); + + $file = APPPATH . 'Language/es/Foobar.php'; + $this->assertStringContainsString('Created file: ', CITestStreamFilter::$buffer); + $this->assertFileExists($file); + $this->assertNotSame(sha1_file(SUPPORTPATH . 'Commands/Foobar.php'), sha1_file($file)); + } + + public function testDisabledSortImportsWillNotAffectLanguageFilesPublish() + { + command('publish:language --lang es --sort off'); + + $file = APPPATH . 'Language/es/Foobar.php'; + $this->assertStringContainsString('Created file: ', CITestStreamFilter::$buffer); + $this->assertFileExists($file); + $this->assertSame(sha1_file(SUPPORTPATH . 'Commands/Foobar.php'), sha1_file($file)); + } +} From 3601c0b6c3ce3741a30fd04263e957276e164677 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 8 Sep 2020 20:22:55 +0800 Subject: [PATCH 102/328] Match 'use' at beginning of phrases only --- system/CLI/GeneratorCommand.php | 2 +- tests/_support/Commands/Foobar.php | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/system/CLI/GeneratorCommand.php b/system/CLI/GeneratorCommand.php index f035b0192356..0cd70140e717 100644 --- a/system/CLI/GeneratorCommand.php +++ b/system/CLI/GeneratorCommand.php @@ -362,7 +362,7 @@ protected function setReplacements(string $template, string $class): string */ protected function sortImports(string $template): string { - if ($this->sortImports && preg_match('/(?P(?:use [^;]+;$\n?)+)/m', $template, $match)) + if ($this->sortImports && preg_match('/(?P(?:^use [^;]+;$\n?)+)/m', $template, $match)) { $imports = explode("\n", trim($match['imports'])); sort($imports); diff --git a/tests/_support/Commands/Foobar.php b/tests/_support/Commands/Foobar.php index b08fab4bd231..1479c68ffa22 100644 --- a/tests/_support/Commands/Foobar.php +++ b/tests/_support/Commands/Foobar.php @@ -1,7 +1,11 @@ 'The command use this as foo.', - 'bar' => 'The command use this as bar.', + 'foo' => 'The command will use this as foo.', + 'bar' => 'The command will use this as bar.', 'baz' => 'The baz is here.', + 'bas' => CLI::color('bas', 'green') . (new App())->baseURL, ]; From c5fc3de691c65ce168be478cd4c01e71c81b9eeb Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 6 Sep 2020 00:51:12 +0800 Subject: [PATCH 103/328] Use DocBlocks to document Config classes' properties --- app/Config/App.php | 642 +++++++++++++++++---------- app/Config/Autoload.php | 5 +- app/Config/Boot/development.php | 38 +- app/Config/Boot/production.php | 23 +- app/Config/Boot/testing.php | 37 +- app/Config/Cache.php | 172 +++---- app/Config/Constants.php | 100 +++-- app/Config/ContentSecurityPolicy.php | 6 +- app/Config/Database.php | 13 +- app/Config/DocTypes.php | 16 +- app/Config/Email.php | 2 +- app/Config/Encryption.php | 48 +- app/Config/Events.php | 4 +- app/Config/Exceptions.php | 60 +-- app/Config/Filters.php | 47 +- app/Config/ForeignCharacters.php | 9 +- app/Config/Honeypot.php | 5 +- app/Config/Images.php | 6 +- app/Config/Kint.php | 27 +- app/Config/Logger.php | 140 +++--- app/Config/Migrations.php | 83 ++-- app/Config/Mimes.php | 42 +- app/Config/Modules.php | 61 +-- app/Config/Pager.php | 48 +- app/Config/Paths.php | 47 +- app/Config/Routes.php | 8 +- app/Config/Services.php | 24 +- app/Config/Toolbar.php | 104 +++-- app/Config/UserAgents.php | 59 ++- app/Config/Validation.php | 8 +- app/Config/View.php | 14 +- 31 files changed, 1100 insertions(+), 798 deletions(-) diff --git a/app/Config/App.php b/app/Config/App.php index 4c0e9a9d02ae..3bba4a902197 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -1,278 +1,434 @@ - APPPATH * ]; * - * @var array + * @var array */ public $psr4 = [ APP_NAMESPACE => APPPATH, // For custom app namespace @@ -59,7 +60,7 @@ class Autoload extends AutoloadConfig * 'MyClass' => '/path/to/class/file.php' * ]; * - * @var array + * @var array */ public $classmap = []; } diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php index 63fdd88be55d..05a861258fc9 100644 --- a/app/Config/Boot/development.php +++ b/app/Config/Boot/development.php @@ -1,32 +1,32 @@ + */ public $memcached = [ 'host' => '127.0.0.1', 'port' => 11211, @@ -84,14 +92,15 @@ class Cache extends BaseConfig 'raw' => false, ]; - /* - | ------------------------------------------------------------------------- - | Redis settings - | ------------------------------------------------------------------------- - | Your Redis server can be specified below, if you are using - | the Redis or Predis drivers. - | - */ + /** + * ------------------------------------------------------------------------- + * Redis settings + * ------------------------------------------------------------------------- + * Your Redis server can be specified below, if you are using + * the Redis or Predis drivers. + * + * @var array + */ public $redis = [ 'host' => '127.0.0.1', 'password' => null, @@ -100,15 +109,16 @@ class Cache extends BaseConfig 'database' => 0, ]; - /* - |-------------------------------------------------------------------------- - | Available Cache Handlers - |-------------------------------------------------------------------------- - | - | This is an array of cache engine alias' and class names. Only engines - | that are listed here are allowed to be used. - | - */ + /** + * -------------------------------------------------------------------------- + * Available Cache Handlers + * -------------------------------------------------------------------------- + * + * This is an array of cache engine alias' and class names. Only engines + * that are listed here are allowed to be used. + * + * @var array + */ public $validHandlers = [ 'dummy' => \CodeIgniter\Cache\Handlers\DummyHandler::class, 'file' => \CodeIgniter\Cache\Handlers\FileHandler::class, diff --git a/app/Config/Constants.php b/app/Config/Constants.php index b25f71cdcc26..8f8498a58a0a 100644 --- a/app/Config/Constants.php +++ b/app/Config/Constants.php @@ -1,36 +1,38 @@ + */ + public $list = [ 'xhtml11' => '', 'xhtml1-strict' => '', 'xhtml1-trans' => '', diff --git a/app/Config/Email.php b/app/Config/Email.php index d9ca1420f6ad..1d0c181e3a41 100644 --- a/app/Config/Email.php +++ b/app/Config/Email.php @@ -1,11 +1,11 @@ \CodeIgniter\Filters\CSRF::class, 'toolbar' => \CodeIgniter\Filters\DebugToolbar::class, 'honeypot' => \CodeIgniter\Filters\Honeypot::class, ]; - // Always applied before every request + /** + * List of filter aliases that are always + * applied before and after every request. + * + * @var array + */ public $globals = [ 'before' => [ - //'honeypot' + // 'honeypot', // 'csrf', ], 'after' => [ 'toolbar', - //'honeypot' + // 'honeypot', ], ]; - // Works on all of a particular HTTP method - // (GET, POST, etc) as BEFORE filters only - // like: 'post' => ['CSRF', 'throttle'], + /** + * List of filter aliases that works on a + * particular HTTP method (GET, POST, etc.). + * + * Example: + * 'post' => ['csrf', 'throttle'] + * + * @var array + */ public $methods = []; - // List filter aliases and any before/after uri patterns - // that they should run on, like: - // 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']], + /** + * List of filter aliases that should run on any + * before or after URI patterns. + * + * Example: + * 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']] + * + * @var array + */ public $filters = []; } diff --git a/app/Config/ForeignCharacters.php b/app/Config/ForeignCharacters.php index 8ee6f113d78e..174ddb16a872 100644 --- a/app/Config/ForeignCharacters.php +++ b/app/Config/ForeignCharacters.php @@ -1,6 +1,9 @@ - + * @var array */ public $handlers = [ 'gd' => \CodeIgniter\Images\Handlers\GDHandler::class, diff --git a/app/Config/Kint.php b/app/Config/Kint.php index 09db83dd5971..665de0bb1def 100644 --- a/app/Config/Kint.php +++ b/app/Config/Kint.php @@ -1,23 +1,22 @@ - [ /* diff --git a/app/Config/Migrations.php b/app/Config/Migrations.php index b83fe907ff95..1fa3100af07c 100644 --- a/app/Config/Migrations.php +++ b/app/Config/Migrations.php @@ -1,50 +1,55 @@ - php spark migrate:create - | - | Typical formats: - | YmdHis_ - | Y-m-d-His_ - | Y_m_d_His_ - | - */ + /** + * -------------------------------------------------------------------------- + * Timestamp Format + * -------------------------------------------------------------------------- + * + * This is the format that will be used when creating new migrations + * using the CLI command: + * > php spark migrate:create + * + * Typical formats: + * - YmdHis_ + * - Y-m-d-His_ + * - Y_m_d_His_ + * + * @var string + */ public $timestampFormat = 'Y-m-d-His_'; - } diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php index fd5ba3690260..cfeea777c7f2 100644 --- a/app/Config/Mimes.php +++ b/app/Config/Mimes.php @@ -1,18 +1,19 @@ - + */ public $templates = [ 'default_full' => 'CodeIgniter\Pager\Views\default_full', 'default_simple' => 'CodeIgniter\Pager\Views\default_simple', 'default_head' => 'CodeIgniter\Pager\Views\default_head', ]; - /* - |-------------------------------------------------------------------------- - | Items Per Page - |-------------------------------------------------------------------------- - | - | The default number of results shown in a single page. - | - */ + /** + * -------------------------------------------------------------------------- + * Items Per Page + * -------------------------------------------------------------------------- + * + * The default number of results shown in a single page. + * + * @var integer + */ public $perPage = 20; } diff --git a/app/Config/Paths.php b/app/Config/Paths.php index 6ca2d37349a4..bd77d27051b3 100644 --- a/app/Config/Paths.php +++ b/app/Config/Paths.php @@ -1,9 +1,14 @@ -set404Override(); $routes->setAutoRoute(true); -/** +/* * -------------------------------------------------------------------- * Route Definitions * -------------------------------------------------------------------- @@ -32,7 +34,7 @@ // route since we don't have to scan directories. $routes->get('/', 'Home::index'); -/** +/* * -------------------------------------------------------------------- * Additional Routing * -------------------------------------------------------------------- diff --git a/app/Config/Services.php b/app/Config/Services.php index c58da709043a..36488a50db3f 100644 --- a/app/Config/Services.php +++ b/app/Config/Services.php @@ -1,6 +1,8 @@ - + */ public $platforms = [ 'windows nt 10.0' => 'Windows 10', 'windows nt 6.3' => 'Windows 8.1', @@ -58,8 +68,16 @@ class UserAgents extends BaseConfig 'symbian' => 'Symbian OS', ]; - // The order of this array should NOT be changed. Many browsers return - // multiple browser types so we want to identify the sub-type first. + /** + * ------------------------------------------------------------------- + * Browsers + * ------------------------------------------------------------------- + * + * The order of this array should NOT be changed. Many browsers return + * multiple browser types so we want to identify the subtype first. + * + * @var array + */ public $browsers = [ 'OPR' => 'Opera', 'Flock' => 'Flock', @@ -94,6 +112,13 @@ class UserAgents extends BaseConfig 'Vivaldi' => 'Vivaldi', ]; + /** + * ------------------------------------------------------------------- + * Mobiles + * ------------------------------------------------------------------- + * + * @var array + */ public $mobiles = [ // legacy array, old values commented out 'mobileexplorer' => 'Mobile Explorer', @@ -194,7 +219,15 @@ class UserAgents extends BaseConfig 'cellphone' => 'Generic Mobile', ]; - // There are hundreds of bots but these are the most common. + /** + * ------------------------------------------------------------------- + * Robots + * ------------------------------------------------------------------- + * + * There are hundred of bots but these are the most common. + * + * @var array + */ public $robots = [ 'googlebot' => 'Googlebot', 'msnbot' => 'MSNBot', diff --git a/app/Config/Validation.php b/app/Config/Validation.php index 97f08c752671..b16c63dd6bf3 100644 --- a/app/Config/Validation.php +++ b/app/Config/Validation.php @@ -1,4 +1,6 @@ - */ public $templates = [ 'list' => 'CodeIgniter\Validation\Views\list', diff --git a/app/Config/View.php b/app/Config/View.php index f66b2532f0f7..53b6f5672fdf 100644 --- a/app/Config/View.php +++ b/app/Config/View.php @@ -1,6 +1,10 @@ - Date: Sun, 6 Sep 2020 01:24:13 +0800 Subject: [PATCH 104/328] Resolve name clashes --- app/Config/Services.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Config/Services.php b/app/Config/Services.php index 36488a50db3f..4b6082dd0568 100644 --- a/app/Config/Services.php +++ b/app/Config/Services.php @@ -2,7 +2,7 @@ namespace Config; -use CodeIgniter\Config\Services as BaseServices; +use CodeIgniter\Config\Services as CoreServices; /** * Services Configuration file. @@ -17,7 +17,7 @@ * method format you should use for your service methods. For more examples, * see the core Services file at system/Config/Services.php. */ -class Services extends BaseServices +class Services extends CoreServices { // public static function example($getShared = true) From 36712967286cb855675f842a3dbf4b0127ab79f2 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 6 Sep 2020 01:34:11 +0800 Subject: [PATCH 105/328] Fix wrong view config parent --- app/Config/View.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Config/View.php b/app/Config/View.php index 53b6f5672fdf..c945e2e068c8 100644 --- a/app/Config/View.php +++ b/app/Config/View.php @@ -2,7 +2,7 @@ namespace Config; -use CodeIgniter\View\View as BaseView; +use CodeIgniter\Config\View as BaseView; class View extends BaseView { From 59cc44455586876217aa4e5b196ea02297bef771 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 8 Sep 2020 02:23:22 +0800 Subject: [PATCH 106/328] Use double dashes for long options --- system/CLI/GeneratorCommand.php | 4 ++-- system/Commands/Database/Migrate.php | 6 +++--- system/Commands/Database/MigrateRefresh.php | 8 ++++---- system/Commands/Encryption/GenerateKey.php | 8 ++++---- system/Commands/Generators/CreateCommand.php | 6 +++--- system/Commands/Generators/CreateController.php | 4 ++-- system/Commands/Generators/CreateModel.php | 6 +++--- system/Commands/Generators/CreateScaffold.php | 12 ++++++------ system/Commands/Generators/MigrateCreate.php | 4 ++-- system/Commands/Housekeeping/ClearLogs.php | 2 +- system/Commands/Server/Serve.php | 6 +++--- 11 files changed, 33 insertions(+), 33 deletions(-) diff --git a/system/CLI/GeneratorCommand.php b/system/CLI/GeneratorCommand.php index 0cd70140e717..ca5f41e712a1 100644 --- a/system/CLI/GeneratorCommand.php +++ b/system/CLI/GeneratorCommand.php @@ -73,8 +73,8 @@ abstract class GeneratorCommand extends BaseCommand * @var array */ private $defaultOptions = [ - '-n' => 'Set root namespace. Defaults to APP_NAMESPACE.', - '-force' => 'Force overwrite existing files.', + '-n' => 'Set root namespace. Defaults to APP_NAMESPACE.', + '--force' => 'Force overwrite existing files.', ]; /** diff --git a/system/Commands/Database/Migrate.php b/system/Commands/Database/Migrate.php index 8370286b0415..1b2af27319ac 100644 --- a/system/Commands/Database/Migrate.php +++ b/system/Commands/Database/Migrate.php @@ -92,9 +92,9 @@ class Migrate extends BaseCommand * @var array */ protected $options = [ - '-n' => 'Set migration namespace', - '-g' => 'Set database group', - '-all' => 'Set for all namespaces, will ignore (-n) option', + '-n' => 'Set migration namespace', + '-g' => 'Set database group', + '--all' => 'Set for all namespaces, will ignore (-n) option', ]; /** diff --git a/system/Commands/Database/MigrateRefresh.php b/system/Commands/Database/MigrateRefresh.php index 32a912ed03e9..35490402e135 100644 --- a/system/Commands/Database/MigrateRefresh.php +++ b/system/Commands/Database/MigrateRefresh.php @@ -93,10 +93,10 @@ class MigrateRefresh extends BaseCommand * @var array */ protected $options = [ - '-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', + '-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', ]; /** diff --git a/system/Commands/Encryption/GenerateKey.php b/system/Commands/Encryption/GenerateKey.php index a938d4ce04fa..cb48945f6934 100644 --- a/system/Commands/Encryption/GenerateKey.php +++ b/system/Commands/Encryption/GenerateKey.php @@ -83,10 +83,10 @@ class GenerateKey extends BaseCommand * @var array */ protected $options = [ - '-force' => 'Force overwrite existing key in `.env` file.', - '-length' => 'The length of the random string that should be returned in bytes. Defaults to 32.', - '-prefix' => 'Prefix to prepend to encoded key (either hex2bin or base64). Defaults to hex2bin.', - '-show' => 'Shows the generated key in the terminal instead of storing in the `.env` file.', + '--force' => 'Force overwrite existing key in `.env` file.', + '--length' => 'The length of the random string that should be returned in bytes. Defaults to 32.', + '--prefix' => 'Prefix to prepend to encoded key (either hex2bin or base64). Defaults to hex2bin.', + '--show' => 'Shows the generated key in the terminal instead of storing in the `.env` file.', ]; /** diff --git a/system/Commands/Generators/CreateCommand.php b/system/Commands/Generators/CreateCommand.php index 73b0a52040fb..3a96f1f18bfd 100644 --- a/system/Commands/Generators/CreateCommand.php +++ b/system/Commands/Generators/CreateCommand.php @@ -80,9 +80,9 @@ class CreateCommand extends GeneratorCommand * @var array */ protected $options = [ - '-command' => 'The command name. Defaults to "command:name"', - '-group' => 'The group of command. Defaults to "CodeIgniter" for basic commands, and "Generators" for generator commands.', - '-type' => 'Type of command. Whether a basic command or a generator command. Defaults to "basic".', + '--command' => 'The command name. Defaults to "command:name"', + '--group' => 'The group of command. Defaults to "CodeIgniter" for basic commands, and "Generators" for generator commands.', + '--type' => 'Type of command. Whether a basic command or a generator command. Defaults to "basic".', ]; /** diff --git a/system/Commands/Generators/CreateController.php b/system/Commands/Generators/CreateController.php index bc93b288ec31..9e077f64654b 100644 --- a/system/Commands/Generators/CreateController.php +++ b/system/Commands/Generators/CreateController.php @@ -83,8 +83,8 @@ class CreateController extends GeneratorCommand * @var array */ protected $options = [ - '-bare' => 'Extends from CodeIgniter\\Controller instead of BaseController', - '-restful' => 'Extends from a RESTful resource. Options are \'controller\' or \'presenter\'.', + '--bare' => 'Extends from CodeIgniter\\Controller instead of BaseController', + '--restful' => 'Extends from a RESTful resource. Options are \'controller\' or \'presenter\'.', ]; /** diff --git a/system/Commands/Generators/CreateModel.php b/system/Commands/Generators/CreateModel.php index 6e5dd6d7d72f..ab39fde308b5 100644 --- a/system/Commands/Generators/CreateModel.php +++ b/system/Commands/Generators/CreateModel.php @@ -83,9 +83,9 @@ class CreateModel extends GeneratorCommand * @var array */ protected $options = [ - '-dbgroup' => 'Database group to use. Defaults to "default".', - '-entity' => 'Use an Entity as return type.', - '-table' => 'Supply a different table name. Defaults to the pluralized name.', + '--dbgroup' => 'Database group to use. Defaults to "default".', + '--entity' => 'Use an Entity as return type.', + '--table' => 'Supply a different table name. Defaults to the pluralized name.', ]; /** diff --git a/system/Commands/Generators/CreateScaffold.php b/system/Commands/Generators/CreateScaffold.php index 6775a3778cb5..98f66040bf06 100644 --- a/system/Commands/Generators/CreateScaffold.php +++ b/system/Commands/Generators/CreateScaffold.php @@ -88,12 +88,12 @@ class CreateScaffold extends BaseCommand * @var array */ protected $options = [ - '-bare' => 'Add the \'-bare\' option to controller scaffold.', - '-restful' => 'Add the \'-restful\' option to controller scaffold.', - '-dbgroup' => 'Add the \'-dbgroup\' option to model scaffold.', - '-table' => 'Add the \'-table\' option to the model scaffold.', - '-n' => 'Set root namespace. Defaults to APP_NAMESPACE.', - '-force' => 'Force overwrite existing files.', + '--bare' => 'Add the \'-bare\' option to controller scaffold.', + '--restful' => 'Add the \'-restful\' option to controller scaffold.', + '--dbgroup' => 'Add the \'-dbgroup\' option to model scaffold.', + '--table' => 'Add the \'-table\' option to the model scaffold.', + '-n' => 'Set root namespace. Defaults to APP_NAMESPACE.', + '--force' => 'Force overwrite existing files.', ]; /** diff --git a/system/Commands/Generators/MigrateCreate.php b/system/Commands/Generators/MigrateCreate.php index 01f79ae527eb..99688480509a 100644 --- a/system/Commands/Generators/MigrateCreate.php +++ b/system/Commands/Generators/MigrateCreate.php @@ -96,8 +96,8 @@ class MigrateCreate extends BaseCommand * @var array */ protected $options = [ - '-n' => 'Set root namespace. Defaults to APP_NAMESPACE', - '-force' => 'Force overwrite existing files.', + '-n' => 'Set root namespace. Defaults to APP_NAMESPACE', + '--force' => 'Force overwrite existing files.', ]; /** diff --git a/system/Commands/Housekeeping/ClearLogs.php b/system/Commands/Housekeeping/ClearLogs.php index 4e0c3009ec01..7fc526c86a98 100644 --- a/system/Commands/Housekeeping/ClearLogs.php +++ b/system/Commands/Housekeeping/ClearLogs.php @@ -82,7 +82,7 @@ class ClearLogs extends BaseCommand * @var array */ protected $options = [ - '-force' => 'Force delete of all logs files without prompting.', + '--force' => 'Force delete of all logs files without prompting.', ]; /** diff --git a/system/Commands/Server/Serve.php b/system/Commands/Server/Serve.php index f08362afaabb..538039d7f7ec 100644 --- a/system/Commands/Server/Serve.php +++ b/system/Commands/Server/Serve.php @@ -115,9 +115,9 @@ class Serve extends BaseCommand * @var array */ protected $options = [ - '-php' => 'The PHP Binary [default: "PHP_BINARY"]', - '-host' => 'The HTTP Host [default: "localhost"]', - '-port' => 'The HTTP Host Port [default: "8080"]', + '--php' => 'The PHP Binary [default: "PHP_BINARY"]', + '--host' => 'The HTTP Host [default: "localhost"]', + '--port' => 'The HTTP Host Port [default: "8080"]', ]; /** From bfe535067cabe37bb34eee2d318bb401fcaa05eb Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 8 Sep 2020 21:00:09 +0800 Subject: [PATCH 107/328] Update getOptionString to allow output using longopts --- system/CLI/CLI.php | 24 ++++++++++--- system/HTTP/CLIRequest.php | 39 ++++++++++----------- tests/system/CLI/CLITest.php | 26 +++++++++----- tests/system/HTTP/CLIRequestTest.php | 52 +++++++++++++--------------- 4 files changed, 80 insertions(+), 61 deletions(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 3f982c129c22..1ee6d45d345c 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -1017,9 +1017,12 @@ public static function getOptions(): array * Returns the options as a string, suitable for passing along on * the CLI to other commands. * + * @param boolean $useLongOpts Use '--' for long options? + * @param boolean $trim Trim final string output? + * * @return string */ - public static function getOptionString(): string + public static function getOptionString(bool $useLongOpts = false, bool $trim = false): string { if (empty(static::$options)) { @@ -1030,17 +1033,28 @@ public static function getOptionString(): string foreach (static::$options as $name => $value) { + if ($useLongOpts && mb_strlen($name) > 1) + { + $out .= "--{$name} "; + } + else + { + $out .= "-{$name} "; + } + // If there's a space, we need to group // so it will pass correctly. if (mb_strpos($value, ' ') !== false) { - $value = '"' . $value . '"'; + $out .= '"' . $value . '" '; + } + elseif ($value !== null) + { + $out .= "{$value} "; } - - $out .= "-{$name} $value "; } - return $out; + return $trim ? trim($out) : $out; } //-------------------------------------------------------------------- diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index fe7f4eff1b06..5eb0cf7f9f6d 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -1,4 +1,5 @@ parseCommand(); } - //-------------------------------------------------------------------- - /** * Returns the "path" of the request script so that it can be used * in routing to the appropriate controller/method. @@ -120,8 +114,6 @@ public function getPath(): string return empty($path) ? '' : $path; } - //-------------------------------------------------------------------- - /** * Returns an associative array of all CLI options found, with * their values. @@ -133,8 +125,6 @@ public function getOptions(): array return $this->options; } - //-------------------------------------------------------------------- - /** * Returns the path segments. * @@ -145,8 +135,6 @@ public function getSegments(): array return $this->segments; } - //-------------------------------------------------------------------- - /** * Returns the value for a single CLI option that was passed in. * @@ -159,8 +147,6 @@ public function getOption(string $key) return $this->options[$key] ?? null; } - //-------------------------------------------------------------------- - /** * Returns the options as a string, suitable for passing along on * the CLI to other commands. @@ -173,9 +159,11 @@ public function getOption(string $key) * * getOptionString() = '-foo bar -baz "queue some stuff"' * + * @param boolean $useLongOpts + * * @return string */ - public function getOptionString(): string + public function getOptionString(bool $useLongOpts = false): string { if (empty($this->options)) { @@ -186,14 +174,25 @@ public function getOptionString(): string foreach ($this->options as $name => $value) { + if ($useLongOpts && mb_strlen($name) > 1) + { + $out .= "--{$name} "; + } + else + { + $out .= "-{$name} "; + } + // If there's a space, we need to group // so it will pass correctly. - if (strpos($value, ' ') !== false) + if (mb_strpos($value, ' ') !== false) { - $value = '"' . $value . '"'; + $out .= '"' . $value . '" '; + } + elseif ($value !== null) + { + $out .= "{$value} "; } - - $out .= "-{$name} $value "; } return trim($out); diff --git a/tests/system/CLI/CLITest.php b/tests/system/CLI/CLITest.php index 9d54cf62e0c8..1779a2e208d9 100644 --- a/tests/system/CLI/CLITest.php +++ b/tests/system/CLI/CLITest.php @@ -280,12 +280,12 @@ public function testParseCommandMixed() 'b', 'c', 'd', - '-parm', + '--parm', 'pvalue', 'd2', 'da-sh', - '-fix', - '-opt-in', + '--fix', + '--opt-in', 'sure', ]; CLI::init(); @@ -295,6 +295,10 @@ public function testParseCommandMixed() $this->assertEquals('d', CLI::getSegment(3)); $this->assertEquals(['b', 'c', 'd', 'd2', 'da-sh'], CLI::getSegments()); $this->assertEquals(['parm' => 'pvalue', 'fix' => null, 'opt-in' => 'sure'], CLI::getOptions()); + $this->assertEquals('-parm pvalue -fix -opt-in sure ', CLI::getOptionString()); + $this->assertEquals('-parm pvalue -fix -opt-in sure', CLI::getOptionString(false, true)); + $this->assertEquals('--parm pvalue --fix --opt-in sure ', CLI::getOptionString(true)); + $this->assertEquals('--parm pvalue --fix --opt-in sure', CLI::getOptionString(true, true)); } public function testParseCommandOption() @@ -303,7 +307,7 @@ public function testParseCommandOption() 'ignored', 'b', 'c', - '-parm', + '--parm', 'pvalue', 'd', ]; @@ -311,6 +315,9 @@ public function testParseCommandOption() $this->assertEquals(['parm' => 'pvalue'], CLI::getOptions()); $this->assertEquals('pvalue', CLI::getOption('parm')); $this->assertEquals('-parm pvalue ', CLI::getOptionString()); + $this->assertEquals('-parm pvalue', CLI::getOptionString(false, true)); + $this->assertEquals('--parm pvalue ', CLI::getOptionString(true)); + $this->assertEquals('--parm pvalue', CLI::getOptionString(true, true)); $this->assertNull(CLI::getOption('bogus')); $this->assertEquals(['b', 'c', 'd'], CLI::getSegments()); } @@ -321,17 +328,20 @@ public function testParseCommandMultipleOptions() 'ignored', 'b', 'c', - '-parm', + '--parm', 'pvalue', 'd', - '-p2', - '-p3', + '--p2', + '--p3', 'value 3', ]; CLI::init(); $this->assertEquals(['parm' => 'pvalue', 'p2' => null, 'p3' => 'value 3'], CLI::getOptions()); $this->assertEquals('pvalue', CLI::getOption('parm')); - $this->assertEquals('-parm pvalue -p2 -p3 "value 3" ', CLI::getOptionString()); + $this->assertEquals('-parm pvalue -p2 -p3 "value 3" ', CLI::getOptionString()); + $this->assertEquals('-parm pvalue -p2 -p3 "value 3"', CLI::getOptionString(false, true)); + $this->assertEquals('--parm pvalue --p2 --p3 "value 3" ', CLI::getOptionString(true)); + $this->assertEquals('--parm pvalue --p2 --p3 "value 3"', CLI::getOptionString(true, true)); $this->assertEquals(['b', 'c', 'd'], CLI::getSegments()); } diff --git a/tests/system/HTTP/CLIRequestTest.php b/tests/system/HTTP/CLIRequestTest.php index 3e875292f69c..8af30829b7a4 100644 --- a/tests/system/HTTP/CLIRequestTest.php +++ b/tests/system/HTTP/CLIRequestTest.php @@ -59,9 +59,9 @@ public function testParsingOptions() 'users', '21', 'profile', - '-foo', + '--foo', 'bar', - '-foo-bar', + '--foo-bar', 'yes', ]; @@ -82,7 +82,7 @@ public function testParsingOptionDetails() 'users', '21', 'profile', - '-foo', + '--foo', 'bar', ]; @@ -100,17 +100,17 @@ public function testParsingOptionString() 'users', '21', 'profile', - '-foo', + '--foo', 'bar', - '-baz', + '--baz', 'queue some stuff', ]; // reinstantiate it to force parsing $this->request = new CLIRequest(new App()); - $expected = '-foo bar -baz "queue some stuff"'; - $this->assertEquals($expected, $this->request->getOptionString()); + $this->assertEquals('-foo bar -baz "queue some stuff"', $this->request->getOptionString()); + $this->assertEquals('--foo bar --baz "queue some stuff"', $this->request->getOptionString(true)); } public function testParsingNoOptions() @@ -136,15 +136,14 @@ public function testParsingPath() 'users', '21', 'profile', - '-foo', + '--foo', 'bar', ]; // reinstantiate it to force parsing $this->request = new CLIRequest(new App()); - $expected = 'users/21/profile'; - $this->assertEquals($expected, $this->request->getPath()); + $this->assertEquals('users/21/profile', $this->request->getPath()); } public function testParsingMalformed() @@ -154,19 +153,18 @@ public function testParsingMalformed() 'users', '21', 'pro-file', - '-foo', + '--foo', 'bar', - '-baz', + '--baz', 'queue some stuff', ]; // reinstantiate it to force parsing $this->request = new CLIRequest(new App()); - $expectedOptions = '-foo bar -baz "queue some stuff"'; - $expectedPath = 'users/21/pro-file'; - $this->assertEquals($expectedOptions, $this->request->getOptionString()); - $this->assertEquals($expectedPath, $this->request->getPath()); + $this->assertEquals('-foo bar -baz "queue some stuff"', $this->request->getOptionString()); + $this->assertEquals('--foo bar --baz "queue some stuff"', $this->request->getOptionString(true)); + $this->assertEquals('users/21/pro-file', $this->request->getPath()); } public function testParsingMalformed2() @@ -176,19 +174,18 @@ public function testParsingMalformed2() 'users', '21', 'profile', - '-foo', + '--foo', 'oops-bar', - '-baz', + '--baz', 'queue some stuff', ]; // reinstantiate it to force parsing $this->request = new CLIRequest(new App()); - $expectedOptions = '-foo oops-bar -baz "queue some stuff"'; - $expectedPath = 'users/21/profile'; - $this->assertEquals($expectedOptions, $this->request->getOptionString()); - $this->assertEquals($expectedPath, $this->request->getPath()); + $this->assertEquals('-foo oops-bar -baz "queue some stuff"', $this->request->getOptionString()); + $this->assertEquals('--foo oops-bar --baz "queue some stuff"', $this->request->getOptionString(true)); + $this->assertEquals('users/21/profile', $this->request->getPath()); } public function testParsingMalformed3() @@ -198,20 +195,19 @@ public function testParsingMalformed3() 'users', '21', 'profile', - '-foo', + '--foo', 'oops', 'bar', - '-baz', + '--baz', 'queue some stuff', ]; // reinstantiate it to force parsing $this->request = new CLIRequest(new App()); - $expectedOptions = '-foo oops -baz "queue some stuff"'; - $expectedPath = 'users/21/profile/bar'; - $this->assertEquals($expectedOptions, $this->request->getOptionString()); - $this->assertEquals($expectedPath, $this->request->getPath()); + $this->assertEquals('-foo oops -baz "queue some stuff"', $this->request->getOptionString()); + $this->assertEquals('--foo oops --baz "queue some stuff"', $this->request->getOptionString(true)); + $this->assertEquals('users/21/profile/bar', $this->request->getPath()); } //-------------------------------------------------------------------- From 8ae9c203e76134021d7134b9c13e1bbc87b54692 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 8 Sep 2020 21:08:48 +0800 Subject: [PATCH 108/328] Update docs to use longopts --- user_guide_src/source/cli/cli_generators.rst | 32 ++++++++++---------- user_guide_src/source/cli/cli_request.rst | 18 +++++++---- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/user_guide_src/source/cli/cli_generators.rst b/user_guide_src/source/cli/cli_generators.rst index 810866ed071a..44b23f6a03e9 100644 --- a/user_guide_src/source/cli/cli_generators.rst +++ b/user_guide_src/source/cli/cli_generators.rst @@ -43,11 +43,11 @@ Argument: Options: ======== -* ``-command``: The command name to run in spark. Defaults to ``command:name``. -* ``-group``: The group/namespace of the command. Defaults to ``CodeIgniter`` for basic commands, and ``Generators`` for generator commands. -* ``-type``: The type of command, whether a ``basic`` command or a ``generator`` command. Defaults to ``basic``. +* ``--command``: The command name to run in spark. Defaults to ``command:name``. +* ``--group``: The group/namespace of the command. Defaults to ``CodeIgniter`` for basic commands, and ``Generators`` for generator commands. +* ``--type``: The type of command, whether a ``basic`` command or a ``generator`` command. Defaults to ``basic``. * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. -* ``-force``: Set this flag to overwrite existing files on destination. +* ``--force``: Set this flag to overwrite existing files on destination. make:controller --------------- @@ -66,10 +66,10 @@ Argument: Options: ======== -* ``-bare``: Extends from ``CodeIgniter\Controller`` instead of ``BaseController``. -* ``-restful``: Extends from a RESTful resource. Choices are ``controller`` and ``presenter``. Defaults to ``controller``. +* ``--bare``: Extends from ``CodeIgniter\Controller`` instead of ``BaseController``. +* ``--restful``: Extends from a RESTful resource. Choices are ``controller`` and ``presenter``. Defaults to ``controller``. * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. -* ``-force``: Set this flag to overwrite existing files on destination. +* ``--force``: Set this flag to overwrite existing files on destination. make:entity ----------- @@ -109,7 +109,7 @@ Argument: Options: ======== * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. -* ``-force``: Set this flag to overwrite existing files on destination. +* ``--force``: Set this flag to overwrite existing files on destination. make:model ---------- @@ -128,11 +128,11 @@ Argument: Options: ======== -* ``-dbgroup``: Database group to use. Defaults to ``default``. -* ``-entity``: Set this flag to use an entity class as the return type. -* ``-table``: Supply a different table name. Defaults to the pluralized class name. +* ``--dbgroup``: Database group to use. Defaults to ``default``. +* ``--entity``: Set this flag to use an entity class as the return type. +* ``--table``: Supply a different table name. Defaults to the pluralized class name. * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. -* ``-force``: Set this flag to overwrite existing files on destination. +* ``--force``: Set this flag to overwrite existing files on destination. make:seeder ----------- @@ -152,7 +152,7 @@ Argument: Options: ======== * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. -* ``-force``: Set this flag to overwrite existing files on destination. +* ``--force``: Set this flag to overwrite existing files on destination. make:migration -------------- @@ -172,7 +172,7 @@ Argument: Options: ======== * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. -* ``-force``: Set this flag to overwrite existing files on destination. +* ``--force``: Set this flag to overwrite existing files on destination. session:migration ----------------- @@ -190,7 +190,7 @@ Options: * ``-g``: Set the database group. * ``-t``: Set the table name. Defaults to ``ci_sessions``. * ``-n``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``. -* ``-force``: Set this flag to overwrite existing files on destination. +* ``--force``: Set this flag to overwrite existing files on destination. .. note:: When running ``php spark help session:migration``, you will see that it has the argument ``name`` listed. This argument is not used as the class name is derived from the table name passed to the ``-t`` option. @@ -346,7 +346,7 @@ which is public and need not be overridden as it is essentially complete. .. note:: ``GeneratorCommand`` has the default argument of ``['name' => 'Class name']``. You can override the description by supplying the name in your ``$arguments`` property, e.g. ``['name' => 'Module class name']``. -.. note:: ``GeneratorCommand`` has the default options of ``-n`` and ``-force``. Child classes cannot override +.. note:: ``GeneratorCommand`` has the default options of ``-n`` and ``--force``. Child classes cannot override these two properties as they are crucial in the implementation of the code generation. .. note:: Generators are default listed under the ``Generators`` namespace because it is the default group diff --git a/user_guide_src/source/cli/cli_request.rst b/user_guide_src/source/cli/cli_request.rst index 70200e98613f..6932294aa2e0 100644 --- a/user_guide_src/source/cli/cli_request.rst +++ b/user_guide_src/source/cli/cli_request.rst @@ -14,28 +14,28 @@ Additional Accessors Returns an array of the command line arguments deemed to be part of a path:: - // command line: php index.php users 21 profile -foo bar + // command line: php index.php users 21 profile --foo bar echo $request->getSegments(); // ['users', '21', 'profile'] **getPath()** Returns the reconstructed path as a string:: - // command line: php index.php users 21 profile -foo bar + // command line: php index.php users 21 profile --foo bar echo $request->getPath(); // users/21/profile **getOptions()** Returns an array of the command line arguments deemed to be options:: - // command line: php index.php users 21 profile -foo bar + // command line: php index.php users 21 profile --foo bar echo $request->getOptions(); // ['foo' => 'bar'] **getOption($which)** Returns the value of a specific command line argument deemed to be an option:: - // command line: php index.php users 21 profile -foo bar + // command line: php index.php users 21 profile --foo bar echo $request->getOption('foo'); // bar echo $request->getOption('notthere'); // NULL @@ -43,5 +43,11 @@ Returns the value of a specific command line argument deemed to be an option:: Returns the reconstructed command line string for the options:: - // command line: php index.php users 21 profile -foo bar - echo $request->getOptionPath(); // -foo bar + // command line: php index.php users 21 profile --foo bar + echo $request->getOptionString(); // -foo bar + +Passing ``true`` to the first argument will try to write long options using two dashes:: + + // php index.php user 21 --foo bar -f + echo $request->getOptionString(); // -foo bar -f + echo $request->getOptionString(true); // --foo bar -f From 3f41b4f19dd823d24b1b8a490f83f7b322638503 Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 9 Sep 2020 15:27:54 +0000 Subject: [PATCH 109/328] Mock cache during testing --- system/Test/CIUnitTestCase.php | 26 ++++++++++++++++-------- tests/system/Commands/ClearCacheTest.php | 5 +++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index 5b8f81b7080a..e8dcaa7787c1 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -41,6 +41,7 @@ use CodeIgniter\Events\Events; use CodeIgniter\Session\Handlers\ArrayHandler; +use CodeIgniter\Test\Mock\MockCache; use CodeIgniter\Test\Mock\MockEmail; use CodeIgniter\Test\Mock\MockSession; use Config\Services; @@ -65,6 +66,7 @@ class CIUnitTestCase extends TestCase * @var array of methods */ protected $setUpMethods = [ + 'mockCache', 'mockEmail', 'mockSession', ]; @@ -120,16 +122,11 @@ protected function tearDown(): void //-------------------------------------------------------------------- /** - * Injects the mock session driver into Services + * Injects the mock Cache driver to prevent filesystem collisions */ - protected function mockSession() + protected function mockCache() { - $_SESSION = []; - - $config = config('App'); - $session = new MockSession(new ArrayHandler($config, '0.0.0.0'), $config); - - Services::injectMock('session', $session); + Services::injectMock('cache', new MockCache()); } /** @@ -140,6 +137,19 @@ protected function mockEmail() Services::injectMock('email', new MockEmail(config('Email'))); } + /** + * Injects the mock session driver into Services + */ + protected function mockSession() + { + $_SESSION = []; + + $config = config('App'); + $session = new MockSession(new ArrayHandler($config, '0.0.0.0'), $config); + + Services::injectMock('session', $session); + } + //-------------------------------------------------------------------- // Assertions //-------------------------------------------------------------------- diff --git a/tests/system/Commands/ClearCacheTest.php b/tests/system/Commands/ClearCacheTest.php index 4e2e0c71ea47..aea8d0a0cf8b 100644 --- a/tests/system/Commands/ClearCacheTest.php +++ b/tests/system/Commands/ClearCacheTest.php @@ -1,7 +1,9 @@ streamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); $this->streamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Make sure we are testing with the correct handler (override injections) + Services::injectMock('cache', CacheFactory::getHandler(config('Cache'))); } public function tearDown(): void From ccc984d129b0d7d9843014a97241f7143dad9051 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 9 Sep 2020 15:43:26 +0200 Subject: [PATCH 110/328] [ci skip] Changelog fixes for the user guide --- user_guide_src/source/changelogs/index.rst | 8 ++------ user_guide_src/source/changelogs/next.rst | 14 -------------- 2 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 user_guide_src/source/changelogs/next.rst diff --git a/user_guide_src/source/changelogs/index.rst b/user_guide_src/source/changelogs/index.rst index abda9b3cd4b1..aeba4b2490ef 100644 --- a/user_guide_src/source/changelogs/index.rst +++ b/user_guide_src/source/changelogs/index.rst @@ -5,17 +5,13 @@ Change Logs Version |version| ==================================================== -Release Date: Not Released +**List of all CodeIgniter4 releases** -**Next release of CodeIgniter4** - - -:doc:`See all the changes. ` +See all the changes. .. toctree:: :titlesonly: - next v4.0.5 v4.0.4 v4.0.3 diff --git a/user_guide_src/source/changelogs/next.rst b/user_guide_src/source/changelogs/next.rst deleted file mode 100644 index 94e4dcad7f4c..000000000000 --- a/user_guide_src/source/changelogs/next.rst +++ /dev/null @@ -1,14 +0,0 @@ -Version |version| -==================================================== - -Release Date: Not released - -**Next alpha release of CodeIgniter4** - - -The list of changed files follows, with PR numbers shown. - - -PRs merged: ------------ - From 967027e1bbc94e1bace470979c306b35ff0ece13 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 9 Sep 2020 15:44:18 +0200 Subject: [PATCH 111/328] Cast null value to string --- system/HTTP/Request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/HTTP/Request.php b/system/HTTP/Request.php index 7c2c3fac0ac3..8c64f1623ed2 100644 --- a/system/HTTP/Request.php +++ b/system/HTTP/Request.php @@ -236,7 +236,7 @@ public function getIPAddress(): string */ public function isValidIP(string $ip = null, string $which = null): bool { - switch (strtolower($which)) + switch (strtolower((string) $which)) { case 'ipv4': $which = FILTER_FLAG_IPV4; From 84833c8f4a4367c66f708e8b55e9d0225ff9f966 Mon Sep 17 00:00:00 2001 From: Kang Jing Date: Fri, 11 Sep 2020 16:35:51 +0800 Subject: [PATCH 112/328] [CI SKIP] Typo --- user_guide_src/source/cli/cli_library.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/cli/cli_library.rst b/user_guide_src/source/cli/cli_library.rst index fca90f546f33..192950ccad62 100644 --- a/user_guide_src/source/cli/cli_library.rst +++ b/user_guide_src/source/cli/cli_library.rst @@ -166,11 +166,11 @@ every line after the first line, so that you will have a crisp column edge on th // to determine the width of the left column $maxlen = max(array_map('strlen', $titles)); - for ($i=0; $i <= count($titles); $i++) + for ($i=0; $i < count($titles); $i++) { CLI::write( // Display the title on the left of the row - $title[$i] . ' ' . + $titles[$i] . ' ' . // Wrap the descriptions in a right-hand column // with its left side 3 characters wider than // the longest item on the left. @@ -182,7 +182,7 @@ Would create something like this: .. code-block:: none - task1a Lorem Ipsum is simply dummy + task1a Lorem Ipsum is simply dummy text of the printing and typesetting industry. task1abc Lorem Ipsum has been the industry's From 5001b3ff2115545406fa841dc1cc7b061596f047 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 12 Sep 2020 00:22:37 +0700 Subject: [PATCH 113/328] using new self in URI::resolveRelativeURI() --- system/HTTP/URI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 74dd2f75286f..4d7b6c388250 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -1075,7 +1075,7 @@ public function resolveRelativeURI(string $uri) * NOTE: We don't use removeDotSegments in this * algorithm since it's already done by this line! */ - $relative = new URI(); + $relative = new self(); $relative->setURI($uri); if ($relative->getScheme() === $this->getScheme()) From 10b4630c87f455aba47b20f40b17c8057ff3ba3b Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 14 Sep 2020 01:18:11 +0800 Subject: [PATCH 114/328] Eliminate 'App' in migrate:status if different from APP_NAMESPACE --- phpstan.neon | 1 + system/Commands/Database/MigrateStatus.php | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 4cb72e5fa010..ed9fc6e08449 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -47,3 +47,4 @@ parameters: dynamicConstantNames: - ENVIRONMENT - CI_DEBUG + - APP_NAMESPACE diff --git a/system/Commands/Database/MigrateStatus.php b/system/Commands/Database/MigrateStatus.php index 021a36198cc1..dd543582f87b 100644 --- a/system/Commands/Database/MigrateStatus.php +++ b/system/Commands/Database/MigrateStatus.php @@ -50,7 +50,6 @@ */ class MigrateStatus extends BaseCommand { - /** * The group the command is lumped under * when listing commands. @@ -141,6 +140,11 @@ public function run(array $params) continue; } + if (APP_NAMESPACE !== 'App' && $namespace === 'App') + { + continue; // @codeCoverageIgnore + } + $runner->setNamespace($namespace); $migrations = $runner->findMigrations(); @@ -170,6 +174,7 @@ public function run(array $params) foreach ($migrations as $uid => $migration) { $date = ''; + foreach ($history as $row) { if ($runner->getObjectUid($row) !== $uid) @@ -179,8 +184,11 @@ public function run(array $params) $date = date('Y-m-d H:i:s', $row->time); } - CLI::write(str_pad(' ' . $migration->name, $max + 6) . ($date ? $date : '---')); + + CLI::write(str_pad(' ' . $migration->name, $max + 6) . ($date ?: '---')); } + + CLI::newLine(); } if (! $found) @@ -188,5 +196,4 @@ public function run(array $params) CLI::error(lang('Migrations.noneFound')); } } - } From 5da096c7d85e89c63b4350388d955142eafef41f Mon Sep 17 00:00:00 2001 From: devorama <33496470+devorama@users.noreply.github.com> Date: Mon, 14 Sep 2020 07:05:48 +0200 Subject: [PATCH 115/328] Remove the autoinc logic as it dont seem to work, it creates a constraint that is not autoinc each time. By doing it the same way as the other databases one can use it now as a autoinc and not get not null errors when inserting data Signed-off-by: devorama <33496470+devorama@users.noreply.github.com> --- .../20160428212500_Create_test_tables.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php b/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php index de0f2c3259a9..8ba2b3814fa5 100644 --- a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php +++ b/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php @@ -4,15 +4,12 @@ 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, + 'auto_increment'=> true, ], 'name' => [ 'type' => 'VARCHAR', @@ -47,7 +44,7 @@ public function up() 'id' => [ 'type' => 'INTEGER', 'constraint' => 3, - $unique_or_auto => true, + 'auto_increment'=> true, ], 'name' => [ 'type' => 'VARCHAR', @@ -81,7 +78,7 @@ public function up() 'id' => [ 'type' => 'INTEGER', 'constraint' => 3, - $unique_or_auto => true, + 'auto_increment'=> true, ], 'key' => [ 'type' => 'VARCHAR', @@ -99,7 +96,7 @@ public function up() 'id' => [ 'type' => 'INTEGER', //must be interger else SQLite3 error on not null for autoinc field 'constraint' => 20, - $unique_or_auto => true, + 'auto_increment'=> true, ], 'type_varchar' => [ 'type' => 'VARCHAR', @@ -208,7 +205,7 @@ public function up() 'id' => [ 'type' => 'INTEGER', 'constraint' => 3, - $unique_or_auto => true, + 'auto_increment'=> true, ], 'name' => [ 'type' => 'VARCHAR', @@ -231,7 +228,7 @@ public function up() 'id' => [ 'type' => 'INTEGER', 'constraint' => 3, - $unique_or_auto => true, + 'auto_increment'=> true, ], 'key' => [ 'type' => 'VARCHAR', From c316913175b35e996ef8853be472dcfb26e62af4 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 15 Sep 2020 00:32:50 +0800 Subject: [PATCH 116/328] Write migrate status in tabular format --- system/Commands/Database/MigrateStatus.php | 69 ++++++++++++---------- system/Language/en/Migrations.php | 8 ++- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/system/Commands/Database/MigrateStatus.php b/system/Commands/Database/MigrateStatus.php index dd543582f87b..b1d8e0edb122 100644 --- a/system/Commands/Database/MigrateStatus.php +++ b/system/Commands/Database/MigrateStatus.php @@ -118,10 +118,9 @@ class MigrateStatus extends BaseCommand public function run(array $params) { $runner = Services::migrations(); + $group = $params['g'] ?? CLI::getOption('g'); - $group = $params['g'] ?? CLI::getOption('g'); - - if (! is_null($group)) + if (is_string($group)) { $runner->setGroup($group); } @@ -129,10 +128,9 @@ public function run(array $params) // Get all namespaces $namespaces = Services::autoloader()->getNamespace(); - // Determines whether any migrations were found - $found = false; + // Collection of migration status + $status = []; - // Loop for all $namespaces foreach ($namespaces as $namespace => $path) { if (in_array($namespace, $this->ignoredNamespaces, true)) @@ -145,55 +143,64 @@ public function run(array $params) continue; // @codeCoverageIgnore } - $runner->setNamespace($namespace); - $migrations = $runner->findMigrations(); + $migrations = $runner->findNamespaceMigrations($namespace); if (empty($migrations)) { continue; } - $found = true; $history = $runner->getHistory(); - - CLI::write($namespace); - ksort($migrations); - $max = 0; - foreach ($migrations as $version => $migration) - { - $file = substr($migration->name, strpos($migration->name, $version . '_')); - $migrations[$version]->name = $file; - - $max = max($max, strlen($file)); - } - - CLI::write(' ' . str_pad(lang('Migrations.filename'), $max + 4) . lang('Migrations.on'), 'yellow'); - foreach ($migrations as $uid => $migration) { - $date = ''; + $migrations[$uid]->name = mb_substr($migration->name, mb_strpos($migration->name, $uid . '_')); + + $date = '---'; + $group = '---'; + $batch = '---'; foreach ($history as $row) { - if ($runner->getObjectUid($row) !== $uid) + if ($runner->getObjectUid($row) !== $migration->uid) { continue; } - $date = date('Y-m-d H:i:s', $row->time); + $date = date('Y-m-d H:i:s', $row->time); + $group = $row->group; + $batch = $row->batch; } - CLI::write(str_pad(' ' . $migration->name, $max + 6) . ($date ?: '---')); + $status[] = [ + $namespace, + $migration->version, + $migration->name, + $group, + $date, + $batch, + ]; } - - CLI::newLine(); } - if (! $found) + if ($status) { - CLI::error(lang('Migrations.noneFound')); + $headers = [ + CLI::color(lang('Migrations.namespace'), 'yellow'), + CLI::color(lang('Migrations.version'), 'yellow'), + CLI::color(lang('Migrations.filename'), 'yellow'), + CLI::color(lang('Migrations.group'), 'yellow'), + CLI::color(str_replace(': ', '', lang('Migrations.on')), 'yellow'), + CLI::color(lang('Migrations.batch'), 'yellow'), + ]; + + CLI::table($status, $headers); + + return; } + + CLI::error(lang('Migrations.noneFound'), 'light_gray', 'red'); + CLI::newLine(); } } diff --git a/system/Language/en/Migrations.php b/system/Language/en/Migrations.php index 80ec470fe326..4bc0b71ec0f5 100644 --- a/system/Language/en/Migrations.php +++ b/system/Language/en/Migrations.php @@ -45,13 +45,17 @@ 'toVersion' => 'Migrating to current version...', 'rollingBack' => 'Rolling back migrations to batch: ', 'noneFound' => 'No migrations were found.', - 'on' => 'Migrated On: ', 'migSeeder' => 'Seeder name', 'migMissingSeeder' => 'You must provide a seeder name.', 'nameSeeder' => 'Name the seeder file', 'removed' => 'Rolling back: ', 'added' => 'Running: ', - 'version' => 'Version', + // Migrate Status + 'namespace' => 'Namespace', 'filename' => 'Filename', + 'version' => 'Version', + 'group' => 'Group', + 'on' => 'Migrated On: ', + 'batch' => 'Batch', ]; From b523f43538d920badcdf80092059531bd729e758 Mon Sep 17 00:00:00 2001 From: MGatner Date: Thu, 10 Sep 2020 13:49:27 +0000 Subject: [PATCH 117/328] Add post-migration event --- system/Database/MigrationRunner.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index c06a1be2b185..9d27971969f6 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -39,6 +39,7 @@ namespace CodeIgniter\Database; use CodeIgniter\CLI\CLI; +use CodeIgniter\Events\Events; use CodeIgniter\Exceptions\ConfigException; use Config\Migrations as MigrationsConfig; use Config\Services; @@ -246,6 +247,10 @@ public function latest(string $group = null) } } + $data = get_object_vars($this); + $data['method'] = 'latest'; + Events::trigger('migrate', $data); + return true; } @@ -369,6 +374,10 @@ public function regress(int $targetBatch = 0, string $group = null) } } + $data = get_object_vars($this); + $data['method'] = 'regress'; + Events::trigger('migrate', $data); + // Restore the namespace $this->namespace = $tmpNamespace; From f475b6771be866f8766e3d7fb75b589afb43cfcf Mon Sep 17 00:00:00 2001 From: MGatner Date: Fri, 11 Sep 2020 00:32:26 +0000 Subject: [PATCH 118/328] Add tests and docs --- .../Migrations/MigrationRunnerTest.php | 30 ++++++++++++++++++- user_guide_src/source/extending/events.rst | 2 ++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/system/Database/Migrations/MigrationRunnerTest.php b/tests/system/Database/Migrations/MigrationRunnerTest.php index 95dd5fc34c27..ae0dc2545747 100644 --- a/tests/system/Database/Migrations/MigrationRunnerTest.php +++ b/tests/system/Database/Migrations/MigrationRunnerTest.php @@ -255,7 +255,7 @@ public function testVersionReturnsUpDownSuccess() $this->assertFalse($this->db->tableExists('foo')); } - public function testProgressSuccess() + public function testLatestSuccess() { $config = $this->config; $runner = new MigrationRunner($config); @@ -294,6 +294,34 @@ public function testRegressSuccess() $this->assertEmpty($history); } + public function testLatestTriggersEvent() + { + $result = null; + + Events::on('migrate', function ($arg) use (&$result) { + $result = $arg; + }); + + $this->testLatestSuccess(); + + $this->assertIsArray($result); + $this->assertEquals('latest', $result['method']); + } + + public function testRegressTriggersEvent() + { + $result = null; + + Events::on('migrate', function ($arg) use (&$result) { + $result = $arg; + }); + + $this->testRegressSuccess(); + + $this->assertIsArray($result); + $this->assertEquals('regress', $result['method']); + } + public function testHistoryRecordsBatches() { $config = $this->config; diff --git a/user_guide_src/source/extending/events.rst b/user_guide_src/source/extending/events.rst index f73a2b1a6882..1ee0e0150b94 100644 --- a/user_guide_src/source/extending/events.rst +++ b/user_guide_src/source/extending/events.rst @@ -109,3 +109,5 @@ The following is a list of available event points within the CodeIgniter core co * **post_controller_constructor** Called immediately after your controller is instantiated, but prior to any method calls happening. * **post_system** Called after the final rendered page is sent to the browser, at the end of system execution after the finalized data is sent to the browser. * **email** Called after an email sent successfully from ``CodeIgniter\Email\Email``. Receives an array of the ``Email`` class's properties as a parameter. +* **DBQuery** Called after a successfully-completed database query. Receives the ``Query`` object. +* **migrate** Called after a successful migration call to ``latest()`` or ``regress()``. Receives the current properties of ``MigrationRunner`` as well as the name of the method. From 4a2c000cb91da98cb0ebc139c759c781cb8a4836 Mon Sep 17 00:00:00 2001 From: MGatner Date: Fri, 11 Sep 2020 01:20:38 +0000 Subject: [PATCH 119/328] Provide missing class --- tests/system/Database/Migrations/MigrationRunnerTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/system/Database/Migrations/MigrationRunnerTest.php b/tests/system/Database/Migrations/MigrationRunnerTest.php index ae0dc2545747..9d9a274aee31 100644 --- a/tests/system/Database/Migrations/MigrationRunnerTest.php +++ b/tests/system/Database/Migrations/MigrationRunnerTest.php @@ -1,5 +1,6 @@ Date: Mon, 14 Sep 2020 14:24:46 +0000 Subject: [PATCH 120/328] Rework migration event tests --- .../Migrations/MigrationRunnerTest.php | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/tests/system/Database/Migrations/MigrationRunnerTest.php b/tests/system/Database/Migrations/MigrationRunnerTest.php index 9d9a274aee31..ead714a06a17 100644 --- a/tests/system/Database/Migrations/MigrationRunnerTest.php +++ b/tests/system/Database/Migrations/MigrationRunnerTest.php @@ -258,12 +258,10 @@ public function testVersionReturnsUpDownSuccess() public function testLatestSuccess() { - $config = $this->config; - $runner = new MigrationRunner($config); - $runner->setSilent(false); - $runner->clearHistory(); - - $runner = $runner->setNamespace('Tests\Support\MigrationTestMigrations'); + $runner = new MigrationRunner($this->config); + $runner->setSilent(false) + ->setNamespace('Tests\Support\MigrationTestMigrations') + ->clearHistory(); $runner->latest(); $version = $runner->getBatchEnd($runner->getLastBatch()); @@ -278,14 +276,14 @@ public function testLatestSuccess() public function testRegressSuccess() { - $config = $this->config; - $runner = new MigrationRunner($config); - $runner->setSilent(false); + $runner = new MigrationRunner($this->config); + $runner->setSilent(false) + ->setNamespace('Tests\Support\MigrationTestMigrations') + ->clearHistory(); - $runner = $runner->setNamespace('Tests\Support\MigrationTestMigrations'); $runner->latest(); - $runner->regress(); + $version = $runner->getBatchEnd($runner->getLastBatch()); $this->assertEquals(0, $version); @@ -297,13 +295,17 @@ public function testRegressSuccess() public function testLatestTriggersEvent() { - $result = null; + $runner = new MigrationRunner($this->config); + $runner->setSilent(false) + ->setNamespace('Tests\Support\MigrationTestMigrations') + ->clearHistory(); + $result = null; Events::on('migrate', function ($arg) use (&$result) { $result = $arg; }); - $this->testLatestSuccess(); + $runner->latest(); $this->assertIsArray($result); $this->assertEquals('latest', $result['method']); @@ -311,13 +313,18 @@ public function testLatestTriggersEvent() public function testRegressTriggersEvent() { - $result = null; + $runner = new MigrationRunner($this->config); + $runner->setSilent(false) + ->setNamespace('Tests\Support\MigrationTestMigrations') + ->clearHistory(); + $result = null; Events::on('migrate', function ($arg) use (&$result) { $result = $arg; }); - $this->testRegressSuccess(); + $runner->latest(); + $runner->regress(); $this->assertIsArray($result); $this->assertEquals('regress', $result['method']); From 1467c0939337619831ad00dca93fa23e59085cf7 Mon Sep 17 00:00:00 2001 From: MGatner Date: Mon, 14 Sep 2020 16:16:06 +0000 Subject: [PATCH 121/328] Reinstate Events after test --- tests/system/Events/EventsTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/system/Events/EventsTest.php b/tests/system/Events/EventsTest.php index 81def6cbcad2..91b80c5495d0 100644 --- a/tests/system/Events/EventsTest.php +++ b/tests/system/Events/EventsTest.php @@ -21,6 +21,11 @@ protected function setUp(): void Events::removeAllListeners(); } + protected function tearDown(): void + { + Events::simulate(false); + } + //-------------------------------------------------------------------- /** From 37f48130468077b80662ac44baaf9ee035a41671 Mon Sep 17 00:00:00 2001 From: MGatner Date: Tue, 15 Sep 2020 14:08:03 -0400 Subject: [PATCH 122/328] [ci skip] Fix pasted comments --- system/Database/ModelFactory.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/system/Database/ModelFactory.php b/system/Database/ModelFactory.php index 6efc3448f6fd..e33a174d7e05 100644 --- a/system/Database/ModelFactory.php +++ b/system/Database/ModelFactory.php @@ -13,10 +13,9 @@ class ModelFactory static private $instances = []; /** - * Create new configuration instances or return - * a shared instance + * Creates new Model instances or returns a shared instance * - * @param string $name Configuration name + * @param string $name Model name, namespace optional * @param boolean $getShared Use shared instance * @param ConnectionInterface $connection * @@ -62,7 +61,7 @@ public static function reset() } /** - * Find configuration class and create instance + * Finds a Model class and creates an instance * * @param string $name Classname * @param ConnectionInterface|null $connection From 1a26156620717efe46638983df43ac0a00803a11 Mon Sep 17 00:00:00 2001 From: MGatner Date: Mon, 14 Sep 2020 15:09:47 +0000 Subject: [PATCH 123/328] Unify QA bootstrap files --- composer.json | 1 + phpstan.neon => phpstan.neon.dist | 2 +- qa-bootstrap.php | 9 --------- system/Commands/Server/Serve.php | 2 +- system/Common.php | 3 +-- system/Config/AutoloadConfig.php | 2 +- system/Debug/Exceptions.php | 4 ++-- system/Log/Logger.php | 2 +- system/Test/CIDatabaseTestCase.php | 2 +- system/Test/bootstrap.php | 8 ++++++++ 10 files changed, 17 insertions(+), 18 deletions(-) rename phpstan.neon => phpstan.neon.dist (99%) delete mode 100644 qa-bootstrap.php diff --git a/composer.json b/composer.json index 123dc7b7a15b..dbbd2661fa90 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,7 @@ "CodeIgniter\\ComposerScripts::postUpdate", "bash admin/setup.sh" ], + "analyze": "phpstan analyze", "test": "phpunit" }, "support": { diff --git a/phpstan.neon b/phpstan.neon.dist similarity index 99% rename from phpstan.neon rename to phpstan.neon.dist index ed9fc6e08449..63f070ab6ab1 100644 --- a/phpstan.neon +++ b/phpstan.neon.dist @@ -6,7 +6,7 @@ parameters: - system treatPhpDocTypesAsCertain: false bootstrapFiles: - - qa-bootstrap.php + - system/Test/bootstrap.php excludes_analyse: - app/Views/errors/cli/* - app/Views/errors/html/* diff --git a/qa-bootstrap.php b/qa-bootstrap.php deleted file mode 100644 index 885aa75820ef..000000000000 --- a/qa-bootstrap.php +++ /dev/null @@ -1,9 +0,0 @@ -psr4['Tests\Support'] = SUPPORTPATH; // @phpstan-ignore-line + $this->psr4['Tests\Support'] = SUPPORTPATH; $this->classmap['CodeIgniter\Log\TestLogger'] = SYSTEMPATH . 'Test/TestLogger.php'; $this->classmap['CIDatabaseTestCase'] = SYSTEMPATH . 'Test/CIDatabaseTestCase.php'; } diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index c2332bdaa9d5..3476da6b28d9 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -389,8 +389,8 @@ public static function cleanPath(string $file): string case strpos($file, SYSTEMPATH) === 0: $file = 'SYSTEMPATH' . DIRECTORY_SEPARATOR . substr($file, strlen(SYSTEMPATH)); break; - case strpos($file, FCPATH) === 0: // @phpstan-ignore-line - $file = 'FCPATH' . DIRECTORY_SEPARATOR . substr($file, strlen(FCPATH)); // @phpstan-ignore-line + 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)); diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 5d4c14d544f7..e62952a3c593 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -534,7 +534,7 @@ protected function cleanFileNames(string $file): string $file = str_replace(APPPATH, 'APPPATH/', $file); $file = str_replace(SYSTEMPATH, 'SYSTEMPATH/', $file); - return str_replace(FCPATH, 'FCPATH/', $file); // @phpstan-ignore-line + return str_replace(FCPATH, 'FCPATH/', $file); } //-------------------------------------------------------------------- diff --git a/system/Test/CIDatabaseTestCase.php b/system/Test/CIDatabaseTestCase.php index ac7092f65abe..878fc59e2cfd 100644 --- a/system/Test/CIDatabaseTestCase.php +++ b/system/Test/CIDatabaseTestCase.php @@ -72,7 +72,7 @@ class CIDatabaseTestCase extends CIUnitTestCase * * @var string */ - protected $basePath = SUPPORTPATH . 'Database'; // @phpstan-ignore-line + protected $basePath = SUPPORTPATH . 'Database'; /** * The namespace(s) to help us find the migration classes. diff --git a/system/Test/bootstrap.php b/system/Test/bootstrap.php index af64c92d3d04..00765697902d 100644 --- a/system/Test/bootstrap.php +++ b/system/Test/bootstrap.php @@ -7,6 +7,14 @@ // Make sure it recognizes that we're testing. $_SERVER['CI_ENVIRONMENT'] = 'testing'; define('ENVIRONMENT', 'testing'); +defined('CI_DEBUG') || define('CI_DEBUG', true); + +// Often these constants are pre-defined, but query the current directory structure as a fallback +defined('HOMEPATH') || define('HOMEPATH', realpath(rtrim(getcwd(), '\\/ ')) . DIRECTORY_SEPARATOR); +$source = is_dir(HOMEPATH . 'app') ? HOMEPATH : 'vendor/codeigniter4/codeigniter4/'; +defined('CONFIGPATH') || define('CONFIGPATH', realpath($source . 'app/Config') . DIRECTORY_SEPARATOR); +defined('PUBLICPATH') || define('PUBLICPATH', realpath($source . 'app/Config') . DIRECTORY_SEPARATOR); +unset($source); // Load framework paths from their config file require CONFIGPATH . 'Paths.php'; From 62d22c868365d86435785237434bcb23b463826b Mon Sep 17 00:00:00 2001 From: MGatner Date: Mon, 14 Sep 2020 13:52:57 -0400 Subject: [PATCH 124/328] Add framework dir to fallbacks Co-authored-by: Abdul Malik Ikhsan --- system/Test/bootstrap.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/system/Test/bootstrap.php b/system/Test/bootstrap.php index 00765697902d..f368350aa457 100644 --- a/system/Test/bootstrap.php +++ b/system/Test/bootstrap.php @@ -11,7 +11,11 @@ // Often these constants are pre-defined, but query the current directory structure as a fallback defined('HOMEPATH') || define('HOMEPATH', realpath(rtrim(getcwd(), '\\/ ')) . DIRECTORY_SEPARATOR); -$source = is_dir(HOMEPATH . 'app') ? HOMEPATH : 'vendor/codeigniter4/codeigniter4/'; +$source = is_dir(HOMEPATH . 'app') + ? HOMEPATH + : (is_dir('vendor/codeigniter4/framework/') + ? 'vendor/codeigniter4/framework/' + : 'vendor/codeigniter4/codeigniter4/'); defined('CONFIGPATH') || define('CONFIGPATH', realpath($source . 'app/Config') . DIRECTORY_SEPARATOR); defined('PUBLICPATH') || define('PUBLICPATH', realpath($source . 'app/Config') . DIRECTORY_SEPARATOR); unset($source); From 6d39ae89a9763f4f10b06d843dca78f4c266a6ac Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 16 Sep 2020 01:24:48 +0000 Subject: [PATCH 125/328] Fix path --- system/Test/bootstrap.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/system/Test/bootstrap.php b/system/Test/bootstrap.php index f368350aa457..3d5e822691b2 100644 --- a/system/Test/bootstrap.php +++ b/system/Test/bootstrap.php @@ -11,13 +11,13 @@ // Often these constants are pre-defined, but query the current directory structure as a fallback defined('HOMEPATH') || define('HOMEPATH', realpath(rtrim(getcwd(), '\\/ ')) . DIRECTORY_SEPARATOR); -$source = is_dir(HOMEPATH . 'app') - ? HOMEPATH - : (is_dir('vendor/codeigniter4/framework/') - ? 'vendor/codeigniter4/framework/' - : 'vendor/codeigniter4/codeigniter4/'); +$source = is_dir(HOMEPATH . 'app') + ? HOMEPATH + : (is_dir('vendor/codeigniter4/framework/') + ? 'vendor/codeigniter4/framework/' + : 'vendor/codeigniter4/codeigniter4/'); defined('CONFIGPATH') || define('CONFIGPATH', realpath($source . 'app/Config') . DIRECTORY_SEPARATOR); -defined('PUBLICPATH') || define('PUBLICPATH', realpath($source . 'app/Config') . DIRECTORY_SEPARATOR); +defined('PUBLICPATH') || define('PUBLICPATH', realpath($source . 'public') . DIRECTORY_SEPARATOR); unset($source); // Load framework paths from their config file From b57a0728a62dd19e3d19c4ed24ec8da1f1e05893 Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 16 Sep 2020 01:25:00 +0000 Subject: [PATCH 126/328] Add bootstap to Guide --- user_guide_src/source/installation/running.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/user_guide_src/source/installation/running.rst b/user_guide_src/source/installation/running.rst index 05cc936026f6..d178262e3659 100644 --- a/user_guide_src/source/installation/running.rst +++ b/user_guide_src/source/installation/running.rst @@ -208,3 +208,15 @@ Once set up, you can then launch your webapp inside a VM, with the command:: Your webapp will be accessible at ``http://localhost:8080``, with the code coverage report for your build at ``http://localhost:8081`` and the user guide for it at ``http://localhost:8082``. + +Bootstrapping the App +================================================= + +In some scenarios you will want to load the framework without actually running the whole +application. This is particularly useful for unit testing your project, but may also be +handy for using third-party tools to analyze and modify your code. The framework comes +with a separate bootstrap script specifically for this scenario: ``system/Test/bootstrap.php``. + +Most of the paths to your project are defined during the bootstrap process. You may use +pre-defined constants to override these, but when using the defaults be sure that your +paths align with the expected directory structure for your installation method. From f2aee59fe45b3bd7ad027e9ca79e0670e8551541 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 16 Sep 2020 14:17:21 +0200 Subject: [PATCH 127/328] Fix some user guide syntax mistakes [ci skip] --- user_guide_src/source/database/configuration.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index ffe0bdeed842..e09ecde20139 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -53,12 +53,12 @@ driver's underlying native PHP extension, like this:: .. note:: If you do not specify a DSN string for a driver that requires it, CodeIgniter will try to build it with the rest of the provided settings. -You can also set a Data Source Name in universal manner (URL like). In that case DSNs must have this prototype: +You can also set a Data Source Name in universal manner (URL like). In that case DSNs must have this prototype:: - $default['DSN'] = 'DBDriver://username:password@hostname:port/database'; + default['DSN'] = 'DBDriver://username:password@hostname:port/database'; To override default config values when connecting with a universal version of the DSN string, -add the config variables as a query string: +add the config variables as a query string:: // MySQLi $default['DSN'] = 'MySQLi://username:password@hostname:3306/database?charset=utf8&DBCollat=utf8_general_ci'; From 64cdf0572173ada7df29ed144c7ed82c39ce53c0 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 16 Sep 2020 14:19:00 +0200 Subject: [PATCH 128/328] Fix syntax mistakes --- user_guide_src/source/database/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index e09ecde20139..48c4cbc663a7 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -55,7 +55,7 @@ driver's underlying native PHP extension, like this:: You can also set a Data Source Name in universal manner (URL like). In that case DSNs must have this prototype:: - default['DSN'] = 'DBDriver://username:password@hostname:port/database'; + $default['DSN'] = 'DBDriver://username:password@hostname:port/database'; To override default config values when connecting with a universal version of the DSN string, add the config variables as a query string:: From 9e1a2d6446404240d758be561117ab855af5c14c Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 16 Sep 2020 14:02:09 +0200 Subject: [PATCH 129/328] Make current_url() function respect forceGlobalSecureRequests config setting --- system/HTTP/URI.php | 9 ++++++++- tests/system/HTTP/URITest.php | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 4d7b6c388250..df57a86fcda2 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -596,7 +596,8 @@ public function __toString(): string // If hosted in a sub-folder, we will have additional // segments that show up prior to the URI path we just // grabbed from the request, so add it on if necessary. - $baseUri = new self(config(\Config\App::class)->baseURL); + $config = config(\Config\App::class); + $baseUri = new self($config->baseURL); $basePath = trim($baseUri->getPath(), '/') . '/'; $path = $this->getPath(); $trimPath = ltrim($path, '/'); @@ -606,6 +607,12 @@ public function __toString(): string $path = $basePath . $trimPath; } + // force https if needed + if ($config->forceGlobalSecureRequests) + { + $this->setScheme('https'); + } + return static::createURIString( $this->getScheme(), $this->getAuthority(), $path, // Absolute URIs should use a "/" for an empty path $this->getQuery(), $this->getFragment() diff --git a/tests/system/HTTP/URITest.php b/tests/system/HTTP/URITest.php index d492ab66a6d8..b96bf8c01fa4 100644 --- a/tests/system/HTTP/URITest.php +++ b/tests/system/HTTP/URITest.php @@ -1,6 +1,7 @@ assertEquals($uri->getPath(), $request->uri->getPath()); } + public function testForceGlobalSecureRequests() + { + Services::reset(); + + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/ci/v4/controller/method'; + + $config = new App(); + $config->baseURL = 'http://example.com/ci/v4'; + $config->indexPage = 'index.php'; + $config->forceGlobalSecureRequests = true; + + Config::injectMock('App', $config); + + $request = Services::request($config); + $request->uri = new URI('http://example.com/ci/v4/controller/method'); + + Services::injectMock('request', $request); + + // going through request + $this->assertEquals('https://example.com/ci/v4/controller/method', (string) $request->uri); + + // standalone + $uri = new URI('http://example.com/ci/v4/controller/method'); + $this->assertEquals('https://example.com/ci/v4/controller/method', (string) $uri); + + $this->assertEquals($uri->getPath(), $request->uri->getPath()); + } + public function testZeroAsURIPath() { $url = 'http://example.com/0'; From 3ea715b9cf43e4d1c38880f0dae07c7e9a426e6e Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 16 Sep 2020 14:52:15 +0000 Subject: [PATCH 130/328] Add method to afterFind event data --- system/Model.php | 11 ++++++++--- user_guide_src/source/models/model.rst | 10 ++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/system/Model.php b/system/Model.php index 862e4a344c13..008a57e2b2ca 100644 --- a/system/Model.php +++ b/system/Model.php @@ -430,8 +430,9 @@ public function find($id = null) } $eventData = [ - 'id' => $id, - 'data' => $row, + 'id' => $id, + 'data' => $row, + 'method' => 'find', ]; if ($this->tempAllowCallbacks) { @@ -508,6 +509,7 @@ public function findAll(int $limit = 0, int $offset = 0) 'data' => $row, 'limit' => $limit, 'offset' => $offset, + 'method' => 'findAll', ]; if ($this->tempAllowCallbacks) { @@ -567,7 +569,10 @@ public function first() $row = $row->getFirstRow($this->tempReturnType); - $eventData = ['data' => $row]; + $eventData = [ + 'data' => $row, + 'method' => 'first', + ]; if ($this->tempAllowCallbacks) { $eventData = $this->trigger('afterFind', $eventData); diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index f4903f27d1ce..b326ddc89e6f 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -795,14 +795,12 @@ beforeUpdate **id** = the array of primary keys of the rows being updated. afterUpdate **id** = the array of primary keys of the rows being updated. **data** = the key/value pairs being updated. **result** = the results of the update() method used through the Query Builder. -afterFind Varies by find* method. See the following: +beforeFind The name of the calling **method**, with these additional fields: +- first() No additional fields - find() **id** = the primary key of the row being searched for. - **data** = The resulting row of data, or null if no result found. -- findAll() **data** = the resulting rows of data, or null if no result found. - **limit** = the number of rows to find. +- findAll() **limit** = the number of rows to find. **offset** = the number of rows to skip during the search. -- first() **data** = the resulting row found during the search, or null if none found. -beforeFind Same as **afterFind** but with the name of the calling **$method** instead of **$data**. +afterFind Same as **beforeFind** but including the resulting row(s) of data, or null if no result found. beforeDelete Varies by delete* method. See the following: - delete() **id** = primary key of row being deleted. **purge** = boolean whether soft-delete rows should be hard deleted. From 6822a7e1ddc59cfb897f5355994497c2b862f49a Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 16 Sep 2020 15:19:07 +0000 Subject: [PATCH 131/328] Implement model find event singletons --- system/Model.php | 45 +++++++++++++++++------- tests/_support/Models/EventModel.php | 27 +++++++++----- tests/system/Database/Live/ModelTest.php | 33 +++++++++++++++++ user_guide_src/source/models/model.rst | 2 +- 4 files changed, 85 insertions(+), 22 deletions(-) diff --git a/system/Model.php b/system/Model.php index 008a57e2b2ca..76d103347f47 100644 --- a/system/Model.php +++ b/system/Model.php @@ -393,10 +393,17 @@ public function __construct(ConnectionInterface &$db = null, ValidationInterface */ public function find($id = null) { + $singleton = is_numeric($id) || is_string($id); + if ($this->tempAllowCallbacks) { // Call the before event and check for a return - $eventData = $this->trigger('beforeFind', ['id' => $id, 'method' => 'find']); + $eventData = $this->trigger('beforeFind', [ + 'id' => $id, + 'method' => 'find', + 'singleton' => $singleton, + ]); + if (! empty($eventData['returnData'])) { return $eventData['data']; @@ -415,7 +422,7 @@ public function find($id = null) ->get(); $row = $row->getResult($this->tempReturnType); } - elseif (is_numeric($id) || is_string($id)) + elseif ($singleton) { $row = $builder->where($this->table . '.' . $this->primaryKey, $id) ->get(); @@ -430,9 +437,10 @@ public function find($id = null) } $eventData = [ - 'id' => $id, - 'data' => $row, - 'method' => 'find', + 'id' => $id, + 'data' => $row, + 'method' => 'find', + 'singleton' => $singleton, ]; if ($this->tempAllowCallbacks) { @@ -486,7 +494,12 @@ public function findAll(int $limit = 0, int $offset = 0) if ($this->tempAllowCallbacks) { // Call the before event and check for a return - $eventData = $this->trigger('beforeFind', ['method' => 'findAll', 'limit' => $limit, 'offset' => $offset]); + $eventData = $this->trigger('beforeFind', [ + 'method' => 'findAll', + 'limit' => $limit, + 'offset' => $offset, + 'singleton' => false, + ]); if (! empty($eventData['returnData'])) { return $eventData['data']; @@ -506,10 +519,11 @@ public function findAll(int $limit = 0, int $offset = 0) $row = $row->getResult($this->tempReturnType); $eventData = [ - 'data' => $row, - 'limit' => $limit, - 'offset' => $offset, - 'method' => 'findAll', + 'data' => $row, + 'limit' => $limit, + 'offset' => $offset, + 'method' => 'findAll', + 'singleton' => false, ]; if ($this->tempAllowCallbacks) { @@ -536,7 +550,11 @@ public function first() if ($this->tempAllowCallbacks) { // Call the before event and check for a return - $eventData = $this->trigger('beforeFind', ['method' => 'first']); + $eventData = $this->trigger('beforeFind', [ + 'method' => 'first', + 'singleton' => true, + ]); + if (! empty($eventData['returnData'])) { return $eventData['data']; @@ -570,8 +588,9 @@ public function first() $row = $row->getFirstRow($this->tempReturnType); $eventData = [ - 'data' => $row, - 'method' => 'first', + 'data' => $row, + 'method' => 'first', + 'singleton' => true, ]; if ($this->tempAllowCallbacks) { diff --git a/tests/_support/Models/EventModel.php b/tests/_support/Models/EventModel.php index bd8c94482a79..c1ef3b84c70f 100644 --- a/tests/_support/Models/EventModel.php +++ b/tests/_support/Models/EventModel.php @@ -28,6 +28,9 @@ class EventModel extends Model protected $beforeFind = ['beforeFindMethod']; protected $afterFind = ['afterFindMethod']; + // Cache of the most recent eventData from a trigger + public $eventData; + // Testing directive to set $returnData on beforeFind event public $beforeFindReturnData = false; @@ -36,49 +39,56 @@ class EventModel extends Model protected function beforeInsertMethod(array $data) { - $this->tokens[] = 'beforeInsert'; + $this->tokens[] = 'beforeInsert'; + $this->eventData = $data; return $data; } protected function afterInsertMethod(array $data) { - $this->tokens[] = 'afterInsert'; + $this->tokens[] = 'afterInsert'; + $this->eventData = $data; return $data; } protected function beforeUpdateMethod(array $data) { - $this->tokens[] = 'beforeUpdate'; + $this->tokens[] = 'beforeUpdate'; + $this->eventData = $data; return $data; } protected function afterUpdateMethod(array $data) { - $this->tokens[] = 'afterUpdate'; + $this->tokens[] = 'afterUpdate'; + $this->eventData = $data; return $data; } protected function beforeDeleteMethod(array $data) { - $this->tokens[] = 'beforeDelete'; + $this->tokens[] = 'beforeDelete'; + $this->eventData = $data; return $data; } protected function afterDeleteMethod(array $data) { - $this->tokens[] = 'afterDelete'; + $this->tokens[] = 'afterDelete'; + $this->eventData = $data; return $data; } protected function beforeFindMethod(array $data) { - $this->tokens[] = 'beforeFind'; + $this->tokens[] = 'beforeFind'; + $this->eventData = $data; if ($this->beforeFindReturnData) { @@ -91,7 +101,8 @@ protected function beforeFindMethod(array $data) protected function afterFindMethod(array $data) { - $this->tokens[] = 'afterFind'; + $this->tokens[] = 'afterFind'; + $this->eventData = $data; return $data; } diff --git a/tests/system/Database/Live/ModelTest.php b/tests/system/Database/Live/ModelTest.php index 748d6b264a9f..da9c72a14dd9 100644 --- a/tests/system/Database/Live/ModelTest.php +++ b/tests/system/Database/Live/ModelTest.php @@ -1130,6 +1130,39 @@ public function testBeforeFindReturnDataPreventsAfterFind() $this->assertFalse($model->hasToken('afterFind')); } + public function testFindEventSingletons() + { + $model = new EventModel(); + + // afterFind + $model->first(); + $this->assertEquals(true, $model->eventData['singleton']); + + $model->find(1); + $this->assertEquals(true, $model->eventData['singleton']); + + $model->find(); + $this->assertEquals(false, $model->eventData['singleton']); + + $model->findAll(); + $this->assertEquals(false, $model->eventData['singleton']); + + // beforeFind + $model->beforeFindReturnData = true; + + $model->first(); + $this->assertEquals(true, $model->eventData['singleton']); + + $model->find(1); + $this->assertEquals(true, $model->eventData['singleton']); + + $model->find(); + $this->assertEquals(false, $model->eventData['singleton']); + + $model->findAll(); + $this->assertEquals(false, $model->eventData['singleton']); + } + //-------------------------------------------------------------------- public function testAllowCallbacksFalsePreventsTriggers() diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index b326ddc89e6f..155436a1120c 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -795,7 +795,7 @@ beforeUpdate **id** = the array of primary keys of the rows being updated. afterUpdate **id** = the array of primary keys of the rows being updated. **data** = the key/value pairs being updated. **result** = the results of the update() method used through the Query Builder. -beforeFind The name of the calling **method**, with these additional fields: +beforeFind The name of the calling **method**, whether a **singleton** was requested, and these additional fields: - first() No additional fields - find() **id** = the primary key of the row being searched for. - findAll() **limit** = the number of rows to find. From 2634a81d514c1cd2212a3a0c0c8a6b42bf5096d2 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 16 Sep 2020 20:05:17 +0200 Subject: [PATCH 132/328] Fix countAllResults() when DBPrefix is defined --- system/Database/BaseBuilder.php | 18 ++++++++++++----- tests/system/Database/Builder/CountTest.php | 22 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 2b851992be1e..ab3c8205ef1f 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -1938,11 +1938,19 @@ public function countAllResults(bool $reset = true) $limit = $this->QBLimit; $this->QBLimit = false; - $sql = ($this->QBDistinct === true || ! empty($this->QBGroupBy)) - ? - $this->countString . $this->db->protectIdentifiers('numrows') . "\nFROM (\n" . $this->compileSelect() . "\n) CI_count_all_results" - : - $this->compileSelect($this->countString . $this->db->protectIdentifiers('numrows')); + if ($this->QBDistinct === true || ! empty($this->QBGroupBy)) + { + // We need to backup the original SELECT in case DBPrefix is used + $select = $this->QBSelect; + $sql = $this->countString . $this->db->protectIdentifiers('numrows') . "\nFROM (\n" . $this->compileSelect() . "\n) CI_count_all_results"; + // Restore SELECT part + $this->QBSelect = $select; + unset($select); + } + else + { + $sql = $this->compileSelect($this->countString . $this->db->protectIdentifiers('numrows')); + } if ($this->testMode) { diff --git a/tests/system/Database/Builder/CountTest.php b/tests/system/Database/Builder/CountTest.php index b5ef4dbac7e0..e68eb0563d28 100644 --- a/tests/system/Database/Builder/CountTest.php +++ b/tests/system/Database/Builder/CountTest.php @@ -59,6 +59,28 @@ public function testCountAllResultsWithGroupBy() //-------------------------------------------------------------------- + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/3651 + */ + public function testCountAllResultsWithGroupByAndPrefix() + { + $this->db = new MockConnection(['DBPrefix' => 'ci_']); + + $builder = new BaseBuilder('jobs', $this->db); + $builder->select('jobs.*')->where('id >', 3)->groupBy('id')->testMode(); + + $expectedSQL = 'SELECT COUNT(*) AS "numrows" FROM ( SELECT "ci_jobs".* FROM "ci_jobs" WHERE "id" > :id: GROUP BY "id" ) CI_count_all_results'; + + $answer1 = $builder->countAllResults(false); + $this->assertEquals($expectedSQL, str_replace("\n", ' ', $answer1)); + + // We run the query one more time to make sure the DBPrefix is added only once + $answer2 = $builder->countAllResults(false); + $this->assertEquals($expectedSQL, str_replace("\n", ' ', $answer2)); + } + + //-------------------------------------------------------------------- + public function testCountAllResultsWithGroupByAndHaving() { $builder = new BaseBuilder('jobs', $this->db); From 612546e7e99f59850ff3d12a5b559f9e55f65b43 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Thu, 10 Sep 2020 20:51:24 +0800 Subject: [PATCH 133/328] Fix Filters' discoverFilters on class redeclaration --- system/Filters/Filters.php | 50 ++++++++++---------------- tests/system/Filters/FiltersTest.php | 53 +++++----------------------- 2 files changed, 28 insertions(+), 75 deletions(-) diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 5cef2f08ad1a..9efbb27efd8f 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -121,9 +121,7 @@ class Filters * * @var \Config\Modules */ - protected $moduleConfig; - - //-------------------------------------------------------------------- + protected $modules; /** * Constructor. @@ -131,38 +129,40 @@ class Filters * @param \Config\Filters $config * @param RequestInterface $request * @param ResponseInterface $response - * @param \Config\Modules|null $moduleConfig + * @param \Config\Modules|null $modules */ - public function __construct($config, RequestInterface $request, ResponseInterface $response, Modules $moduleConfig = null) + public function __construct($config, RequestInterface $request, ResponseInterface $response, Modules $modules = null) { $this->config = $config; $this->request = &$request; $this->setResponse($response); - $this->moduleConfig = $moduleConfig ?? config('Modules'); + $this->modules = $modules ?? config('Modules'); } - //-------------------------------------------------------------------- - /** - * If discoverFilters is enabled in Config then system will try to auto - * Discovery custom filters files in Namespaces and allow access to - * The config object via the variable $customfilters as with the routes file + * If discoverFilters is enabled in Config then system will try to + * auto-discover custom filters files in Namespaces and allow access to + * the config object via the variable $customfilters as with the routes file + * * Sample : * $filters->aliases['custom-auth'] = \Acme\Blob\Filters\BlobAuth::class; */ private function discoverFilters() { - $locater = Services::locator(); + $locator = Services::locator(); + // for access by custom filters $filters = $this->config; - $files = $locater->search('Config/Filters.php'); + $files = $locator->search('Config/Filters.php'); foreach ($files as $file) { - // Don't include our main file again... - if ($file === APPPATH . 'Config/Filters.php') + $className = $locator->getClassname($file); + + // Don't include our main Filter config again... + if ($className === 'Config\\Filters') { continue; } @@ -181,8 +181,6 @@ public function setResponse(ResponseInterface $response) $this->response = &$response; } - //-------------------------------------------------------------------- - /** * Runs through all of the filters for the specified * uri and position. @@ -231,7 +229,7 @@ public function run(string $uri, string $position = 'before') return $result; } - + if ($position === 'after') { $result = $class->after($this->request, $this->response, $this->argumentsClass[$className] ?? null); @@ -239,6 +237,7 @@ public function run(string $uri, string $position = 'before') if ($result instanceof ResponseInterface) { $this->response = $result; + continue; } } @@ -247,8 +246,6 @@ public function run(string $uri, string $position = 'before') return $position === 'before' ? $this->request : $this->response; } - //-------------------------------------------------------------------- - /** * Runs through our list of filters provided by the configuration * object to get them ready for use, including getting uri masks @@ -272,7 +269,8 @@ public function initialize(string $uri = null) { return $this; } - if ($this->moduleConfig->shouldDiscover('filters')) + + if ($this->modules->shouldDiscover('filters')) { $this->discoverFilters(); } @@ -299,8 +297,6 @@ public function initialize(string $uri = null) return $this; } - //-------------------------------------------------------------------- - /** * Returns the processed filters array. * @@ -354,8 +350,6 @@ public function addFilter(string $class, string $alias = null, string $when = 'b return $this; } - //-------------------------------------------------------------------- - /** * Ensures that a specific filter is on and enabled for the current request. * @@ -416,7 +410,6 @@ public function getArguments(string $key = null) return is_null($key) ? $this->arguments : $this->arguments[$key]; } - //-------------------------------------------------------------------- //-------------------------------------------------------------------- // Processors //-------------------------------------------------------------------- @@ -478,8 +471,6 @@ protected function processGlobals(string $uri = null) } } - //-------------------------------------------------------------------- - /** * Add any method-specific flters to the mix. * @@ -502,8 +493,6 @@ protected function processMethods() } } - //-------------------------------------------------------------------- - /** * Add any applicable configured filters to the mix. * @@ -614,5 +603,4 @@ private function pathApplies(string $uri, $paths) return false; } - } diff --git a/tests/system/Filters/FiltersTest.php b/tests/system/Filters/FiltersTest.php index 186beb87c4b7..6441fc184f83 100644 --- a/tests/system/Filters/FiltersTest.php +++ b/tests/system/Filters/FiltersTest.php @@ -1,4 +1,5 @@ APPPATH . 'Config', - 'App' => APPPATH, + 'Config' => APPPATH . 'Config', + 'App' => APPPATH, 'Tests\Support' => TESTPATH . '_support', ]; Services::autoloader()->addNamespace($defaults); - + $this->request = Services::request(); $this->response = Services::response(); - } - //-------------------------------------------------------------------- public function testProcessMethodDetectsCLI() { $config = [ @@ -60,8 +58,6 @@ public function testProcessMethodDetectsCLI() $this->assertEquals($expected, $filters->initialize()->getFilters()); } - //-------------------------------------------------------------------- - public function testProcessMethodDetectsGetRequests() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -82,9 +78,6 @@ public function testProcessMethodDetectsGetRequests() $this->assertEquals($expected, $filters->initialize()->getFilters()); } - - //-------------------------------------------------------------------- - public function testProcessMethodRespectsMethod() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -133,13 +126,11 @@ public function testProcessMethodIgnoresMethod() $this->assertEquals($expected, $filters->initialize()->getFilters()); } - //-------------------------------------------------------------------- - public function testProcessMethodProcessGlobals() { $_SERVER['REQUEST_METHOD'] = 'GET'; - $config = [ + $config = [ 'aliases' => [ 'foo' => '', 'bar' => '', @@ -155,6 +146,7 @@ public function testProcessMethodProcessGlobals() ], ], ]; + $filters = new Filters((object) $config, $this->request, $this->response); $expected = [ @@ -168,8 +160,6 @@ public function testProcessMethodProcessGlobals() $this->assertEquals($expected, $filters->initialize()->getFilters()); } - //-------------------------------------------------------------------- - public function provideExcept() { return [ @@ -218,8 +208,6 @@ public function testProcessMethodProcessGlobalsWithExcept(array $except) $this->assertEquals($expected, $filters->initialize($uri)->getFilters()); } - //-------------------------------------------------------------------- - public function testProcessMethodProcessesFiltersBefore() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -248,8 +236,6 @@ public function testProcessMethodProcessesFiltersBefore() $this->assertEquals($expected, $filters->initialize($uri)->getFilters()); } - //-------------------------------------------------------------------- - public function testProcessMethodProcessesFiltersAfter() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -280,8 +266,6 @@ public function testProcessMethodProcessesFiltersAfter() $this->assertEquals($expected, $filters->initialize($uri)->getFilters()); } - //-------------------------------------------------------------------- - public function testProcessMethodProcessesCombined() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -330,8 +314,6 @@ public function testProcessMethodProcessesCombined() $this->assertEquals($expected, $filters->initialize($uri)->getFilters()); } - //-------------------------------------------------------------------- - public function testProcessMethodProcessesCombinedAfterForToolbar() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -373,8 +355,6 @@ public function testProcessMethodProcessesCombinedAfterForToolbar() $this->assertEquals($expected, $filters->initialize($uri)->getFilters()); } - //-------------------------------------------------------------------- - public function testRunThrowsWithInvalidAlias() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -394,8 +374,6 @@ public function testRunThrowsWithInvalidAlias() $filters->run($uri); } - - //-------------------------------------------------------------------- public function testCustomFiltersLoad() { @@ -415,10 +393,7 @@ public function testCustomFiltersLoad() $request = $filters->run($uri, 'before'); $this->assertEquals('http://hellowworld.com', $request->url); - } - - - //-------------------------------------------------------------------- + } public function testRunThrowsWithInvalidClassType() { @@ -440,8 +415,6 @@ public function testRunThrowsWithInvalidClassType() $filters->run($uri); } - //-------------------------------------------------------------------- - public function testRunDoesBefore() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -462,8 +435,6 @@ public function testRunDoesBefore() $this->assertEquals('http://google.com', $request->url); } - //-------------------------------------------------------------------- - public function testRunDoesAfter() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -484,8 +455,6 @@ public function testRunDoesAfter() $this->assertEquals('http://google.com', $response->csp); } - //-------------------------------------------------------------------- - public function testShortCircuit() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -532,8 +501,6 @@ public function testOtherResult() $this->assertEquals('This is curious', $response); } - //-------------------------------------------------------------------- - public function testBeforeExceptString() { $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -1090,9 +1057,8 @@ public function testFilterClass() ], ]; $filters = new Filters((object) $config, $this->request, $this->response); - $uri = 'admin/foo/bar'; - $filters->run($uri, 'before'); + $filters->run('admin/foo/bar', 'before'); $expected = [ 'after' => [ 'CodeIgniter\Filters\fixtures\Multiple1', @@ -1102,5 +1068,4 @@ public function testFilterClass() ]; $this->assertEquals($expected, $filters->getFiltersClass()); } - } From 8a1b36c89978e507d1a8d22609b8a8b4653d8fc0 Mon Sep 17 00:00:00 2001 From: Agung Sugiarto Date: Tue, 15 Sep 2020 11:12:46 +0700 Subject: [PATCH 134/328] Implement getTotal item on pager --- system/Pager/Pager.php | 16 ++++++++++++++++ system/Pager/PagerInterface.php | 11 +++++++++++ tests/system/Pager/PagerTest.php | 7 +++++++ 3 files changed, 34 insertions(+) diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index 6d7aa2ec7254..b9053425633c 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -262,6 +262,22 @@ public function setPath(string $path, string $group = 'default') //-------------------------------------------------------------------- + /** + * Returns the total number of items in data store. + * + * @param string $group + * + * @return integer + */ + public function getTotal(string $group = 'default'): int + { + $this->ensureGroup($group); + + return $this->groups[$group]['total']; + } + + //-------------------------------------------------------------------- + /** * Returns the total number of pages. * diff --git a/system/Pager/PagerInterface.php b/system/Pager/PagerInterface.php index 6552804db738..ea6eeed16156 100644 --- a/system/Pager/PagerInterface.php +++ b/system/Pager/PagerInterface.php @@ -111,6 +111,17 @@ public function setPath(string $path, string $group = 'default'); //-------------------------------------------------------------------- + /** + * Returns the total number of items in data store. + * + * @param string $group + * + * @return integer + */ + public function getTotal(string $group = 'default'): int; + + //-------------------------------------------------------------------- + /** * Returns the total number of pages. * diff --git a/tests/system/Pager/PagerTest.php b/tests/system/Pager/PagerTest.php index 6a79395d3ae8..1aab45fa659d 100644 --- a/tests/system/Pager/PagerTest.php +++ b/tests/system/Pager/PagerTest.php @@ -205,6 +205,13 @@ public function testGetTotalPagesDefaultsToOne() $this->assertEquals(1, $this->pager->getPageCount()); } + public function testGetTotalCorrectValue() + { + $this->pager->store('foo', 3, 12, 70); + + $this->assertEquals(70, $this->pager->getTotal('foo')); + } + public function testGetTotalPagesCalcsCorrectValue() { $this->pager->store('foo', 3, 12, 70); From 155b8ba0133d4579127e11061b781e4f80d09a1f Mon Sep 17 00:00:00 2001 From: Agung Sugiarto Date: Thu, 17 Sep 2020 14:28:22 +0700 Subject: [PATCH 135/328] Remove getTotal() on PagerInterface --- system/Pager/PagerInterface.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/system/Pager/PagerInterface.php b/system/Pager/PagerInterface.php index ea6eeed16156..6552804db738 100644 --- a/system/Pager/PagerInterface.php +++ b/system/Pager/PagerInterface.php @@ -111,17 +111,6 @@ public function setPath(string $path, string $group = 'default'); //-------------------------------------------------------------------- - /** - * Returns the total number of items in data store. - * - * @param string $group - * - * @return integer - */ - public function getTotal(string $group = 'default'): int; - - //-------------------------------------------------------------------- - /** * Returns the total number of pages. * From 9031afb78b7964c5943a1853463e2104281d2001 Mon Sep 17 00:00:00 2001 From: uraku Date: Fri, 18 Sep 2020 15:33:56 +0900 Subject: [PATCH 136/328] If CSRFExpire specifies 0, set 0 as it is If CSRFExpire specifies 0, set 0 as it is. fix: #3655 --- system/Security/Security.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/system/Security/Security.php b/system/Security/Security.php index 44af8ed1ea3f..b2acc76c5877 100644 --- a/system/Security/Security.php +++ b/system/Security/Security.php @@ -288,7 +288,11 @@ public function CSRFVerify(RequestInterface $request) */ public function CSRFSetCookie(RequestInterface $request) { - $expire = time() + $this->CSRFExpire; + if ($this->CSRFExpire !== 0) { + $expire = time() + $this->CSRFExpire; + } else { + $expire = $this->CSRFExpire; + } $secure_cookie = (bool) $this->cookieSecure; if ($secure_cookie && ! $request->isSecure()) From 76fde580ca5c49385ac304537d0ebad93a91a110 Mon Sep 17 00:00:00 2001 From: uraku Date: Sat, 19 Sep 2020 09:21:35 +0900 Subject: [PATCH 137/328] Change code format to be simpler --- system/Security/Security.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/system/Security/Security.php b/system/Security/Security.php index b2acc76c5877..3bc3a261e3d1 100644 --- a/system/Security/Security.php +++ b/system/Security/Security.php @@ -288,11 +288,7 @@ public function CSRFVerify(RequestInterface $request) */ public function CSRFSetCookie(RequestInterface $request) { - if ($this->CSRFExpire !== 0) { - $expire = time() + $this->CSRFExpire; - } else { - $expire = $this->CSRFExpire; - } + $expire = $this->CSRFExpire === 0 ? $this->CSRFExpire : time() + $this->CSRFExpire; $secure_cookie = (bool) $this->cookieSecure; if ($secure_cookie && ! $request->isSecure()) From 50d559bdcf5e8e395998fb17d13f5de207bd6716 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 20 Sep 2020 13:50:06 +0700 Subject: [PATCH 138/328] phpstan update: no longer need ignore error on isset($_SESSION) --- system/CodeIgniter.php | 2 +- system/Log/Logger.php | 2 +- system/Validation/Validation.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index bd4972c0a11c..6ee2900300e6 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -1121,7 +1121,7 @@ public function storePreviousURL($uri) $uri = new URI($uri); } - if (isset($_SESSION)) // @phpstan-ignore-line + if (isset($_SESSION)) { $_SESSION['_ci_previous_url'] = (string) $uri; } diff --git a/system/Log/Logger.php b/system/Log/Logger.php index e62952a3c593..5f3847183dce 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -458,7 +458,7 @@ protected function interpolate($message, array $context = []) } } - if (isset($_SESSION)) // @phpstan-ignore-line + if (isset($_SESSION)) { $replace['{session_vars}'] = '$_SESSION: ' . print_r($_SESSION, true); } diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index ae1664992731..835128d42b52 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -758,7 +758,6 @@ public function getErrors(): array // passed along from a redirect_with_input request. if (empty($this->errors) && ! is_cli()) { - // @phpstan-ignore-next-line if (isset($_SESSION, $_SESSION['_ci_validation_errors'])) { $this->errors = unserialize($_SESSION['_ci_validation_errors']); From 99b7d2bc62990696958fe5bb1a102c9ca614e22b Mon Sep 17 00:00:00 2001 From: Agung Sugiarto Date: Sun, 20 Sep 2020 10:55:57 +0700 Subject: [PATCH 139/328] Command cache info to show cache in current system --- system/Commands/Cache/InfoCache.php | 90 +++++++++++++++++++++++++ tests/system/Commands/InfoCacheTest.php | 76 +++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 system/Commands/Cache/InfoCache.php create mode 100644 tests/system/Commands/InfoCacheTest.php diff --git a/system/Commands/Cache/InfoCache.php b/system/Commands/Cache/InfoCache.php new file mode 100644 index 000000000000..a6b359783183 --- /dev/null +++ b/system/Commands/Cache/InfoCache.php @@ -0,0 +1,90 @@ + 'The cache driver to use', + ]; + + /** + * Clears the cache + * + * @param array $params + */ + public function run(array $params) + { + $config = config('Cache'); + helper('number'); + + $handler = $params[0] ?? $config->handler; + if (! array_key_exists($handler, $config->validHandlers)) + { + CLI::error($handler . ' is not a valid cache handler.'); + return; + } + + $config->handler = $handler; + $cache = CacheFactory::getHandler($config); + + $caches = $cache->getCacheInfo(); + + $tbody = []; + + foreach ($caches as $key => $field) + { + $tbody[] = [ + $key, + $field['server_path'], + number_to_size($field['size']), + Time::createFromTimestamp($field['date']), + ]; + } + + $thead = [ + CLI::color('Name', 'green'), + CLI::color('Server Path', 'green'), + CLI::color('Size', 'green'), + CLI::color('Date', 'green'), + ]; + + CLI::table($tbody, $thead); + } +} diff --git a/tests/system/Commands/InfoCacheTest.php b/tests/system/Commands/InfoCacheTest.php new file mode 100644 index 000000000000..783a40123af5 --- /dev/null +++ b/tests/system/Commands/InfoCacheTest.php @@ -0,0 +1,76 @@ +streamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $this->streamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Make sure we are testing with the correct handler (override injections) + Services::injectMock('cache', CacheFactory::getHandler(config('Cache'))); + } + + public function tearDown(): void + { + if (! $this->result) + { + return; + } + + stream_filter_remove($this->streamFilter); + } + + protected function getBuffer() + { + return CITestStreamFilter::$buffer; + } + + public function testInfoCacheInvalidHandler() + { + command('cache:info notfound'); + + $result = CITestStreamFilter::$buffer; + + $this->assertStringContainsString('notfound is not a valid cache handler.', $result); + } + + public function testInfoCacheCanSeeFoo() + { + cache()->save('foo', 'bar'); + + command('cache:info'); + + $this->assertStringContainsString('foo', $this->getBuffer()); + } + + public function testInfoCacheCanSeeTable() + { + command('cache:info'); + + $this->assertStringContainsString('Name', $this->getBuffer()); + $this->assertStringContainsString('Server Path', $this->getBuffer()); + $this->assertStringContainsString('Size', $this->getBuffer()); + $this->assertStringContainsString('Date', $this->getBuffer()); + } + + public function testInfoCacheCannotSeeFoo() + { + cache()->delete('foo'); + + command('cache:info'); + + $this->assertStringNotContainsString ('foo', $this->getBuffer()); + } +} From c2ab38fb37e15e0568032503293a1ed1098ef254 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sat, 19 Sep 2020 09:21:05 +0200 Subject: [PATCH 140/328] Fix checking for redirect in FeatureResponse --- system/Test/FeatureResponse.php | 8 +++++--- tests/system/Test/FeatureResponseTest.php | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/system/Test/FeatureResponse.php b/system/Test/FeatureResponse.php index 9e51339ca117..4684fb42dc8a 100644 --- a/system/Test/FeatureResponse.php +++ b/system/Test/FeatureResponse.php @@ -110,13 +110,15 @@ public function isOK(): bool } /** - * Returns whether or not the Response was a redirect response + * Returns whether or not the Response was a redirect or RedirectResponse * * @return boolean */ public function isRedirect(): bool { - return $this->response instanceof RedirectResponse; + return $this->response instanceof RedirectResponse + || $this->response->hasHeader('Location') + || $this->response->hasHeader('Refresh'); } /** @@ -126,7 +128,7 @@ public function isRedirect(): bool */ public function assertRedirect() { - $this->assertTrue($this->isRedirect(), 'Response is not a RedirectResponse.'); + $this->assertTrue($this->isRedirect(), 'Response is not a redirect or RedirectResponse.'); } /** diff --git a/tests/system/Test/FeatureResponseTest.php b/tests/system/Test/FeatureResponseTest.php index 7da891656879..fb4095856a7b 100644 --- a/tests/system/Test/FeatureResponseTest.php +++ b/tests/system/Test/FeatureResponseTest.php @@ -124,6 +124,17 @@ public function testAssertRedirectSuccess() $this->feature->assertRedirect(); } + public function testAssertRedirectSuccessWithoutRedirectResponse() + { + $this->getFeatureResponse('

Hello World

'); + $this->response->redirect('foo/bar'); + + $this->assertFalse($this->feature->response instanceof RedirectResponse); + $this->assertTrue($this->feature->isRedirect()); + $this->feature->assertRedirect(); + $this->assertEquals('foo/bar', $this->feature->getRedirectUrl()); + } + public function testGetRedirectUrlReturnsUrl() { $this->getFeatureResponse('

Hello World

'); From 43d2ae714b365dd90eaa89fdcf250d54aae1a349 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sat, 19 Sep 2020 19:43:29 +0200 Subject: [PATCH 141/328] Fix ControllerResponse::isRedirect() --- system/Test/ControllerResponse.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/system/Test/ControllerResponse.php b/system/Test/ControllerResponse.php index 6500cb120076..65949a1c5fad 100644 --- a/system/Test/ControllerResponse.php +++ b/system/Test/ControllerResponse.php @@ -196,13 +196,15 @@ public function isOK(): bool } /** - * Returns whether or not the Response was a redirect response + * Returns whether or not the Response was a redirect or RedirectResponse * * @return boolean */ public function isRedirect(): bool { - return $this->response instanceof RedirectResponse; + return $this->response instanceof RedirectResponse + || $this->response->hasHeader('Location') + || $this->response->hasHeader('Refresh'); } //-------------------------------------------------------------------- From 6466d0a4e41c6dd25b10471aab1cb32e04516e61 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 21 Sep 2020 00:27:22 +0700 Subject: [PATCH 142/328] Remove "awkward" note from readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a4319ced0345..1a4575702398 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ not to the project root. A better practice would be to configure a virtual host framework are exposed. **Please** read the user guide for a better explanation of how CI4 works! -The user guide updating and deployment is a bit awkward at the moment, but we are working on it! ## Repository Management From fe756de37886e209b34f3f9b090a557394995c55 Mon Sep 17 00:00:00 2001 From: MGatner Date: Sun, 20 Sep 2020 09:59:31 -0400 Subject: [PATCH 143/328] Fix self return types --- system/HTTP/Message.php | 91 ++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 60 deletions(-) diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 84168e168653..7743ae8d1c41 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -46,7 +46,6 @@ */ class Message { - /** * List of all HTTP request headers. * @@ -88,7 +87,6 @@ class Message */ protected $body; - //-------------------------------------------------------------------- //-------------------------------------------------------------------- // Body //-------------------------------------------------------------------- @@ -103,39 +101,34 @@ public function getBody() return $this->body; } - //-------------------------------------------------------------------- - /** * Sets the body of the current message. * * @param mixed $data * - * @return Message|Response + * @return $this */ - public function setBody($data) + public function setBody($data): self { $this->body = $data; return $this; } - //-------------------------------------------------------------------- - /** * Appends data to the body of the current message. * * @param mixed $data * - * @return Message|Response + * @return $this */ - public function appendBody($data) + public function appendBody($data): self { $this->body .= (string) $data; return $this; } - //-------------------------------------------------------------------- //-------------------------------------------------------------------- // Headers //-------------------------------------------------------------------- @@ -143,7 +136,7 @@ public function appendBody($data) /** * Populates the $headers array with any headers the getServer knows about. */ - public function populateHeaders() + public function populateHeaders(): void { $contentType = $_SERVER['CONTENT_TYPE'] ?? getenv('CONTENT_TYPE'); if (! empty($contentType)) @@ -168,8 +161,6 @@ public function populateHeaders() } } - //-------------------------------------------------------------------- - /** * Returns an array containing all headers. * @@ -188,15 +179,13 @@ public function getHeaders(): array return $this->headers; } - //-------------------------------------------------------------------- - /** * Returns a single header object. If multiple headers with the same * name exist, then will return an array of header objects. * * @param string $name * - * @return array|\CodeIgniter\HTTP\Header|null + * @return array|Header|null */ public function getHeader(string $name) { @@ -210,8 +199,6 @@ public function getHeader(string $name) return $this->headers[$orig_name]; } - //-------------------------------------------------------------------- - /** * Determines whether a header exists. * @@ -226,8 +213,6 @@ public function hasHeader(string $name): bool return isset($this->headers[$orig_name]); } - //-------------------------------------------------------------------- - /** * Retrieves a comma-separated string of the values for a single header. * @@ -255,17 +240,15 @@ public function getHeaderLine(string $name): string return $this->headers[$orig_name]->getValueLine(); } - //-------------------------------------------------------------------- - /** * Sets a header and it's value. * * @param string $name * @param array|null|string $value * - * @return Message|Response + * @return $this */ - public function setHeader(string $name, $value) + public function setHeader(string $name, $value): self { $origName = $this->getHeaderName($name); @@ -290,16 +273,14 @@ public function setHeader(string $name, $value) return $this; } - //-------------------------------------------------------------------- - /** * Removes a header from the list of headers we track. * * @param string $name * - * @return Message + * @return $this */ - public function removeHeader(string $name) + public function removeHeader(string $name): self { $orig_name = $this->getHeaderName($name); @@ -309,8 +290,6 @@ public function removeHeader(string $name) return $this; } - //-------------------------------------------------------------------- - /** * Adds an additional header value to any headers that accept * multiple values (i.e. are an array or implement ArrayAccess) @@ -318,9 +297,9 @@ public function removeHeader(string $name) * @param string $name * @param string|null $value * - * @return Message + * @return $this */ - public function appendHeader(string $name, ?string $value) + public function appendHeader(string $name, ?string $value): self { $orig_name = $this->getHeaderName($name); @@ -331,8 +310,6 @@ public function appendHeader(string $name, ?string $value) return $this; } - //-------------------------------------------------------------------- - /** * Adds an additional header value to any headers that accept * multiple values (i.e. are an array or implement ArrayAccess) @@ -340,9 +317,9 @@ public function appendHeader(string $name, ?string $value) * @param string $name * @param string $value * - * @return Message + * @return $this */ - public function prependHeader(string $name, string $value) + public function prependHeader(string $name, string $value): self { $orig_name = $this->getHeaderName($name); @@ -351,6 +328,21 @@ public function prependHeader(string $name, string $value) return $this; } + /** + * Takes a header name in any case, and returns the + * normal-case version of the header. + * + * @param string $name + * + * @return string + */ + protected function getHeaderName(string $name): string + { + $lower_name = strtolower($name); + + return $this->headerMap[$lower_name] ?? $name; + } + //-------------------------------------------------------------------- /** @@ -363,16 +355,14 @@ public function getProtocolVersion(): string return $this->protocolVersion ?? '1.1'; } - //-------------------------------------------------------------------- - /** * Sets the HTTP protocol version. * * @param string $version * - * @return Message + * @return $this */ - public function setProtocolVersion(string $version) + public function setProtocolVersion(string $version): self { if (! is_numeric($version)) { @@ -391,23 +381,4 @@ public function setProtocolVersion(string $version) return $this; } - - //-------------------------------------------------------------------- - - /** - * Takes a header name in any case, and returns the - * normal-case version of the header. - * - * @param string $name - * - * @return string - */ - protected function getHeaderName(string $name): string - { - $lower_name = strtolower($name); - - return $this->headerMap[$lower_name] ?? $name; - } - - //-------------------------------------------------------------------- } From ea0e796bd704a298a3280f8b089c1b91d2bfe8b3 Mon Sep 17 00:00:00 2001 From: MGatner Date: Sun, 20 Sep 2020 10:09:29 -0400 Subject: [PATCH 144/328] Stop ignoring Message error, standardize format --- phpstan.neon.dist | 97 +++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 63f070ab6ab1..aee79b697641 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,50 +1,49 @@ parameters: - tmpDir: build/phpstan - level: 5 - paths: - - app - - system - treatPhpDocTypesAsCertain: false - bootstrapFiles: - - system/Test/bootstrap.php - excludes_analyse: - - app/Views/errors/cli/* - - app/Views/errors/html/* - - system/Commands/Generators/Views/* - - system/Config/Routes.php - - system/Debug/Toolbar/Views/toolbar.tpl.php - - system/Validation/Views/single.php - - system/ThirdParty/* - ignoreErrors: - - '#Unsafe usage of new static\(\)*#' - - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::_(disable|enable)ForeignKeyChecks\(\)#' - - '#Access to an undefined property CodeIgniter\\Database\\Forge::\$dropConstraintStr#' - - '#Access to protected property CodeIgniter\\Database\\BaseConnection::(\$DBDebug|\$DBPrefix|\$swapPre|\$charset|\$DBCollat)#' - - '#Access to an undefined property CodeIgniter\\Database\\BaseConnection::\$mysqli#' - - '#Access to an undefined property CodeIgniter\\Database\\ConnectionInterface::(\$DBDriver|\$connID|\$likeEscapeStr|\$likeEscapeChar|\$escapeChar|\$protectIdentifiers)#' - - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::supportsForeignKeys\(\)#' - - '#Cannot call method [a-zA-Z_]+\(\) on ((bool\|)?object\|resource)#' - - '#Cannot access property [\$a-z_]+ on ((bool\|)?object\|resource)#' - - '#Call to an undefined method CodeIgniter\\HTTP\\Request::(getPath|getSegments|getMethod|setLocale|getPost)\(\)#' - - '#Access to an undefined property CodeIgniter\\HTTP\\Request::\$uri#' - - '#Call to an undefined method CodeIgniter\\HTTP\\Message::setStatusCode\(\)#' - - '#Call to an undefined method CodeIgniter\\Database\\ConnectionInterface::(tableExists|protectIdentifiers|setAliasedTables|escapeIdentifiers|affectedRows|addTableAlias)\(\)#' - - '#Method CodeIgniter\\Database\\ConnectionInterface::query\(\) invoked with 3 parameters, 1-2 required#' - - '#Return type \(bool\) of method CodeIgniter\\Test\\TestLogger::log\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::log\(\)#' - - '#Call to an undefined method CodeIgniter\\Router\\RouteCollectionInterface::(getDefaultNamespace|isFiltered|getFilterForRoute|getRoutesOptions)\(\)#' - - '#Method CodeIgniter\\Router\\RouteCollectionInterface::getRoutes\(\) invoked with 1 parameter, 0 required#' - - '#Call to an undefined method CodeIgniter\\Validation\\ValidationInterface::loadRuleGroup\(\)#' - - '#Method CodeIgniter\\Validation\\ValidationInterface::run\(\) invoked with 3 parameters, 0-2 required#' - - '#Return type \(bool\) of method CodeIgniter\\Log\\Logger::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\)#' - - '#Return type \(bool\) of method CodeIgniter\\HTTP\\Files\\UploadedFile::move\(\) should be compatible with return type \(CodeIgniter\\Files\\File\) of method CodeIgniter\\Files\\File::move\(\)#' - - '#Call to function is_null\(\) with mysqli_stmt\|resource will always evaluate to false#' - - '#Negated boolean expression is always (true|false)#' - - '#Parameter \#1 \$db of class CodeIgniter\\Database\\SQLite3\\Table constructor expects CodeIgniter\\Database\\SQLite3\\Connection, CodeIgniter\\Database\\BaseConnection given#' - parallel: - processTimeout: 300.0 - scanDirectories: - - system/Helpers - dynamicConstantNames: - - ENVIRONMENT - - CI_DEBUG - - APP_NAMESPACE + tmpDir: build/phpstan + level: 5 + paths: + - app + - system + treatPhpDocTypesAsCertain: false + bootstrapFiles: + - system/Test/bootstrap.php + excludes_analyse: + - app/Views/errors/cli/* + - app/Views/errors/html/* + - system/Commands/Generators/Views/* + - system/Config/Routes.php + - system/Debug/Toolbar/Views/toolbar.tpl.php + - system/ThirdParty/* + - system/Validation/Views/single.php + ignoreErrors: + - '#Access to an undefined property CodeIgniter\\Database\\Forge::\$dropConstraintStr#' + - '#Access to an undefined property CodeIgniter\\Database\\BaseConnection::\$mysqli#' + - '#Access to an undefined property CodeIgniter\\Database\\ConnectionInterface::(\$DBDriver|\$connID|\$likeEscapeStr|\$likeEscapeChar|\$escapeChar|\$protectIdentifiers)#' + - '#Access to an undefined property CodeIgniter\\HTTP\\Request::\$uri#' + - '#Access to protected property CodeIgniter\\Database\\BaseConnection::(\$DBDebug|\$DBPrefix|\$swapPre|\$charset|\$DBCollat)#' + - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::_(disable|enable)ForeignKeyChecks\(\)#' + - '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::supportsForeignKeys\(\)#' + - '#Call to an undefined method CodeIgniter\\Database\\ConnectionInterface::(tableExists|protectIdentifiers|setAliasedTables|escapeIdentifiers|affectedRows|addTableAlias)\(\)#' + - '#Call to an undefined method CodeIgniter\\HTTP\\Request::(getPath|getSegments|getMethod|setLocale|getPost)\(\)#' + - '#Call to an undefined method CodeIgniter\\Router\\RouteCollectionInterface::(getDefaultNamespace|isFiltered|getFilterForRoute|getRoutesOptions)\(\)#' + - '#Call to an undefined method CodeIgniter\\Validation\\ValidationInterface::loadRuleGroup\(\)#' + - '#Call to function is_null\(\) with mysqli_stmt\|resource will always evaluate to false#' + - '#Cannot access property [\$a-z_]+ on ((bool\|)?object\|resource)#' + - '#Cannot call method [a-zA-Z_]+\(\) on ((bool\|)?object\|resource)#' + - '#Method CodeIgniter\\Database\\ConnectionInterface::query\(\) invoked with 3 parameters, 1-2 required#' + - '#Method CodeIgniter\\Router\\RouteCollectionInterface::getRoutes\(\) invoked with 1 parameter, 0 required#' + - '#Method CodeIgniter\\Validation\\ValidationInterface::run\(\) invoked with 3 parameters, 0-2 required#' + - '#Negated boolean expression is always (true|false)#' + - '#Parameter \#1 \$db of class CodeIgniter\\Database\\SQLite3\\Table constructor expects CodeIgniter\\Database\\SQLite3\\Connection, CodeIgniter\\Database\\BaseConnection given#' + - '#Return type \(bool\) of method CodeIgniter\\Log\\Logger::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::(emergency|alert|critical|error|warning|notice|info|debug|log)\(\)#' + - '#Return type \(bool\) of method CodeIgniter\\HTTP\\Files\\UploadedFile::move\(\) should be compatible with return type \(CodeIgniter\\Files\\File\) of method CodeIgniter\\Files\\File::move\(\)#' + - '#Return type \(bool\) of method CodeIgniter\\Test\\TestLogger::log\(\) should be compatible with return type \(null\) of method Psr\\Log\\LoggerInterface::log\(\)#' + - '#Unsafe usage of new static\(\)*#' + parallel: + processTimeout: 300.0 + scanDirectories: + - system/Helpers + dynamicConstantNames: + - APP_NAMESPACE + - CI_DEBUG + - ENVIRONMENT From 4a09d89bdca1113b49e1d33dc5869b4a504eee9c Mon Sep 17 00:00:00 2001 From: MGatner Date: Sun, 20 Sep 2020 18:34:16 +0000 Subject: [PATCH 145/328] Clean up --- system/HTTP/Message.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 7743ae8d1c41..3f58de9c15b1 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -338,13 +338,9 @@ public function prependHeader(string $name, string $value): self */ protected function getHeaderName(string $name): string { - $lower_name = strtolower($name); - - return $this->headerMap[$lower_name] ?? $name; + return $this->headerMap[strtolower($name)] ?? $name; } - //-------------------------------------------------------------------- - /** * Returns the HTTP Protocol Version. * From 66799743ad14cff179de0504571b32426cfc4fd4 Mon Sep 17 00:00:00 2001 From: Stefan Bauer Date: Mon, 21 Sep 2020 11:47:47 +0200 Subject: [PATCH 146/328] set correct PHPDoc title --- system/Language/en/Entity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Language/en/Entity.php b/system/Language/en/Entity.php index 97bc74fdf335..bb5fadaf5828 100644 --- a/system/Language/en/Entity.php +++ b/system/Language/en/Entity.php @@ -1,7 +1,7 @@ Date: Mon, 21 Sep 2020 13:46:24 +0200 Subject: [PATCH 147/328] PHPDoc blocks unified Blank lines harmonised --- system/Language/en/Cast.php | 1 + system/Language/en/Email.php | 1 + system/Language/en/Encryption.php | 1 + system/Language/en/Files.php | 1 + system/Language/en/RESTful.php | 1 + system/Language/en/View.php | 1 + 6 files changed, 6 insertions(+) diff --git a/system/Language/en/Cast.php b/system/Language/en/Cast.php index f299df85aabe..1da0bf3e2fec 100644 --- a/system/Language/en/Cast.php +++ b/system/Language/en/Cast.php @@ -1,4 +1,5 @@ Date: Wed, 23 Sep 2020 04:43:19 -0400 Subject: [PATCH 148/328] Update helpers.rst Added the countAllResults was missing from the User Guide. --- user_guide_src/source/database/helpers.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/user_guide_src/source/database/helpers.rst b/user_guide_src/source/database/helpers.rst index 297c75151fcd..47d148907083 100644 --- a/user_guide_src/source/database/helpers.rst +++ b/user_guide_src/source/database/helpers.rst @@ -40,6 +40,16 @@ Example:: // Produces an integer, like 25 +**$db->countAllResults()** + +Permits you to determine the number of rows in a particular result set. +Submit the table name in the first parameter. This is part of Query Builder. +Example:: + + echo $db->table('my_table')->countAllResults(); + + // Produces an integer, like 5 + **$db->getPlatform()** Outputs the database platform you are running (MySQL, MS SQL, Postgres, From fbc16c0a4fbbb0aa4da9ea7c4e7a22cbd0be516f Mon Sep 17 00:00:00 2001 From: InsiteFX Date: Wed, 23 Sep 2020 13:40:50 -0400 Subject: [PATCH 149/328] Updated static_pages.rst [ci skip] Corrected date to new date. --- user_guide_src/source/tutorial/static_pages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst index ed8398d24c44..6d059e1264bb 100644 --- a/user_guide_src/source/tutorial/static_pages.rst +++ b/user_guide_src/source/tutorial/static_pages.rst @@ -91,7 +91,7 @@ includes the following code: :: - © 2019 + © 2020 From 73801fd2130c99bac05bdb3f0d4ae54f7fbff7fa Mon Sep 17 00:00:00 2001 From: InsiteFX Date: Thu, 24 Sep 2020 05:56:49 -0400 Subject: [PATCH 150/328] Update view_renderer.rst [ci skip] Removed \ not nedded this is a common helper method. --- user_guide_src/source/outgoing/view_renderer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/view_renderer.rst b/user_guide_src/source/outgoing/view_renderer.rst index dba111fc8165..a55d848accae 100644 --- a/user_guide_src/source/outgoing/view_renderer.rst +++ b/user_guide_src/source/outgoing/view_renderer.rst @@ -70,7 +70,7 @@ If you choose not to escape data, or you are passing in an object instance, you the view with the ``esc()`` function. The first parameter is the string to escape. The second parameter is the context to escape the data for (see below):: - getStat()) ?> + getStat()) ?> Escaping Contexts ----------------- From 9c1f54fa3ad45d79aa75b45f6e5bc34f484144df Mon Sep 17 00:00:00 2001 From: InsiteFX Date: Thu, 24 Sep 2020 06:43:16 -0400 Subject: [PATCH 151/328] Update view_paser.rst [ci skip] The end of these tables are being cut off so that you cannot read them. --- .../source/outgoing/view_parser.rst | 124 +++++++++--------- 1 file changed, 64 insertions(+), 60 deletions(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index fd46e85832d6..1790a4723385 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -401,66 +401,70 @@ Provided Filters The following filters are available when using the parser: -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ **Filter** + **Arguments** + **Description** + **Example** + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ abs + + Displays the absolute value of a number. + { v|abs } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ capitalize + + Displays the string in sentence case: all lowercase + { v|capitalize} + -+ + + with firstletter capitalized. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ date + format (Y-m-d) + A PHP **date**-compatible formatting string. + { v|date(Y-m-d) } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ date_modify + value to add + A **strtotime** compatible string to modify the date, + { v|date_modify(+1 day) } + -+ + / subtract + like ``+5 day`` or ``-1 week``. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ default + default value + Displays the default value if the variable is empty or + { v|default(just in case) } + -+ + + undefined. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ esc + html, attr, css, js + Specifies the context to escape the data. + { v|esc(attr) } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ excerpt + phrase, radius + Returns the text within a radius of words from a given + { v|excerpt(green giant, 20) } + -+ + + phrase. Same as **excerpt** helper function. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ highlight + phrase + Highlights a given phrase within the text using + { v|highlight(view parser) } + -+ + + '' tags. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ highlight_code+ + Highlights code samples with HTML/CSS. + { v|highlight_code } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ limit_chars + limit + Limits the number of characters to $limit. + { v|limit_chars(100) } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ limit_words + limit + Limits the number of words to $limit. + { v|limit_words(20) } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ local_currency+ currency, locale + Displays a localized version of a currency. "currency" + { v|local_currency(EUR,en_US) } + -+ + + valueis any 3-letter ISO 4217 currency code. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ local_number + type, precision, + Displays a localized version of a number. "type" can be + { v|local_number(decimal,2,en_US) } + -+ + locale + one of: decimal, currency, percent, scientific, spellout, + + -+ + + ordinal, duration. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ lower + + Converts a string to lowercase. + { v|lower } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ nl2br + + Replaces all newline characters (\n) to an HTML
tag. + { v|nl2br } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ number_format + places + Wraps PHP **number_format** function for use within the + { v|number_format(3) } + -+ + + parser. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ prose + + Takes a body of text and uses the **auto_typography()** + { v|prose } + -+ + + method to turn it into prettier, easier-to-read, prose. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ round + places, type + Rounds a number to the specified places. Types of **ceil** + { v|round(3) } { v|round(ceil) } + -+ + + and **floor** can be passed to use those functions instead. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ strip_tags + allowed chars + Wraps PHP **strip_tags**. Can accept a string of allowed + { v|strip_tags(
) } + -+ + + tags. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ title + + Displays a "title case" version of the string, with all + { v|title } + -+ + + lowercase, and each word capitalized. + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ upper + + Displays the string in all uppercase. + { v|upper } + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ -+ + + + + -+---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ **Filter** + **Arguments** + **Description** + **Example** + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ abs + + Displays the absolute value of a number. + { v|abs } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ capitalize + + Displays the string in sentence case: all + { v|capitalize + ++ + + lowercase with firstletter capitalized + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ date + format (Y-m-d) + A PHP **date**-compatible formatting string. + { v|date(Y-m-d) } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ date_modify + value to add + A **strtotime** compatible string to modify the + { v|date_modify(+1 day) } + ++ + / subtract + date, like ``+5 day`` or ``-1 week``. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ default + default value + Displays the default value if the variable is empty+ { v|default(just in case) } + ++ + + or undefined. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ esc + html, attr, css, js + Specifies the context to escape the data. + { v|esc(attr) } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ excerpt + phrase, radius + Returns the text within a radius of words from a + { v|excerpt(green giant, 20) } + ++ + + given phrase. Same as **excerpt** helper function. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ highlight + phrase + Highlights a given phrase within the text using + { v|highlight(view parser) } + ++ + + '' tags. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ highlight_code+ + Highlights code samples with HTML/CSS. + { v|highlight_code } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ limit_chars + limit + Limits the number of characters to $limit. + { v|limit_chars(100) } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ limit_words + limit + Limits the number of words to $limit. + { v|limit_words(20) } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ local_currency+ currency, locale + Displays a localized version of a currency. + { v|local_currency(EUR,en_US) } + ++ + + "currency" valueis any 3-letter ISO 4217 currency + + ++ + + code. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ local_number + type, precision, + Displays a localized version of a number. "type" + { v|local_number(decimal,2,en_US) } + ++ + locale + can be one of: decimal, currency, percent, + + ++ + + scientific, spellout, ordinal, duration. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ lower + + Converts a string to lowercase. + { v|lower } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ nl2br + + Replaces all newline characters (\n) to an HTML + { v|nl2br } + ++ + +
tag. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ number_format + places + Wraps PHP **number_format** function for use within+ { v|number_format(3) } + ++ + + the parser. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ prose + + Takes a body of text and uses the + { v|prose } + ++ + + **auto_typography()** method to turn it into + + ++ + + a prettier, easier-to-read, prose. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ round + places, type + Rounds a number to the specified places. Types of + { v|round(3) } { v|round(ceil) } + ++ + + **cell** and **floor** can be passed to use those + + ++ + + functions instead. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ strip_tags + allowed chars + Wraps PHP **strip_tags**. Can accept a string of + { v|strip_tags(
) } + ++ + + allowed tags. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ title + + Displays a "title case" version of the string, with+ { v|title } + ++ + + all lowercase, and each word capitalized. + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ upper + + Displays the string in all uppercase. + { v|upper } + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++ + + + + ++---------------+---------------------+----------------------------------------------------+-------------------------------------+ See `PHP's NumberFormatter `_ for details relevant to the "local_number" filter. From a8781e16fde674e73b54d239800dbacc4d639ff9 Mon Sep 17 00:00:00 2001 From: InsiteFX Date: Thu, 24 Sep 2020 07:17:23 -0400 Subject: [PATCH 152/328] Update view_paser.rst [ci skip] Tried a different table still cuts of the end so reverting back to orginal file. --- .../source/outgoing/view_parser.rst | 124 +++++++++--------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index 1790a4723385..fd46e85832d6 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -401,70 +401,66 @@ Provided Filters The following filters are available when using the parser: -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ **Filter** + **Arguments** + **Description** + **Example** + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ abs + + Displays the absolute value of a number. + { v|abs } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ capitalize + + Displays the string in sentence case: all + { v|capitalize + -+ + + lowercase with firstletter capitalized + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ date + format (Y-m-d) + A PHP **date**-compatible formatting string. + { v|date(Y-m-d) } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ date_modify + value to add + A **strtotime** compatible string to modify the + { v|date_modify(+1 day) } + -+ + / subtract + date, like ``+5 day`` or ``-1 week``. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ default + default value + Displays the default value if the variable is empty+ { v|default(just in case) } + -+ + + or undefined. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ esc + html, attr, css, js + Specifies the context to escape the data. + { v|esc(attr) } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ excerpt + phrase, radius + Returns the text within a radius of words from a + { v|excerpt(green giant, 20) } + -+ + + given phrase. Same as **excerpt** helper function. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ highlight + phrase + Highlights a given phrase within the text using + { v|highlight(view parser) } + -+ + + '' tags. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ highlight_code+ + Highlights code samples with HTML/CSS. + { v|highlight_code } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ limit_chars + limit + Limits the number of characters to $limit. + { v|limit_chars(100) } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ limit_words + limit + Limits the number of words to $limit. + { v|limit_words(20) } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ local_currency+ currency, locale + Displays a localized version of a currency. + { v|local_currency(EUR,en_US) } + -+ + + "currency" valueis any 3-letter ISO 4217 currency + + -+ + + code. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ local_number + type, precision, + Displays a localized version of a number. "type" + { v|local_number(decimal,2,en_US) } + -+ + locale + can be one of: decimal, currency, percent, + + -+ + + scientific, spellout, ordinal, duration. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ lower + + Converts a string to lowercase. + { v|lower } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ nl2br + + Replaces all newline characters (\n) to an HTML + { v|nl2br } + -+ + +
tag. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ number_format + places + Wraps PHP **number_format** function for use within+ { v|number_format(3) } + -+ + + the parser. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ prose + + Takes a body of text and uses the + { v|prose } + -+ + + **auto_typography()** method to turn it into + + -+ + + a prettier, easier-to-read, prose. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ round + places, type + Rounds a number to the specified places. Types of + { v|round(3) } { v|round(ceil) } + -+ + + **cell** and **floor** can be passed to use those + + -+ + + functions instead. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ strip_tags + allowed chars + Wraps PHP **strip_tags**. Can accept a string of + { v|strip_tags(
) } + -+ + + allowed tags. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ title + + Displays a "title case" version of the string, with+ { v|title } + -+ + + all lowercase, and each word capitalized. + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ upper + + Displays the string in all uppercase. + { v|upper } + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ -+ + + + + -+---------------+---------------------+----------------------------------------------------+-------------------------------------+ ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ **Filter** + **Arguments** + **Description** + **Example** + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ abs + + Displays the absolute value of a number. + { v|abs } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ capitalize + + Displays the string in sentence case: all lowercase + { v|capitalize} + ++ + + with firstletter capitalized. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ date + format (Y-m-d) + A PHP **date**-compatible formatting string. + { v|date(Y-m-d) } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ date_modify + value to add + A **strtotime** compatible string to modify the date, + { v|date_modify(+1 day) } + ++ + / subtract + like ``+5 day`` or ``-1 week``. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ default + default value + Displays the default value if the variable is empty or + { v|default(just in case) } + ++ + + undefined. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ esc + html, attr, css, js + Specifies the context to escape the data. + { v|esc(attr) } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ excerpt + phrase, radius + Returns the text within a radius of words from a given + { v|excerpt(green giant, 20) } + ++ + + phrase. Same as **excerpt** helper function. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ highlight + phrase + Highlights a given phrase within the text using + { v|highlight(view parser) } + ++ + + '' tags. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ highlight_code+ + Highlights code samples with HTML/CSS. + { v|highlight_code } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ limit_chars + limit + Limits the number of characters to $limit. + { v|limit_chars(100) } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ limit_words + limit + Limits the number of words to $limit. + { v|limit_words(20) } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ local_currency+ currency, locale + Displays a localized version of a currency. "currency" + { v|local_currency(EUR,en_US) } + ++ + + valueis any 3-letter ISO 4217 currency code. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ local_number + type, precision, + Displays a localized version of a number. "type" can be + { v|local_number(decimal,2,en_US) } + ++ + locale + one of: decimal, currency, percent, scientific, spellout, + + ++ + + ordinal, duration. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ lower + + Converts a string to lowercase. + { v|lower } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ nl2br + + Replaces all newline characters (\n) to an HTML
tag. + { v|nl2br } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ number_format + places + Wraps PHP **number_format** function for use within the + { v|number_format(3) } + ++ + + parser. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ prose + + Takes a body of text and uses the **auto_typography()** + { v|prose } + ++ + + method to turn it into prettier, easier-to-read, prose. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ round + places, type + Rounds a number to the specified places. Types of **ceil** + { v|round(3) } { v|round(ceil) } + ++ + + and **floor** can be passed to use those functions instead. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ strip_tags + allowed chars + Wraps PHP **strip_tags**. Can accept a string of allowed + { v|strip_tags(
) } + ++ + + tags. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ title + + Displays a "title case" version of the string, with all + { v|title } + ++ + + lowercase, and each word capitalized. + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ upper + + Displays the string in all uppercase. + { v|upper } + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ ++ + + + + ++---------------+---------------------+--------------------------------------------------------------+-------------------------------------+ See `PHP's NumberFormatter `_ for details relevant to the "local_number" filter. From e65512d7e9005f01b1b54955efd209ce4754ca01 Mon Sep 17 00:00:00 2001 From: InsiteFX Date: Thu, 24 Sep 2020 11:39:20 -0400 Subject: [PATCH 153/328] Update response.rst [ci skip] Removed double word. --- user_guide_src/source/outgoing/response.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/response.rst b/user_guide_src/source/outgoing/response.rst index 1a9504552e79..36db5570fdf1 100644 --- a/user_guide_src/source/outgoing/response.rst +++ b/user_guide_src/source/outgoing/response.rst @@ -120,7 +120,7 @@ introduction to all of the cache headers power, but you can get a good understan By default, all response objects sent through CodeIgniter have HTTP caching turned off. The options and exact circumstances are too varied for us to be able to create a good default other than turning it off. It's simple -to set the Cache values to what you need, though, through the ``setCache()`` method:: +to set the Cache values to what you need, through the ``setCache()`` method:: $options = [ 'max-age' => 300, From 3489398ae7b879daf0a49728bd549868fa8be850 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 25 Sep 2020 15:57:47 +0800 Subject: [PATCH 154/328] Update intro/index.rst [ci skip] --- user_guide_src/source/intro/index.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/intro/index.rst b/user_guide_src/source/intro/index.rst index 4ef55381d8b2..1aa920d86838 100644 --- a/user_guide_src/source/intro/index.rst +++ b/user_guide_src/source/intro/index.rst @@ -17,9 +17,9 @@ or completely replaced to make the system work the way you need it to. In short, CodeIgniter is the malleable framework that tries to provide the tools you need while staying out of the way. -*********************** -Who is CodeIgniter For? -*********************** +***************************** +Is CodeIgniter Right for You? +***************************** CodeIgniter is right for you if: @@ -44,4 +44,3 @@ CodeIgniter is right for you if: requirements credits psr - From 3e767ad5c4ca36bbe2ca376209aaf01788b6534e Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 25 Sep 2020 16:00:44 +0800 Subject: [PATCH 155/328] Update view cell doc [ci skip] --- user_guide_src/source/outgoing/view_cells.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/outgoing/view_cells.rst b/user_guide_src/source/outgoing/view_cells.rst index 510c91d8e2fe..7aba0f6ed554 100644 --- a/user_guide_src/source/outgoing/view_cells.rst +++ b/user_guide_src/source/outgoing/view_cells.rst @@ -16,7 +16,7 @@ must return the generated HTML as a string. The method can be either a static me Cell Parameters --------------- -You can further refine the call by passing a list of parameters in the second parameter to the method. The values passed +You can further refine the call by passing a list of parameters in the second parameter to the method. The values passed can be an array of key/value pairs, or a comma-separated string of key/value pairs:: // Passing Parameter Array @@ -25,7 +25,7 @@ can be an array of key/value pairs, or a comma-separated string of key/value pai // Passing Parameter String - public function recentPosts(array $params=[]) + public function recentPosts(array $params = []) { $posts = $this->blogModel->where('category', $params['category']) ->orderBy('published_on', 'desc') @@ -40,7 +40,7 @@ When you use it this way, all of the parameters must always be specified in the - public function recentPosts(int $limit, string $category) + public function recentPosts(string $category, int $limit) { $posts = $this->blogModel->where('category', $category) ->orderBy('published_on', 'desc') From 69d5a8b9282ccd71a14ef0ac8b77937044397bf9 Mon Sep 17 00:00:00 2001 From: MGatner Date: Wed, 23 Sep 2020 16:30:50 +0000 Subject: [PATCH 156/328] Normalize url_is test path --- system/Helpers/url_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index 562f98b4bcb1..d626c0215678 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -675,7 +675,7 @@ function url_is(string $path): bool { // Setup our regex to allow wildcards $path = '/' . trim(str_replace('*', '(\S)*', $path), '/ '); - $currentPath = rtrim(service('request')->uri->getPath(), '/ '); + $currentPath = '/' . trim(service('request')->uri->getPath(), '/ '); return (bool)preg_match("|^{$path}$|", $currentPath, $matches); } From 0dea8a0f8e91ce67b2947bd7f24318db7c3ebc3d Mon Sep 17 00:00:00 2001 From: Agung Sugiarto Date: Mon, 21 Sep 2020 15:03:43 +0700 Subject: [PATCH 157/328] Cache command info only for file cache handler --- system/Commands/Cache/InfoCache.php | 19 ++++--------------- tests/system/Commands/InfoCacheTest.php | 14 -------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/system/Commands/Cache/InfoCache.php b/system/Commands/Cache/InfoCache.php index a6b359783183..30218673791d 100644 --- a/system/Commands/Cache/InfoCache.php +++ b/system/Commands/Cache/InfoCache.php @@ -33,16 +33,7 @@ class InfoCache extends BaseCommand * * @var string */ - protected $usage = 'cache:info [driver]'; - - /** - * the Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'driver' => 'The cache driver to use', - ]; + protected $usage = 'cache:info'; /** * Clears the cache @@ -54,15 +45,13 @@ public function run(array $params) $config = config('Cache'); helper('number'); - $handler = $params[0] ?? $config->handler; - if (! array_key_exists($handler, $config->validHandlers)) + if ($config->handler !== 'file') { - CLI::error($handler . ' is not a valid cache handler.'); + CLI::error('This command only supports the file cache handler.'); return; } - $config->handler = $handler; - $cache = CacheFactory::getHandler($config); + $cache = CacheFactory::getHandler($config); $caches = $cache->getCacheInfo(); diff --git a/tests/system/Commands/InfoCacheTest.php b/tests/system/Commands/InfoCacheTest.php index 783a40123af5..353d4345f437 100644 --- a/tests/system/Commands/InfoCacheTest.php +++ b/tests/system/Commands/InfoCacheTest.php @@ -24,11 +24,6 @@ protected function setUp(): void public function tearDown(): void { - if (! $this->result) - { - return; - } - stream_filter_remove($this->streamFilter); } @@ -37,15 +32,6 @@ protected function getBuffer() return CITestStreamFilter::$buffer; } - public function testInfoCacheInvalidHandler() - { - command('cache:info notfound'); - - $result = CITestStreamFilter::$buffer; - - $this->assertStringContainsString('notfound is not a valid cache handler.', $result); - } - public function testInfoCacheCanSeeFoo() { cache()->save('foo', 'bar'); From 15a60aad23a2e301a4c4292befb9f52827f1513a Mon Sep 17 00:00:00 2001 From: MGatner Date: Fri, 25 Sep 2020 14:54:14 +0000 Subject: [PATCH 158/328] Enforce baseURL trailing slash --- admin/framework/phpunit.xml.dist | 2 +- admin/module/phpunit.xml.dist | 2 +- admin/starter/phpunit.xml.dist | 2 +- phpunit.xml.dist | 2 +- system/HTTP/URI.php | 4 ++-- system/Test/ControllerTester.php | 2 +- system/Test/Mock/MockAppConfig.php | 2 +- system/Test/Mock/MockCLIConfig.php | 2 +- system/Test/bootstrap.php | 2 +- tests/system/API/ResponseTraitTest.php | 4 ++-- tests/system/CLI/CommandRunnerTest.php | 2 +- tests/system/CLI/ConsoleTest.php | 2 +- tests/system/CommonFunctionsTest.php | 4 ++-- tests/system/HTTP/IncomingRequestTest.php | 12 ++++++------ tests/system/HTTP/RedirectResponseTest.php | 2 +- tests/system/HTTP/ResponseTest.php | 2 +- tests/system/Validation/ValidationTest.php | 10 +++++----- user_guide_src/source/installation/running.rst | 3 ++- 18 files changed, 31 insertions(+), 30 deletions(-) diff --git a/admin/framework/phpunit.xml.dist b/admin/framework/phpunit.xml.dist index 88aca1fb5c29..a0f8204f7160 100644 --- a/admin/framework/phpunit.xml.dist +++ b/admin/framework/phpunit.xml.dist @@ -36,7 +36,7 @@ - + diff --git a/admin/module/phpunit.xml.dist b/admin/module/phpunit.xml.dist index 4d872910916e..8ecb72e70ed8 100644 --- a/admin/module/phpunit.xml.dist +++ b/admin/module/phpunit.xml.dist @@ -36,7 +36,7 @@ - + diff --git a/admin/starter/phpunit.xml.dist b/admin/starter/phpunit.xml.dist index 96947df5a615..64628e210e3d 100644 --- a/admin/starter/phpunit.xml.dist +++ b/admin/starter/phpunit.xml.dist @@ -36,7 +36,7 @@ - + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 12bd7b9778e8..719eec4cc761 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -41,7 +41,7 @@ - + diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index df57a86fcda2..6dd5ce49b7c8 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -614,8 +614,8 @@ public function __toString(): string } return static::createURIString( - $this->getScheme(), $this->getAuthority(), $path, // Absolute URIs should use a "/" for an empty path - $this->getQuery(), $this->getFragment() + $this->getScheme(), $this->getAuthority(), $path, // Absolute URIs should use a "/" for an empty path + $this->getQuery(), $this->getFragment() ); } diff --git a/system/Test/ControllerTester.php b/system/Test/ControllerTester.php index 89d443199ada..b606d65a776a 100644 --- a/system/Test/ControllerTester.php +++ b/system/Test/ControllerTester.php @@ -131,7 +131,7 @@ public function controller(string $name) if (! $this->uri instanceof URI) { - $this->uri = new URI($this->appConfig->baseURL ?? 'http://example.com'); + $this->uri = new URI($this->appConfig->baseURL ?? 'http://example.com/'); } if (empty($this->request)) diff --git a/system/Test/Mock/MockAppConfig.php b/system/Test/Mock/MockAppConfig.php index b2adcb0206ac..ae84511f0926 100644 --- a/system/Test/Mock/MockAppConfig.php +++ b/system/Test/Mock/MockAppConfig.php @@ -2,7 +2,7 @@ class MockAppConfig { - public $baseURL = 'http://example.com'; + public $baseURL = 'http://example.com/'; public $uriProtocol = 'REQUEST_URI'; diff --git a/system/Test/Mock/MockCLIConfig.php b/system/Test/Mock/MockCLIConfig.php index 7c9996302374..3b3b86d8d068 100644 --- a/system/Test/Mock/MockCLIConfig.php +++ b/system/Test/Mock/MockCLIConfig.php @@ -2,7 +2,7 @@ class MockCLIConfig extends \Config\App { - public $baseURL = 'http://example.com'; + public $baseURL = 'http://example.com/'; public $uriProtocol = 'REQUEST_URI'; diff --git a/system/Test/bootstrap.php b/system/Test/bootstrap.php index 3d5e822691b2..90d8991a5c8b 100644 --- a/system/Test/bootstrap.php +++ b/system/Test/bootstrap.php @@ -47,7 +47,7 @@ // Set environment values that would otherwise stop the framework from functioning during tests. if (! isset($_SERVER['app.baseURL'])) { - $_SERVER['app.baseURL'] = 'http://example.com'; + $_SERVER['app.baseURL'] = 'http://example.com/'; } // Load necessary components diff --git a/tests/system/API/ResponseTraitTest.php b/tests/system/API/ResponseTraitTest.php index 724d5100d196..e7639d21233b 100644 --- a/tests/system/API/ResponseTraitTest.php +++ b/tests/system/API/ResponseTraitTest.php @@ -29,7 +29,7 @@ protected function setUp(): void protected function makeController(array $userConfig = [], string $uri = 'http://example.com', array $userHeaders = []) { $config = [ - 'baseURL' => 'http://example.com', + 'baseURL' => 'http://example.com/', 'uriProtocol' => 'REQUEST_URI', 'defaultLocale' => 'en', 'negotiateLocale' => false, @@ -466,7 +466,7 @@ public function testXMLFormatter() public function testFormatByRequestNegotiateIfFormatIsNotJsonOrXML() { $config = [ - 'baseURL' => 'http://example.com', + 'baseURL' => 'http://example.com/', 'uriProtocol' => 'REQUEST_URI', 'defaultLocale' => 'en', 'negotiateLocale' => false, diff --git a/tests/system/CLI/CommandRunnerTest.php b/tests/system/CLI/CommandRunnerTest.php index cc14b26e0eb1..2eb513991f04 100644 --- a/tests/system/CLI/CommandRunnerTest.php +++ b/tests/system/CLI/CommandRunnerTest.php @@ -30,7 +30,7 @@ protected function setUp(): void // Set environment values that would otherwise stop the framework from functioning during tests. if (! isset($_SERVER['app.baseURL'])) { - $_SERVER['app.baseURL'] = 'http://example.com'; + $_SERVER['app.baseURL'] = 'http://example.com/'; } $_SERVER['argv'] = [ diff --git a/tests/system/CLI/ConsoleTest.php b/tests/system/CLI/ConsoleTest.php index 2dd6b8e8db72..458dd29b6d1a 100644 --- a/tests/system/CLI/ConsoleTest.php +++ b/tests/system/CLI/ConsoleTest.php @@ -27,7 +27,7 @@ protected function setUp(): void // Set environment values that would otherwise stop the framework from functioning during tests. if (! isset($_SERVER['app.baseURL'])) { - $_SERVER['app.baseURL'] = 'http://example.com'; + $_SERVER['app.baseURL'] = 'http://example.com/'; } $_SERVER['argv'] = [ diff --git a/tests/system/CommonFunctionsTest.php b/tests/system/CommonFunctionsTest.php index 3bb54f11b85d..5cdcf464fb3a 100644 --- a/tests/system/CommonFunctionsTest.php +++ b/tests/system/CommonFunctionsTest.php @@ -298,7 +298,7 @@ public function testOldInput() $_SERVER['REQUEST_METHOD'] = 'GET'; $this->config = new App(); - $this->config->baseURL = 'http://example.com'; + $this->config->baseURL = 'http://example.com/'; $this->routes = new RouteCollection(Services::locator(), new \Config\Modules()); Services::injectMock('routes', $this->routes); @@ -334,7 +334,7 @@ public function testOldInputArray() $_SERVER['REQUEST_METHOD'] = 'GET'; $this->config = new App(); - $this->config->baseURL = 'http://example.com'; + $this->config->baseURL = 'http://example.com/'; $this->routes = new RouteCollection(Services::locator(), new \Config\Modules()); Services::injectMock('routes', $this->routes); diff --git a/tests/system/HTTP/IncomingRequestTest.php b/tests/system/HTTP/IncomingRequestTest.php index 6080fcdbb177..5ece3a30132c 100644 --- a/tests/system/HTTP/IncomingRequestTest.php +++ b/tests/system/HTTP/IncomingRequestTest.php @@ -176,7 +176,7 @@ public function testSetLocaleSaves() 'es', ]; $config->defaultLocale = 'es'; - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), null, new UserAgent()); @@ -192,7 +192,7 @@ public function testSetBadLocale() 'es', ]; $config->defaultLocale = 'es'; - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), null, new UserAgent()); @@ -215,7 +215,7 @@ public function testNegotiatesLocale() 'fr', 'en', ]; - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), null, new UserAgent()); @@ -233,7 +233,7 @@ public function testNegotiatesLocaleOnlyBroad() 'fr', 'en', ]; - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), null, new UserAgent()); @@ -291,7 +291,7 @@ public function testCanGrabGetRawJSON() ]; $config = new App(); - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), $json, new UserAgent()); @@ -309,7 +309,7 @@ public function testCanGrabGetRawInput() ]; $config = new App(); - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), $rawstring, new UserAgent()); diff --git a/tests/system/HTTP/RedirectResponseTest.php b/tests/system/HTTP/RedirectResponseTest.php index 89e420f0b57f..83b975f36ed7 100644 --- a/tests/system/HTTP/RedirectResponseTest.php +++ b/tests/system/HTTP/RedirectResponseTest.php @@ -26,7 +26,7 @@ protected function setUp(): void $_SERVER['REQUEST_METHOD'] = 'GET'; $this->config = new App(); - $this->config->baseURL = 'http://example.com'; + $this->config->baseURL = 'http://example.com/'; $this->routes = new RouteCollection(Services::locator(), new \Config\Modules()); Services::injectMock('routes', $this->routes); diff --git a/tests/system/HTTP/ResponseTest.php b/tests/system/HTTP/ResponseTest.php index 3eba93b1fc91..3d85f1399598 100644 --- a/tests/system/HTTP/ResponseTest.php +++ b/tests/system/HTTP/ResponseTest.php @@ -165,7 +165,7 @@ public function testSetLink() { // Ensure our URL is not getting overridden $config = new App(); - $config->baseURL = 'http://example.com/test'; + $config->baseURL = 'http://example.com/test/'; Config::injectMock('App', $config); $response = new Response($config); diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index d1d01bc08f51..d097f7ac1aae 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -441,7 +441,7 @@ public function testRawInput() ]; $config = new App(); - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), $rawstring, new UserAgent()); $request->setMethod('patch'); @@ -666,7 +666,7 @@ public function testTagReplacement() public function testRulesForArrayField($body, $rules, $results) { $config = new App(); - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $request = new IncomingRequest($config, new URI(), http_build_query($body), new UserAgent()); $request->setMethod('post'); @@ -740,7 +740,7 @@ public function arrayFieldDataProvider() public function testRulesForSingleRuleWithAsteriskWillReturnNoError() { $config = new App(); - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $_REQUEST = [ 'id_user' => [ @@ -771,7 +771,7 @@ public function testRulesForSingleRuleWithAsteriskWillReturnNoError() public function testRulesForSingleRuleWithAsteriskWillReturnError() { $config = new App(); - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $_REQUEST = [ 'id_user' => [ @@ -805,7 +805,7 @@ public function testRulesForSingleRuleWithAsteriskWillReturnError() public function testRulesForSingleRuleWithSingleValue() { $config = new App(); - $config->baseURL = 'http://example.com'; + $config->baseURL = 'http://example.com/'; $_REQUEST = [ 'id_user' => 'gh', diff --git a/user_guide_src/source/installation/running.rst b/user_guide_src/source/installation/running.rst index d178262e3659..ca8b838a9c81 100644 --- a/user_guide_src/source/installation/running.rst +++ b/user_guide_src/source/installation/running.rst @@ -17,7 +17,8 @@ Initial Configuration & Set Up #. Open the **app/Config/App.php** file with a text editor and set your base URL. If you need more flexibility, the baseURL may - be set within the ``.env`` file as **app.baseURL="http://example.com"**. + be set within the ``.env`` file as **app.baseURL="http://example.com/"**. + (Always use a trailing slash on your base URL!) #. If you intend to use a database, open the **app/Config/Database.php** file with a text editor and set your database settings. Alternately, these could be set in your ``.env`` file. From 29339a65c36b8135bd128e91fc65dd6fe91a03d3 Mon Sep 17 00:00:00 2001 From: MGatner Date: Fri, 25 Sep 2020 15:12:09 +0000 Subject: [PATCH 159/328] Rework and add URL tests for baseURL slashes --- tests/system/Helpers/URLHelperTest.php | 500 +++++++++++-------------- 1 file changed, 219 insertions(+), 281 deletions(-) diff --git a/tests/system/Helpers/URLHelperTest.php b/tests/system/Helpers/URLHelperTest.php index 86021167940d..fd188920f2cb 100644 --- a/tests/system/Helpers/URLHelperTest.php +++ b/tests/system/Helpers/URLHelperTest.php @@ -11,6 +11,10 @@ */ class URLHelperTest extends \CodeIgniter\Test\CIUnitTestCase { + /** + * @var App + */ + protected $config; protected function setUp(): void { @@ -19,6 +23,15 @@ protected function setUp(): void helper('url'); Services::reset(true); Config::reset(); + + // Set a common base configuration (overriden by individual tests) + $this->config = new App(); + $this->config->baseURL = 'http://example.com/'; + $this->config->indexPage = 'index.php'; + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/'; + + //Config::injectMock('App', $this->config); } public function tearDown(): void @@ -33,147 +46,108 @@ public function tearDown(): void public function testSiteURLBasics() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/index.php', site_url('', null, $config)); + $this->assertEquals('http://example.com/index.php', site_url('', null, $this->config)); } public function testSiteURLHTTPS() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - $_SERVER['HTTPS'] = 'on'; + $_SERVER['HTTPS'] = 'on'; - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('https://example.com/index.php', site_url('', null, $config)); + $this->assertEquals('https://example.com/index.php', site_url('', null, $this->config)); } - public function testSiteURLNoIndex() + public function testSiteURLNoTrailingSlash() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; + $this->config->baseURL = 'http://example.com'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/index.php'); - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = ''; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + Services::injectMock('request', $request); + + $this->assertEquals('http://example.com/index.php', site_url('', null, $this->config)); + } + + public function testSiteURLNoIndex() + { + $this->config->indexPage = ''; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/', site_url('', null, $config)); + $this->assertEquals('http://example.com/', site_url('', null, $this->config)); } public function testSiteURLDifferentIndex() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'banana.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $this->config->indexPage = 'banana.php'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/banana.php', site_url('', null, $config)); + $this->assertEquals('http://example.com/banana.php', site_url('', null, $this->config)); } public function testSiteURLNoIndexButPath() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = ''; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $this->config->indexPage = ''; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/abc', site_url('abc', null, $config)); + $this->assertEquals('http://example.com/abc', site_url('abc', null, $this->config)); } public function testSiteURLAttachesPath() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/index.php/foo', site_url('foo', null, $config)); + $this->assertEquals('http://example.com/index.php/foo', site_url('foo', null, $this->config)); } public function testSiteURLAttachesScheme() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('ftp://example.com/index.php/foo', site_url('foo', 'ftp', $config)); + $this->assertEquals('ftp://example.com/index.php/foo', site_url('foo', 'ftp', $this->config)); } public function testSiteURLExample() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/index.php/news/local/123', site_url('news/local/123', null, $config)); + $this->assertEquals('http://example.com/index.php/news/local/123', site_url('news/local/123', null, $this->config)); } public function testSiteURLSegments() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/index.php/news/local/123', site_url(['news', 'local', '123'], null, $config)); + $this->assertEquals('http://example.com/index.php/news/local/123', site_url(['news', 'local', '123'], null, $this->config)); } /** @@ -185,10 +159,8 @@ public function testSiteURLWithSegments() $_SERVER['REQUEST_URI'] = '/test'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/'; - $request = Services::request($config, false); - $request->uri = new URI('http://example.com/test'); + $request = Services::request($this->config, false); + $request->uri = new URI('http://example.com/test'); Services::injectMock('request', $request); @@ -204,10 +176,8 @@ public function testSiteURLWithSegmentsAgain() $_SERVER['REQUEST_URI'] = '/test/page'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com'; - $request = Services::request($config, false); - $request->uri = new URI('http://example.com/test/page'); + $request = Services::request($this->config, false); + $request->uri = new URI('http://example.com/test/page'); Services::injectMock('request', $request); @@ -220,57 +190,50 @@ public function testSiteURLWithSegmentsAgain() public function testBaseURLBasics() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - $this->assertEquals('http://example.com', base_url()); } public function testBaseURLAttachesPath() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - $this->assertEquals('http://example.com/foo', base_url('foo')); } public function testBaseURLAttachesPathArray() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - $this->assertEquals('http://example.com/foo/bar', base_url(['foo', 'bar'])); } public function testBaseURLAttachesScheme() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - $this->assertEquals('https://example.com/foo', base_url('foo', 'https')); } public function testBaseURLHeedsBaseURL() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/public'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/public'); + $this->config->baseURL = 'http://example.com/public'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/public'); Services::injectMock('request', $request); $this->assertEquals('http://example.com/public', base_url()); } - public function testBaseURLExample() + public function testBaseURLNoTrailingSlash() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; + // Since we're on a CLI, we must provide our own URI + $this->config->baseURL = 'http://example.com'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/foobar'); + + Services::injectMock('request', $request); + $this->assertEquals('http://example.com', base_url()); + } + + public function testBaseURLExample() + { $this->assertEquals('http://example.com/blog/post/123', base_url('blog/post/123')); } @@ -283,10 +246,8 @@ public function testBaseURLWithSegments() $_SERVER['REQUEST_URI'] = '/test'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/'; - $request = Services::request($config, false); - $request->uri = new URI('http://example.com/test'); + $request = Services::request($this->config, false); + $request->uri = new URI('http://example.com/test'); Services::injectMock('request', $request); @@ -298,9 +259,7 @@ public function testBaseURLWithSegments() */ public function testBaseURLHTTPS() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - $_SERVER['HTTPS'] = 'on'; + $_SERVER['HTTPS'] = 'on'; $this->assertEquals('https://example.com/blog/post/123', base_url('blog/post/123')); } @@ -314,10 +273,8 @@ public function testBaseURLWithSegmentsAgain() $_SERVER['REQUEST_URI'] = '/test/page'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com'; - $request = Services::request($config, false); - $request->uri = new URI('http://example.com/test/page'); + $request = Services::request($this->config, false); + $request->uri = new URI('http://example.com/test/page'); Services::injectMock('request', $request); @@ -332,11 +289,27 @@ public function testBaseURLHasSubfolder() $_SERVER['SCRIPT_NAME'] = '/subfolder/index.php'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/subfolder'; - Config::injectMock('App', $config); + $this->config->baseURL = 'http://example.com/subfolder/'; + Config::injectMock('App', $this->config); - $request = Services::request($config, false); + $request = Services::request($this->config, false); + Services::injectMock('request', $request); + + $this->assertEquals('http://example.com/subfolder/foo', base_url('foo')); + $this->assertEquals('http://example.com/subfolder', base_url()); + } + + public function testBaseURLNoTrailingSlashHasSubfolder() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/subfolder/test'; + $_SERVER['SCRIPT_NAME'] = '/subfolder/index.php'; + + // Since we're on a CLI, we must provide our own URI + $this->config->baseURL = 'http://example.com/subfolder'; + Config::injectMock('App', $this->config); + + $request = Services::request($this->config, false); Services::injectMock('request', $request); $this->assertEquals('http://example.com/subfolder/foo', base_url('foo')); @@ -348,14 +321,10 @@ public function testBaseURLHasSubfolder() public function testCurrentURLReturnsBasicURL() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/public'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/public'); + $this->config->baseURL = 'http://example.com/public'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/public'); Services::injectMock('request', $request); @@ -364,14 +333,10 @@ public function testCurrentURLReturnsBasicURL() public function testCurrentURLReturnsObject() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/public'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/public'); + $this->config->baseURL = 'http://example.com/public'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/public'); Services::injectMock('request', $request); @@ -388,11 +353,9 @@ public function testCurrentURLEquivalence() $_SERVER['SCRIPT_NAME'] = '/index.php'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/'; - Config::injectMock('App', $config); + Config::injectMock('App', $this->config); - $request = Services::request($config); + $request = Services::request($this->config); Services::injectMock('request', $request); $this->assertEquals(base_url(uri_string()), current_url()); @@ -405,11 +368,10 @@ public function testCurrentURLInSubfolder() $_SERVER['SCRIPT_NAME'] = '/foo/public/index.php'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/foo/public'; - Config::injectMock('App', $config); + $this->config->baseURL = 'http://example.com/foo/public'; + Config::injectMock('App', $this->config); - $request = Services::request($config); + $request = Services::request($this->config); Services::injectMock('request', $request); $this->assertEquals('http://example.com/foo/public/bar', current_url()); @@ -430,11 +392,10 @@ public function testCurrentURLWithPortInSubfolder() $_SERVER['SCRIPT_NAME'] = '/foo/public/index.php'; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com:8080/foo/public'; - Config::injectMock('App', $config); + $this->config->baseURL = 'http://example.com:8080/foo/public'; + Config::injectMock('App', $this->config); - $request = Services::request($config); + $request = Services::request($this->config); Services::injectMock('request', $request); $this->assertEquals('http://example.com:8080/foo/public/bar', current_url()); @@ -456,16 +417,13 @@ public function testPreviousURLUsesSessionFirst() $uri1 = 'http://example.com/one?two'; $uri2 = 'http://example.com/two?foo'; - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; $_SERVER['HTTP_REFERER'] = $uri1; $_SESSION['_ci_previous_url'] = $uri2; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/public'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/public'); + $this->config->baseURL = 'http://example.com/public'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/public'); Services::injectMock('request', $request); @@ -479,15 +437,12 @@ public function testPreviousURLUsesRefererIfNeeded() $uri1 = 'http://example.com/one?two'; $uri2 = 'http://example.com/two?foo'; - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; $_SERVER['HTTP_REFERER'] = $uri1; // Since we're on a CLI, we must provide our own URI - $config = new App(); - $config->baseURL = 'http://example.com/public'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/public'); + $this->config->baseURL = 'http://example.com/public'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/public'); Services::injectMock('request', $request); @@ -499,14 +454,8 @@ public function testPreviousURLUsesRefererIfNeeded() public function testUriString() { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); @@ -519,11 +468,8 @@ public function testUriStringExample() $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/assets/image.jpg'; - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/assets/image.jpg'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/assets/image.jpg'); Services::injectMock('request', $request); @@ -536,11 +482,8 @@ public function testUriStringExample() public function testIndexPage() { - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); @@ -549,15 +492,13 @@ public function testIndexPage() public function testIndexPageAlt() { - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'banana.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $this->config->indexPage = 'banana.php'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals('banana.php', index_page($config)); + $this->assertEquals('banana.php', index_page($this->config)); } //-------------------------------------------------------------------- @@ -611,17 +552,11 @@ public function anchorNormalPatterns() */ public function testAnchor($expected = '', $uri = '', $title = '', $attributes = '') { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals($expected, anchor($uri, $title, $attributes, $config)); + $this->assertEquals($expected, anchor($uri, $title, $attributes, $this->config)); } public function anchorNoindexPatterns() @@ -672,17 +607,12 @@ public function anchorNoindexPatterns() */ public function testAnchorNoindex($expected = '', $uri = '', $title = '', $attributes = '') { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = ''; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $this->config->indexPage = ''; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals($expected, anchor($uri, $title, $attributes, $config)); + $this->assertEquals($expected, anchor($uri, $title, $attributes, $this->config)); } public function anchorSubpagePatterns() @@ -729,17 +659,12 @@ public function anchorSubpagePatterns() */ public function testAnchorTargetted($expected = '', $uri = '', $title = '', $attributes = '') { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = ''; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $this->config->indexPage = ''; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals($expected, anchor($uri, $title, $attributes, $config)); + $this->assertEquals($expected, anchor($uri, $title, $attributes, $this->config)); } public function anchorExamplePatterns() @@ -775,17 +700,11 @@ public function anchorExamplePatterns() */ public function testAnchorExamples($expected = '', $uri = '', $title = '', $attributes = '') { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals($expected, anchor($uri, $title, $attributes, $config)); + $this->assertEquals($expected, anchor($uri, $title, $attributes, $this->config)); } //-------------------------------------------------------------------- @@ -836,17 +755,11 @@ public function anchorPopupPatterns() */ public function testAnchorPopup($expected = '', $uri = '', $title = '', $attributes = false) { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); - $this->assertEquals($expected, anchor_popup($uri, $title, $attributes, $config)); + $this->assertEquals($expected, anchor_popup($uri, $title, $attributes, $this->config)); } //-------------------------------------------------------------------- @@ -878,14 +791,8 @@ public function mailtoPatterns() */ public function testMailto($expected = '', $email, $title = '', $attributes = '') { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); @@ -921,14 +828,8 @@ public function safeMailtoPatterns() */ public function testSafeMailto($expected = '', $email, $title = '', $attributes = '') { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/'; - - $config = new App(); - $config->baseURL = 'http://example.com'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); Services::injectMock('request', $request); @@ -1202,23 +1103,35 @@ public function testMbUrlTitleExtraDashes() } //-------------------------------------------------------------------- - // Exploratory testing, investigating https://github.com/codeigniter4/CodeIgniter4/issues/2016 public function testBasedNoIndex() { $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - $config = new App(); - $config->baseURL = 'http://example.com/ci/v4/'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/ci/v4/x/y'); + $this->config->baseURL = 'http://example.com/ci/v4/'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/ci/v4/x/y'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $config)); + $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $this->config)); + $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); + } + + public function testBasedNoTrailingSlash() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; + + $this->config->baseURL = 'http://example.com/ci/v4'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/ci/v4/x/y'); + + Services::injectMock('request', $request); + + $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $this->config)); + $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); } public function testBasedWithIndex() @@ -1226,16 +1139,14 @@ public function testBasedWithIndex() $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/ci/v4/index.php/x/y'; - $config = new App(); - $config->baseURL = 'http://example.com/ci/v4/'; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/ci/v4/index.php/x/y'); + $this->config->baseURL = 'http://example.com/ci/v4/'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/ci/v4/index.php/x/y'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $config)); + $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $this->config)); + $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); } public function testBasedWithoutIndex() @@ -1243,16 +1154,15 @@ public function testBasedWithoutIndex() $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - $config = new App(); - $config->baseURL = 'http://example.com/ci/v4/'; - $config->indexPage = ''; - $request = Services::request($config); - $request->uri = new URI('http://example.com/ci/v4/x/y'); + $this->config->baseURL = 'http://example.com/ci/v4/'; + $this->config->indexPage = ''; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/ci/v4/x/y'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/ci/v4/controller/method', site_url('controller/method', null, $config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $config)); + $this->assertEquals('http://example.com/ci/v4/controller/method', site_url('controller/method', null, $this->config)); + $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); } public function testBasedWithOtherIndex() @@ -1260,16 +1170,15 @@ public function testBasedWithOtherIndex() $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - $config = new App(); - $config->baseURL = 'http://example.com/ci/v4/'; - $config->indexPage = 'fc.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/ci/v4/x/y'); + $this->config->baseURL = 'http://example.com/ci/v4/'; + $this->config->indexPage = 'fc.php'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/ci/v4/x/y'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/ci/v4/fc.php/controller/method', site_url('controller/method', null, $config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $config)); + $this->assertEquals('http://example.com/ci/v4/fc.php/controller/method', site_url('controller/method', null, $this->config)); + $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); } /** @@ -1383,9 +1292,38 @@ public function testUrlIs(string $currentPath, string $testPath, bool $expected) { $_SERVER['HTTP_HOST'] = 'example.com'; - $url = new URI('http://example.com/' . $currentPath); - $request = service('request'); - $request->uri = $url; + $request = Services::request(); + $request->uri = new URI('http://example.com/' . $currentPath); + Services::injectMock('request', $request); + + $this->assertEquals($expected, url_is($testPath)); + } + + /** + * @dataProvider urlIsProvider + */ + public function testUrlIsNoIndex(string $currentPath, string $testPath, bool $expected) + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $this->config->indexPage = ''; + + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/' . $currentPath); + Services::injectMock('request', $request); + + $this->assertEquals($expected, url_is($testPath)); + } + + /** + * @dataProvider urlIsProvider + */ + public function testUrlIsWithSubfolder(string $currentPath, string $testPath, bool $expected) + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $this->config->baseUrl = 'http://example.com/foobar/'; + + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/foobar/' . $currentPath); Services::injectMock('request', $request); $this->assertEquals($expected, url_is($testPath)); From 32313eee7247994ff7fd1f4c8a9b62096fdacc70 Mon Sep 17 00:00:00 2001 From: MGatner Date: Fri, 25 Sep 2020 16:17:07 +0000 Subject: [PATCH 160/328] Add uri_string relative, implement for url_is --- system/Helpers/url_helper.php | 21 ++++- tests/system/Helpers/URLHelperTest.php | 98 ++++++++++++++++++-- user_guide_src/source/helpers/url_helper.rst | 16 +++- 3 files changed, 122 insertions(+), 13 deletions(-) diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index d626c0215678..7ad2ad84a7a4 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -205,11 +205,26 @@ function previous_url(bool $returnObject = false) * * Returns the path part of the current URL * + * @param boolean $relative Whether the resulting path should be relative to baseURL + * * @return string */ - function uri_string(): string + function uri_string(bool $relative = false): string { - return \Config\Services::request()->uri->getPath(); + $request = \Config\Services::request(); + $uri = $request->uri; + + // An absolute path is equivalent to getPath() + if (! $relative) + { + return $uri->getPath(); + } + + // Remove the baseURL from the entire URL + $url = (string) $uri->__toString(); + $baseURL = rtrim($request->config->baseURL, '/ ') . '/'; + + return substr($url, strlen($baseURL)); } } @@ -675,7 +690,7 @@ function url_is(string $path): bool { // Setup our regex to allow wildcards $path = '/' . trim(str_replace('*', '(\S)*', $path), '/ '); - $currentPath = '/' . trim(service('request')->uri->getPath(), '/ '); + $currentPath = '/' . trim(uri_string(true), '/ '); return (bool)preg_match("|^{$path}$|", $currentPath, $matches); } diff --git a/tests/system/Helpers/URLHelperTest.php b/tests/system/Helpers/URLHelperTest.php index fd188920f2cb..742d74817286 100644 --- a/tests/system/Helpers/URLHelperTest.php +++ b/tests/system/Helpers/URLHelperTest.php @@ -452,18 +452,21 @@ public function testPreviousURLUsesRefererIfNeeded() //-------------------------------------------------------------------- // Test uri_string - public function testUriString() + public function testUriStringAbsolute() { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/assets/image.jpg'; + $request = Services::request($this->config); - $request->uri = new URI('http://example.com/'); + $request->uri = new URI('http://example.com/assets/image.jpg'); Services::injectMock('request', $request); $url = current_url(); - $this->assertEquals('/', uri_string()); + $this->assertEquals('/assets/image.jpg', uri_string()); } - public function testUriStringExample() + public function testUriStringRelative() { $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/assets/image.jpg'; @@ -473,10 +476,93 @@ public function testUriStringExample() Services::injectMock('request', $request); + $url = current_url(); + $this->assertEquals('assets/image.jpg', uri_string(true)); + } + + public function testUriStringNoTrailingSlashAbsolute() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/assets/image.jpg'; + + $this->config->baseURL = 'http://example.com'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/assets/image.jpg'); + + Services::injectMock('request', $request); + $url = current_url(); $this->assertEquals('/assets/image.jpg', uri_string()); } + public function testUriStringNoTrailingSlashRelative() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/assets/image.jpg'; + + $this->config->baseURL = 'http://example.com'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/assets/image.jpg'); + + Services::injectMock('request', $request); + + $url = current_url(); + $this->assertEquals('assets/image.jpg', uri_string(true)); + } + + public function testUriStringEmptyAbsolute() + { + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); + + Services::injectMock('request', $request); + + $url = current_url(); + $this->assertEquals('/', uri_string()); + } + + public function testUriStringEmptyRelative() + { + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/'); + + Services::injectMock('request', $request); + + $url = current_url(); + $this->assertEquals('', uri_string(true)); + } + + public function testUriStringSubfolderAbsolute() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/subfolder/assets/image.jpg'; + + $this->config->baseURL = 'http://example.com/subfolder/'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/subfolder/assets/image.jpg'); + + Services::injectMock('request', $request); + + $url = current_url(); + $this->assertEquals('/subfolder/assets/image.jpg', uri_string()); + } + + public function testUriStringSubfolderRelative() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/assets/image.jpg'; + $_SERVER['REQUEST_URI'] = '/subfolder/assets/image.jpg'; + + $this->config->baseURL = 'http://example.com/subfolder/'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/subfolder/assets/image.jpg'); + + Services::injectMock('request', $request); + + $url = current_url(); + $this->assertEquals('assets/image.jpg', uri_string(true)); + } + //-------------------------------------------------------------------- // Test index_page @@ -1320,10 +1406,10 @@ public function testUrlIsNoIndex(string $currentPath, string $testPath, bool $ex public function testUrlIsWithSubfolder(string $currentPath, string $testPath, bool $expected) { $_SERVER['HTTP_HOST'] = 'example.com'; - $this->config->baseUrl = 'http://example.com/foobar/'; + $this->config->baseURL = 'http://example.com/subfolder/'; $request = Services::request($this->config); - $request->uri = new URI('http://example.com/foobar/' . $currentPath); + $request->uri = new URI('http://example.com/subfolder/' . $currentPath); Services::injectMock('request', $request); $this->assertEquals($expected, url_is($testPath)); diff --git a/user_guide_src/source/helpers/url_helper.rst b/user_guide_src/source/helpers/url_helper.rst index 3115d66bb1cb..805f49f173ab 100644 --- a/user_guide_src/source/helpers/url_helper.rst +++ b/user_guide_src/source/helpers/url_helper.rst @@ -112,19 +112,27 @@ The following functions are available: use a known and trusted source. If the session hasn't been loaded, or is otherwise unavailable, then a sanitized version of HTTP_REFERER will be used. -.. php:function:: uri_string() +.. php:function:: uri_string([$relative = false]) - :returns: An URI string + :param boolean $relative: True if you would like the string relative to baseURL + :returns: A URI string :rtype: string - Returns the path part relative to **baseUrl**. + Returns the path part of the current URL. For example, if your URL was this:: http://some-site.com/blog/comments/123 The function would return:: - blog/comments/123 + /blog/comments/123 + + Or with the optional relative parameter:: + + app.baseURL = http://some-site.com/subfolder/ + + uri_string(); // "/subfolder/blog/comments/123" + uri_string(true); // "blog/comments/123" .. php:function:: index_page([$altConfig = NULL]) From 63e8a433dd4702d89f3855ee5623e1b922a31df8 Mon Sep 17 00:00:00 2001 From: MGatner Date: Fri, 25 Sep 2020 16:29:21 +0000 Subject: [PATCH 161/328] Tweak tests --- tests/system/CodeIgniterTest.php | 2 +- tests/system/CommonFunctionsTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 7063e863dddd..0f3e167cc100 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -281,7 +281,7 @@ public function testRunForceSecure() $codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); - $this->assertEquals('https://example.com', $response->getHeader('Location')->getValue()); + $this->assertEquals('https://example.com/', $response->getHeader('Location')->getValue()); } public function testRunRedirectionWithNamed() diff --git a/tests/system/CommonFunctionsTest.php b/tests/system/CommonFunctionsTest.php index 5cdcf464fb3a..a65a16aa30a8 100644 --- a/tests/system/CommonFunctionsTest.php +++ b/tests/system/CommonFunctionsTest.php @@ -457,7 +457,7 @@ public function testForceHttpsNullRequestAndResponse() force_https(); - $this->assertEquals('https://example.com', Services::response()->getHeader('Location')->getValue()); + $this->assertEquals('https://example.com/', Services::response()->getHeader('Location')->getValue()); } //-------------------------------------------------------------------- From c17e0001c6fcb981b660e9ec328f96159ff2e4c1 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 26 Sep 2020 19:52:33 +0800 Subject: [PATCH 162/328] Update cache commands --- system/Commands/Cache/ClearCache.php | 54 +++++++++++++++++++++-- system/Commands/Cache/InfoCache.php | 55 +++++++++++++++++++++--- tests/system/Commands/ClearCacheTest.php | 14 +----- tests/system/Commands/InfoCacheTest.php | 19 ++++++-- 4 files changed, 115 insertions(+), 27 deletions(-) diff --git a/system/Commands/Cache/ClearCache.php b/system/Commands/Cache/ClearCache.php index f97af0ff5a84..8fa1edd3221d 100644 --- a/system/Commands/Cache/ClearCache.php +++ b/system/Commands/Cache/ClearCache.php @@ -1,9 +1,51 @@ -handler; + if (! array_key_exists($handler, $config->validHandlers)) { CLI::error($handler . ' is not a valid cache handler.'); + return; } @@ -64,10 +107,13 @@ public function run(array $params) if (! $cache->clean()) { + // @codeCoverageIgnoreStart CLI::error('Error while clearing the cache.'); + return; + // @codeCoverageIgnoreEnd } - CLI::write(CLI::color('Done', 'green')); + CLI::write(CLI::color('Cache cleared.', 'green')); } } diff --git a/system/Commands/Cache/InfoCache.php b/system/Commands/Cache/InfoCache.php index 30218673791d..1bde168cf6ce 100644 --- a/system/Commands/Cache/InfoCache.php +++ b/system/Commands/Cache/InfoCache.php @@ -1,10 +1,52 @@ -handler !== 'file') { CLI::error('This command only supports the file cache handler.'); + return; } - $cache = CacheFactory::getHandler($config); - + $cache = CacheFactory::getHandler($config); $caches = $cache->getCacheInfo(); - - $tbody = []; + $tbody = []; foreach ($caches as $key => $field) { $tbody[] = [ $key, - $field['server_path'], + clean_path($field['server_path']), number_to_size($field['size']), Time::createFromTimestamp($field['date']), ]; diff --git a/tests/system/Commands/ClearCacheTest.php b/tests/system/Commands/ClearCacheTest.php index aea8d0a0cf8b..0c34eb45acd6 100644 --- a/tests/system/Commands/ClearCacheTest.php +++ b/tests/system/Commands/ClearCacheTest.php @@ -8,7 +8,6 @@ class ClearCacheTest extends CIUnitTestCase { protected $streamFilter; - protected $result; protected function setUp(): void { @@ -24,33 +23,24 @@ protected function setUp(): void public function tearDown(): void { - if (! $this->result) - { - return; - } - stream_filter_remove($this->streamFilter); } public function testClearCacheInvalidHandler() { command('cache:clear junk'); - $result = CITestStreamFilter::$buffer; - $this->assertStringContainsString('junk is not a valid cache handler.', $result); + $this->assertStringContainsString('junk is not a valid cache handler.', CITestStreamFilter::$buffer); } public function testClearCacheWorks() { cache()->save('foo', 'bar'); - $this->assertEquals('bar', cache('foo')); command('cache:clear'); - $result = CITestStreamFilter::$buffer; $this->assertNull(cache('foo')); - - $this->assertStringContainsString('Done', $result); + $this->assertStringContainsString('Cache cleared.', CITestStreamFilter::$buffer); } } diff --git a/tests/system/Commands/InfoCacheTest.php b/tests/system/Commands/InfoCacheTest.php index 353d4345f437..26c7b1373d2c 100644 --- a/tests/system/Commands/InfoCacheTest.php +++ b/tests/system/Commands/InfoCacheTest.php @@ -1,4 +1,6 @@ -streamFilter); + + // restore default cache handler + config('Cache')->handler = 'file'; } protected function getBuffer() @@ -32,10 +36,18 @@ protected function getBuffer() return CITestStreamFilter::$buffer; } - public function testInfoCacheCanSeeFoo() + public function testInfoCacheErrorsOnInvalidHandler() { + config('Cache')->handler = 'redis'; cache()->save('foo', 'bar'); + command('cache:info'); + + $this->assertStringContainsString('This command only supports the file cache handler.', $this->getBuffer()); + } + public function testInfoCacheCanSeeFoo() + { + cache()->save('foo', 'bar'); command('cache:info'); $this->assertStringContainsString('foo', $this->getBuffer()); @@ -54,7 +66,6 @@ public function testInfoCacheCanSeeTable() public function testInfoCacheCannotSeeFoo() { cache()->delete('foo'); - command('cache:info'); $this->assertStringNotContainsString ('foo', $this->getBuffer()); From bfa66b1455596f50b45775812483a711910e8a1d Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 21 Sep 2020 18:42:01 +0800 Subject: [PATCH 163/328] CS and Docblock fixes --- system/Encryption/EncrypterInterface.php | 13 ++-- system/Encryption/Encryption.php | 60 ++++++++++++------- .../Exceptions/EncryptionException.php | 44 +++++++++++++- system/Encryption/Handlers/BaseHandler.php | 26 ++++---- system/Encryption/Handlers/OpenSSLHandler.php | 41 +++---------- tests/system/Encryption/EncryptionTest.php | 46 +++++++------- .../{ => Handlers}/OpenSSLHandlerTest.php | 48 +++++++++------ 7 files changed, 160 insertions(+), 118 deletions(-) rename tests/system/Encryption/{ => Handlers}/OpenSSLHandlerTest.php (74%) diff --git a/system/Encryption/EncrypterInterface.php b/system/Encryption/EncrypterInterface.php index 8da118a3cb99..d3b9432f2357 100644 --- a/system/Encryption/EncrypterInterface.php +++ b/system/Encryption/EncrypterInterface.php @@ -46,12 +46,13 @@ */ interface EncrypterInterface { - /** * Encrypt - convert plaintext into ciphertext * - * @param string $data Input data - * @param array $params Over-ridden parameters, specifically the key + * @param string $data Input data + * @param array|string|null $params Overridden parameters, specifically the key + * + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException * * @return string */ @@ -60,8 +61,10 @@ public function encrypt($data, $params = null); /** * Decrypt - convert ciphertext into plaintext * - * @param string $data Encrypted data - * @param array $params Over-ridden parameters, specifically the key + * @param string $data Encrypted data + * @param array|string|null $params Overridden parameters, specifically the key + * + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException * * @return string */ diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index aa31e5f95c55..7109c5171788 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -1,4 +1,5 @@ + */ + protected $handlers = []; /** * Class constructor * - * @param EncryptionConfig $config Configuration parameters - * @return void + * @param \Config\Encryption $config Configuration parameters * * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException + * + * @return void */ public function __construct(EncryptionConfig $config = null) { - $config = $config ?? new \Config\Encryption(); + $config = $config ?? config('Encryption'); $this->key = $config->key; $this->driver = $config->driver; $this->digest = $config->digest ?? 'SHA512'; - // if any aren't there, bomb - if ($this->driver === 'OpenSSL' && ! extension_loaded('openssl')) + // Map what we have installed + $this->handlers = [ + 'OpenSSL' => extension_loaded('openssl'), + 'Sodium' => extension_loaded('sodium'), + ]; + + // If requested driver is not active, bail + if (! in_array($this->driver, $this->drivers, true) || (array_key_exists($this->driver, $this->handlers) && ! $this->handlers[$this->driver])) { // this should never happen in travis-ci - // @codeCoverageIgnoreStart throw EncryptionException::forNoHandlerAvailable($this->driver); - // @codeCoverageIgnoreEnd } } /** * Initialize or re-initialize an encrypter * - * @param EncryptionConfig $config Configuration parameters - * @return \CodeIgniter\Encryption\EncrypterInterface + * @param \Config\Encryption $config Configuration parameters * * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException + * + * @return \CodeIgniter\Encryption\EncrypterInterface */ public function initialize(EncryptionConfig $config = null) { @@ -155,15 +175,15 @@ public function initialize(EncryptionConfig $config = null) $handlerName = 'CodeIgniter\\Encryption\\Handlers\\' . $this->driver . 'Handler'; $this->encrypter = new $handlerName($config); + return $this->encrypter; } - // -------------------------------------------------------------------- - /** * Create a random key * - * @param integer $length Output length + * @param integer $length Output length + * * @return string */ public static function createKey($length = 32) @@ -171,17 +191,16 @@ public static function createKey($length = 32) return random_bytes($length); } - // -------------------------------------------------------------------- - /** * __get() magic, providing readonly access to some of our protected properties * - * @param string $key Property name + * @param string $key Property name + * * @return mixed */ public function __get($key) { - if (in_array($key, ['key', 'digest', 'driver', 'drivers'], true)) + if ($this->__isset($key)) { return $this->{$key}; } @@ -199,5 +218,4 @@ public function __isset($key): bool { return in_array($key, ['key', 'digest', 'driver', 'drivers'], true); } - } diff --git a/system/Encryption/Exceptions/EncryptionException.php b/system/Encryption/Exceptions/EncryptionException.php index 7a95ccda9b54..c94a37fd960a 100644 --- a/system/Encryption/Exceptions/EncryptionException.php +++ b/system/Encryption/Exceptions/EncryptionException.php @@ -1,14 +1,52 @@ $value) // @phpstan-ignore-line + foreach ($config as $key => $value) { - $this->$pkey = $value; + if (property_exists($this, $key)) + { + $this->{$key} = $value; + } } } /** * Byte-safe substr() * - * @param string $str - * @param integer $start - * @param integer $length + * @param string $str + * @param integer $start + * @param integer $length + * * @return string */ protected static function substr($str, $start, $length = null) @@ -93,7 +95,7 @@ protected static function substr($str, $start, $length = null) */ public function __get($key) { - if (in_array($key, ['cipher', 'key'], true)) + if ($this->__isset($key)) { return $this->{$key}; } diff --git a/system/Encryption/Handlers/OpenSSLHandler.php b/system/Encryption/Handlers/OpenSSLHandler.php index 2a40f765a3ad..de65e616a3f9 100644 --- a/system/Encryption/Handlers/OpenSSLHandler.php +++ b/system/Encryption/Handlers/OpenSSLHandler.php @@ -1,4 +1,5 @@ digest, $data, $secret, true); + if (! hash_equals($hmacKey, $hmacCalc)) { throw EncryptionException::forAuthenticationFailed(); @@ -182,5 +158,4 @@ public function decrypt($data, $params = null) return \openssl_decrypt($data, $this->cipher, $secret, OPENSSL_RAW_DATA, $iv); } - } diff --git a/tests/system/Encryption/EncryptionTest.php b/tests/system/Encryption/EncryptionTest.php index 680d5643726c..11cd9b4356ab 100644 --- a/tests/system/Encryption/EncryptionTest.php +++ b/tests/system/Encryption/EncryptionTest.php @@ -1,21 +1,24 @@ encryption = new \CodeIgniter\Encryption\Encryption(); + $this->encryption = new Encryption(); } - // -------------------------------------------------------------------- - /** * __construct test * @@ -27,15 +30,15 @@ public function testConstructor() $this->assertEmpty($this->encryption->key); // Try with an empty value - $config = new EncryptionConfig(); - $this->encrypt = new \CodeIgniter\Encryption\Encryption($config); - $this->assertEmpty($this->encrypt->key); + $config = new EncryptionConfig(); + $this->encryption = new Encryption($config); + $this->assertEmpty($this->encryption->key); // try a different key - $ikm = 'Secret stuff'; - $config->key = $ikm; - $this->encrypt = new \CodeIgniter\Encryption\Encryption($config); - $this->assertEquals($ikm, $this->encrypt->key); + $ikm = 'Secret stuff'; + $config->key = $ikm; + $this->encryption = new Encryption($config); + $this->assertEquals($ikm, $this->encryption->key); } /** @@ -50,7 +53,7 @@ public function testBadDriver() $config->driver = 'Bogus'; $config->key = 'anything'; - $encrypter = $this->encryption->initialize($config); + $this->encryption->initialize($config); } /** @@ -65,11 +68,9 @@ public function testMissingDriver() $config->driver = ''; $config->key = 'anything'; - $encrypter = $this->encryption->initialize($config); + $this->encryption->initialize($config); } - // -------------------------------------------------------------------- - public function testKeyCreation() { $this->assertNotEmpty($this->encryption->createKey()); @@ -77,8 +78,6 @@ public function testKeyCreation() $this->assertEquals(16, strlen($this->encryption->createKey(16))); } - // -------------------------------------------------------------------- - public function testServiceSuccess() { $config = new EncryptionConfig(); @@ -98,14 +97,14 @@ public function testServiceFailure() $config->driver = 'Kazoo'; $config->key = 'anything'; - $encrypter = Services::encrypter($config); + Services::encrypter($config); } public function testServiceWithoutKey() { $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); - $encrypter = Services::encrypter(); + Services::encrypter(); } public function testServiceShared() @@ -121,8 +120,6 @@ public function testServiceShared() $this->assertEquals('anything', $encrypter->key); } - //-------------------------------------------------------------------- - public function testMagicIssetTrue() { $this->assertTrue(isset($this->encryption->digest)); @@ -142,5 +139,4 @@ public function testMagicGetMissing() { $this->assertNull($this->encryption->bogus); } - } diff --git a/tests/system/Encryption/OpenSSLHandlerTest.php b/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php similarity index 74% rename from tests/system/Encryption/OpenSSLHandlerTest.php rename to tests/system/Encryption/Handlers/OpenSSLHandlerTest.php index f5da9e4e24f5..285c6c4ce836 100644 --- a/tests/system/Encryption/OpenSSLHandlerTest.php +++ b/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php @@ -1,33 +1,44 @@ encryption = new \CodeIgniter\Encryption\Encryption(); - } + if (! extension_loaded('openssl')) + { + $this->markTestSkipped('OpenSSL is not available.'); + } - // -------------------------------------------------------------------- + $this->encryption = new Encryption(); + } /** * Sanity test */ public function testSanity() { - $params = new \Config\Encryption(); + $params = new EncryptionConfig(); $params->driver = 'OpenSSL'; $params->key = 'Something other than an empty string'; - $this->encrypter = $this->encryption->initialize($params); + $encrypter = $this->encryption->initialize($params); - $this->assertEquals('AES-256-CTR', $this->encrypter->cipher); - $this->assertEquals('Something other than an empty string', $this->encrypter->key); + $this->assertEquals('AES-256-CTR', $encrypter->cipher); + $this->assertEquals('Something other than an empty string', $encrypter->key); } - // -------------------------------------------------------------------- - /** * initialize(), encrypt(), decrypt() test * @@ -36,7 +47,7 @@ public function testSanity() */ public function testSimple() { - $params = new \Config\Encryption(); + $params = new EncryptionConfig(); $params->driver = 'OpenSSL'; $params->key = '\xd0\xc9\x08\xc4\xde\x52\x12\x6e\xf8\xcc\xdb\x03\xea\xa0\x3a\x5c'; // Default state (AES-256/Rijndael-256 in CTR mode) @@ -60,7 +71,7 @@ public function testWithoutKey() { $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); - $encrypter = new \CodeIgniter\Encryption\Handlers\OpenSSLHandler(); + $encrypter = new OpenSSLHandler(); $message1 = 'This is a plain-text message.'; $encrypter->encrypt($message1); } @@ -68,7 +79,7 @@ public function testWithoutKey() public function testWithKeyString() { $key = 'abracadabra'; - $encrypter = new \CodeIgniter\Encryption\Handlers\OpenSSLHandler(); + $encrypter = new OpenSSLHandler(); $message1 = 'This is a plain-text message.'; $encoded = $encrypter->encrypt($message1, $key); $this->assertEquals($message1, $encrypter->decrypt($encoded, $key)); @@ -82,7 +93,7 @@ public function testWithWrongKeyString() $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); $key1 = 'abracadabra'; - $encrypter = new \CodeIgniter\Encryption\Handlers\OpenSSLHandler(); + $encrypter = new OpenSSLHandler(); $message1 = 'This is a plain-text message.'; $encoded = $encrypter->encrypt($message1, $key1); $this->assertNotEquals($message1, $encoded); @@ -93,7 +104,7 @@ public function testWithWrongKeyString() public function testWithKeyArray() { $key = 'abracadabra'; - $encrypter = new \CodeIgniter\Encryption\Handlers\OpenSSLHandler(); + $encrypter = new OpenSSLHandler(); $message1 = 'This is a plain-text message.'; $encoded = $encrypter->encrypt($message1, ['key' => $key]); $this->assertEquals($message1, $encrypter->decrypt($encoded, ['key' => $key])); @@ -107,12 +118,11 @@ public function testWithWrongKeyArray() $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); $key1 = 'abracadabra'; - $encrypter = new \CodeIgniter\Encryption\Handlers\OpenSSLHandler(); + $encrypter = new OpenSSLHandler(); $message1 = 'This is a plain-text message.'; $encoded = $encrypter->encrypt($message1, ['key' => $key1]); $this->assertNotEquals($message1, $encoded); $key2 = 'Holy cow, batman!'; $this->assertNotEquals($message1, $encrypter->decrypt($encoded, ['key' => $key2])); } - } From a51f969bb9c3ac4fafc201253adef4066c32a839 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 21 Sep 2020 19:51:21 +0800 Subject: [PATCH 164/328] Add SodiumHandler encrypter --- app/Config/Encryption.php | 14 +- env | 1 + system/Encryption/Handlers/BaseHandler.php | 4 +- system/Encryption/Handlers/SodiumHandler.php | 168 ++++++++++++++++++ .../Encryption/Handlers/SodiumHandlerTest.php | 95 ++++++++++ 5 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 system/Encryption/Handlers/SodiumHandler.php create mode 100644 tests/system/Encryption/Handlers/SodiumHandlerTest.php diff --git a/app/Config/Encryption.php b/app/Config/Encryption.php index ab5940a6672a..f9d69a845d85 100644 --- a/app/Config/Encryption.php +++ b/app/Config/Encryption.php @@ -31,12 +31,24 @@ class Encryption extends BaseConfig * -------------------------------------------------------------------------- * * One of the supported drivers, e.g. 'OpenSSL' or 'Sodium'. - * The default driver, if you don't specify one, is 'OpenSSL'. * * @var string */ public $driver = 'OpenSSL'; + /** + * -------------------------------------------------------------------------- + * SodiumHandler's Padding Size + * -------------------------------------------------------------------------- + * + * This is the number of bytes that will be padded to the plaintext message + * before it is encrypted. Maximum allowed value is 512 bytes. If none is + * given, it will default to 512. + * + * @var integer + */ + public $blockSize = 512; + /** * -------------------------------------------------------------------------- * Encryption digest diff --git a/env b/env index 2e3ca032715b..891bc89b124c 100644 --- a/env +++ b/env @@ -91,6 +91,7 @@ # encryption.key = # encryption.driver = OpenSSL +# encryption.blockSize = 512 # encryption.digest = SHA512 #-------------------------------------------------------------------- diff --git a/system/Encryption/Handlers/BaseHandler.php b/system/Encryption/Handlers/BaseHandler.php index cd9c68e14548..2067d610afca 100644 --- a/system/Encryption/Handlers/BaseHandler.php +++ b/system/Encryption/Handlers/BaseHandler.php @@ -64,7 +64,7 @@ public function __construct(Encryption $config = null) $config = $config ?? config('Encryption'); // make the parameters conveniently accessible - foreach ($config as $key => $value) + foreach (get_object_vars($config) as $key => $value) { if (property_exists($this, $key)) { @@ -111,6 +111,6 @@ public function __get($key) */ public function __isset($key): bool { - return in_array($key, ['cipher', 'key'], true); + return property_exists($this, $key); } } diff --git a/system/Encryption/Handlers/SodiumHandler.php b/system/Encryption/Handlers/SodiumHandler.php new file mode 100644 index 000000000000..008490664023 --- /dev/null +++ b/system/Encryption/Handlers/SodiumHandler.php @@ -0,0 +1,168 @@ +parseParams($params); + + if (empty($this->key)) + { + throw EncryptionException::forNeedsStarterKey(); + } + + // create a nonce for this operation + $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes + + // pad to a maximum of 512 bytes chunks + $paddedLength = $this->blockSize <= 512 ? $this->blockSize : 512; + $paddedMessage = sodium_pad($data, $paddedLength); + + // encrypt message and combine with nonce + $ciphertext = $nonce . sodium_crypto_secretbox($paddedMessage, $nonce, $this->key); + + // cleanup buffers + sodium_memzero($data); + sodium_memzero($this->key); + + return $ciphertext; + } + + /** + * {@inheritDoc} + */ + public function decrypt($data, $params = null) + { + $this->parseParams($params); + + if (empty($this->key)) + { + throw EncryptionException::forNeedsStarterKey(); + } + + if (mb_strlen($data, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) + { + // message was truncated + throw EncryptionException::forAuthenticationFailed(); + } + + // Extract info from encrypted data + $nonce = self::substr($data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); + $ciphertext = self::substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null); + + // decrypt and account for extra padding + $paddedLength = $this->blockSize <= 512 ? $this->blockSize : 512; + $paddedPlaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key); + + if ($paddedPlaintext === false) + { + // message was tampered in transit + throw EncryptionException::forAuthenticationFailed(); // @codeCoverageIgnore + } + + $plaintext = sodium_unpad($paddedPlaintext, $paddedLength); + + // cleanup buffers + sodium_memzero($ciphertext); + sodium_memzero($this->key); + + return $plaintext; + } + + /** + * Parse the $params before doing assignment. + * + * @param array|string|null $params + * + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException If key is empty + * + * @return void + */ + protected function parseParams($params) + { + if ($params === null) + { + return; + } + + if (is_array($params)) + { + if (isset($params['key'])) + { + $this->key = $params['key']; + } + + if (isset($params['block_size'])) + { + $this->blockSize = $params['block_size']; + } + + return; + } + + $this->key = (string) $params; + } +} diff --git a/tests/system/Encryption/Handlers/SodiumHandlerTest.php b/tests/system/Encryption/Handlers/SodiumHandlerTest.php new file mode 100644 index 000000000000..23a867dac3b3 --- /dev/null +++ b/tests/system/Encryption/Handlers/SodiumHandlerTest.php @@ -0,0 +1,95 @@ +markTestSkipped('Libsodium is not available.'); + } + + parent::setUp(); + + $this->config = new EncryptionConfig(); + $this->config->driver = 'Sodium'; + $this->config->key = sodium_crypto_secretbox_keygen(); + $this->encryption = new Encryption($this->config); + } + + public function testPropertiesGetter() + { + $this->config->key = sodium_crypto_secretbox_keygen(); + $this->config->blockSize = 256; + $encrypter = $this->encryption->initialize($this->config); + + $this->assertSame($this->config->key, $encrypter->key); + $this->assertSame($this->config->blockSize, $encrypter->blockSize); + $this->assertNull($encrypter->driver); + } + + public function testEmptyKeyThrowsErrorOnInitialize() + { + $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); + + $this->config->key = ''; + $this->encryption->initialize($this->config); + } + + public function testEmptyKeyThrowsErrorOnEncrypt() + { + $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); + + $encrypter = $this->encryption->initialize($this->config); + $encrypter->encrypt('Some message to encrypt', ''); + } + + public function testEmptyKeyThrowsErrorOnDecrypt() + { + $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); + + $encrypter = $this->encryption->initialize($this->config); + $ciphertext = $encrypter->encrypt('Some message to encrypt'); + // After encrypt, the message and key are wiped from buffer + $encrypter->decrypt($ciphertext); + } + + public function testTruncatedMessageThrowsErrorOnDecrypt() + { + $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); + + $encrypter = $this->encryption->initialize($this->config); + $ciphertext = $encrypter->encrypt('Some message to encrypt'); + $truncated = mb_substr($ciphertext, 0, 24, '8bit'); + $encrypter->decrypt($truncated, ['block_size' => 256, 'key' => sodium_crypto_secretbox_keygen()]); + } + + public function testDecryptingMessages() + { + $key = sodium_crypto_secretbox_keygen(); + $msg = 'A plaintext message for you.'; + + $this->config->key = $key; + $encrypter = $this->encryption->initialize($this->config); + $ciphertext = $encrypter->encrypt($msg); + + $this->assertSame($msg, $encrypter->decrypt($ciphertext, $key)); + $this->assertNotSame('A plain-text message for you.', $encrypter->decrypt($ciphertext, $key)); + } +} From e79f7573eef4a099d32b8a7868b652992a97df82 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 21 Sep 2020 20:28:23 +0800 Subject: [PATCH 165/328] Update encryption docs for Sodium --- .../source/libraries/encryption.rst | 97 +++++++++++-------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/user_guide_src/source/libraries/encryption.rst b/user_guide_src/source/libraries/encryption.rst index 91e7c76c5b9b..3cbbf76acb5f 100644 --- a/user_guide_src/source/libraries/encryption.rst +++ b/user_guide_src/source/libraries/encryption.rst @@ -13,12 +13,13 @@ encryption **handler** to suit your parameters as explained below. Encryption Service handlers must implement CodeIgniter's simple ``EncrypterInterface``. Using an appropriate PHP cryptographic extension or third-party library may require -additional software is installed on your server and/or might need to be explicitly +additional software to be installed on your server and/or might need to be explicitly enabled in your instance of PHP. The following PHP extensions are currently supported: - `OpenSSL `_ +- `Sodium `_ This is not a full cryptographic solution. If you need more capabilities, for example, public-key encryption, we suggest you consider direct use of OpenSSL or @@ -67,19 +68,18 @@ Configuring the Library The example above uses the configuration settings found in ``app/Config/Encryption.php``. -There are only two settings: - -======== =============================================== -Option Possible values (default in parentheses) -======== =============================================== -key Encryption key starter -driver Preferred handler (OpenSSL) -======== =============================================== +========== ==================================================== +Option Possible values (default in parentheses) +========== ==================================================== +key Encryption key starter +driver Preferred handler, e.g. OpenSSL or Sodium (``OpenSSL``) +blockSize Padding size in bytes for SodiumHandler (``512``) +digest Message digest algorithm (``SHA512``) +========== ==================================================== You can replace the config file's settings by passing a configuration object of your own to the ``Services`` call. The ``$config`` variable must be -an instance of either the ``Config\Encryption`` class or an object -that extends ``CodeIgniter\Config\BaseConfig``. +an instance of the ``Config\Encryption`` class. :: $config = new \Config\Encryption(); @@ -88,7 +88,6 @@ that extends ``CodeIgniter\Config\BaseConfig``. $encrypter = \Config\Services::encrypter($config); - Default Behavior ================ @@ -107,7 +106,14 @@ you can use the Encryption library's ``createKey()`` method. :: // $key will be assigned a 32-byte (256-bit) random key - $key = Encryption::createKey(32); + $key = Encryption::createKey(); + + // $key will be assigned a 24-byte random key + $key = Encryption::createKey(24); + + // for the SodiumHandler, you can use either: + $key = sodium_crypto_secretbox_keygen(); + $key = Encryption::createKey(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); The key can be stored in ``app/Config/Encryption.php``, or you can design a storage mechanism of your own and pass the key dynamically when encrypting/decrypting. @@ -122,7 +128,7 @@ Encoding Keys or Results You'll notice that the ``createKey()`` method outputs binary data, which is hard to deal with (i.e. a copy-paste may damage it), so you may use -``bin2hex()``, ``hex2bin()`` or Base64-encoding to work with the key in +``bin2hex()``, or ``base64_encode`` to work with the key in a more friendly manner. For example:: // Get a hex-encoded representation of the key: @@ -177,6 +183,21 @@ The *key* your configuration provides is used to derive two other keys, one for encryption and one for authentication. This is achieved by way of a technique known as an `HMAC-based Key Derivation Function `_ (HKDF). +Sodium Notes +------------ + +The `Sodium `_ extension is bundled by default in PHP as +of PHP 7.2.0. + +Sodium uses the algorithms XSalsa20 to encrypt, Poly1305 for MAC, and XS25519 for key exchange in +sending secret messages in an end-to-end scenario. To encrypt and/or authenticate a string using +a shared-key, such as symmetric encryption, Sodium uses the XSalsa20 algorithm to encrypt and +HMAC-SHA512 for the authentication. + +.. note:: CodeIgniter's ``SodiumHandler`` uses ``sodium_memzero`` in every encryption or decryption + session. After each session, the message (whether plaintext or ciphertext) and starter key are + wiped out from the buffers. You may need to provide again the key before starting a new session. + Message Length ============== @@ -203,9 +224,7 @@ you can create an "Encrypter" directly, or change the settings of an existing in // reconfigure an instance with different settings $encrypter = $encryption->initialize($config); -Remember, that ``$config`` must be an instance of either a ``Config\Encryption`` class -or an object that extends ``CodeIgniter\Config\BaseConfig``. - +Remember, that ``$config`` must be an instance of ``Config\Encryption`` class. *************** Class Reference @@ -215,20 +234,19 @@ Class Reference .. php:staticmethod:: createKey([$length = 32]) - :param int $length: Output length - :returns: A pseudo-random cryptographic key with the specified length, or ``false`` on failure + :param int $length: Output length + :returns: A pseudo-random cryptographic key with the specified length, or ``false`` on failure :rtype: string Creates a cryptographic key by fetching random data from the operating system's sources (*i.e.* ``/dev/urandom``). + .. php:method:: initialize([Encryption $config = null]) - .. php:method:: initialize([BaseConfig $config = null]) - - :param BaseConfig $config: Configuration parameters - :returns: CodeIgniter\\Encryption\\EncrypterInterface instance - :rtype: CodeIgniter\\Encryption\\EncrypterInterface - :throws: CodeIgniter\\Encryption\\Exceptions\\EncryptionException + :param Config\\Encryption $config: Configuration parameters + :returns: ``CodeIgniter\Encryption\EncrypterInterface`` instance + :rtype: ``CodeIgniter\Encryption\EncrypterInterface`` + :throws: ``CodeIgniter\Encryption\Exceptions\EncryptionException`` Initializes (configures) the library to use different settings. @@ -242,17 +260,17 @@ Class Reference .. php:method:: encrypt($data[, $params = null]) - :param string $data: Data to encrypt - :param $params: Configuration parameters (key) - :returns: Encrypted data or FALSE on failure + :param string $data: Data to encrypt + :param array|string|null $params: Configuration parameters (key) + :returns: Encrypted data :rtype: string - :throws: CodeIgniter\\Encryption\\Exceptions\\EncryptionException + :throws: ``CodeIgniter\Encryption\Exceptions\EncryptionException`` Encrypts the input data and returns its ciphertext. - If you pass parameters as the second argument, the ``key`` element - will be used as the starting key for this operation if ``$params`` - is an array; or the starting key may be passed as a string. + If you pass parameters as the second argument, the ``key`` element + will be used as the starting key for this operation if ``$params`` + is an array; or the starting key may be passed as a string. Examples:: @@ -262,18 +280,17 @@ Class Reference .. php:method:: decrypt($data[, $params = null]) - :param string $data: Data to decrypt - :param $params: Configuration parameters (key) - :returns: Decrypted data or FALSE on failure + :param string $data: Data to decrypt + :param array|string|null $params: Configuration parameters (key) + :returns: Decrypted data :rtype: string - :throws: CodeIgniter\\Encryption\\Exceptions\\EncryptionException + :throws: ``CodeIgniter\Encryption\Exceptions\EncryptionException`` Decrypts the input data and returns it in plain-text. - If you pass parameters as the second argument, the ``key`` element - will be used as the starting key for this operation if ``$params`` - is an array; or the starting key may be passed as a string. - + If you pass parameters as the second argument, the ``key`` element + will be used as the starting key for this operation if ``$params`` + is an array; or the starting key may be passed as a string. Examples:: From 3a1aaccd4f878296f0f7021db0186c78db882d6c Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 21 Sep 2020 22:26:50 +0800 Subject: [PATCH 166/328] Do not use shared encryption config --- system/Encryption/Encryption.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index 7109c5171788..1cd6ca9df36d 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -114,7 +114,7 @@ class Encryption */ public function __construct(EncryptionConfig $config = null) { - $config = $config ?? config('Encryption'); + $config = $config ?? new EncryptionConfig(); $this->key = $config->key; $this->driver = $config->driver; From 4e53d27bf8c968b20b9f904f87cf9da9b31e3440 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 21 Sep 2020 23:14:25 +0800 Subject: [PATCH 167/328] Make explicit the missing key in encrypt --- tests/system/Encryption/Handlers/OpenSSLHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php b/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php index 285c6c4ce836..a6b69c5462c0 100644 --- a/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php +++ b/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php @@ -73,7 +73,7 @@ public function testWithoutKey() $encrypter = new OpenSSLHandler(); $message1 = 'This is a plain-text message.'; - $encrypter->encrypt($message1); + $encrypter->encrypt($message1, ['key' => '']); } public function testWithKeyString() From 5172ff6209c2a3807e35612eba4a2a285477e6cc Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 22 Sep 2020 23:13:54 +0800 Subject: [PATCH 168/328] Conditionally install sodium --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 310972ae4121..551f48120074 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,6 +48,12 @@ before_install: before_script: - echo 'extension = memcached.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - | + if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.2" ]]; then + git clone -b stable https://github.com/jedisct1/libsodium.git + cd libsodium && sudo ./configure && sudo make check && sudo make install && cd .. + printf "\n" | pecl install -f libsodium + fi - composer install --prefer-source after_success: From 73e2722f86a101fc9f5fdbd3c2a6be9cc358495a Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 25 Sep 2020 16:56:44 +0800 Subject: [PATCH 169/328] Apply changes from code review --- app/Config/Encryption.php | 15 ++++-- env | 2 +- phpstan.neon.dist | 1 + system/Encryption/Encryption.php | 3 +- .../Exceptions/EncryptionException.php | 34 +++++++++++++ system/Encryption/Handlers/SodiumHandler.php | 35 +++++++++----- .../Encryption/Handlers/SodiumHandlerTest.php | 22 ++++++++- .../source/libraries/encryption.rst | 48 +++++++++++++++---- 8 files changed, 129 insertions(+), 31 deletions(-) diff --git a/app/Config/Encryption.php b/app/Config/Encryption.php index f9d69a845d85..39c6fd37dcb8 100644 --- a/app/Config/Encryption.php +++ b/app/Config/Encryption.php @@ -30,7 +30,11 @@ class Encryption extends BaseConfig * Encryption Driver to Use * -------------------------------------------------------------------------- * - * One of the supported drivers, e.g. 'OpenSSL' or 'Sodium'. + * One of the supported encryption drivers. + * + * Available drivers: + * - OpenSSL + * - Sodium * * @var string */ @@ -38,16 +42,17 @@ class Encryption extends BaseConfig /** * -------------------------------------------------------------------------- - * SodiumHandler's Padding Size + * SodiumHandler's Padding Length in Bytes * -------------------------------------------------------------------------- * * This is the number of bytes that will be padded to the plaintext message - * before it is encrypted. Maximum allowed value is 512 bytes. If none is - * given, it will default to 512. + * before it is encrypted. This value should be greater than zero. + * + * See the user guide for more information on padding. * * @var integer */ - public $blockSize = 512; + public $blockSize = 16; /** * -------------------------------------------------------------------------- diff --git a/env b/env index 891bc89b124c..cdab116294f9 100644 --- a/env +++ b/env @@ -91,7 +91,7 @@ # encryption.key = # encryption.driver = OpenSSL -# encryption.blockSize = 512 +# encryption.blockSize = 16 # encryption.digest = SHA512 #-------------------------------------------------------------------- diff --git a/phpstan.neon.dist b/phpstan.neon.dist index aee79b697641..83393500ee81 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -47,3 +47,4 @@ parameters: - APP_NAMESPACE - CI_DEBUG - ENVIRONMENT + - SODIUM_LIBRARY_VERSION diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index 1cd6ca9df36d..155128e031cc 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -123,7 +123,8 @@ public function __construct(EncryptionConfig $config = null) // Map what we have installed $this->handlers = [ 'OpenSSL' => extension_loaded('openssl'), - 'Sodium' => extension_loaded('sodium'), + // the SodiumHandler uses some API (like sodium_pad) that is available only on v1.0.14+ + 'Sodium' => extension_loaded('sodium') && version_compare(SODIUM_LIBRARY_VERSION, '1.0.14', '>='), ]; // If requested driver is not active, bail diff --git a/system/Encryption/Exceptions/EncryptionException.php b/system/Encryption/Exceptions/EncryptionException.php index c94a37fd960a..74259a896cb6 100644 --- a/system/Encryption/Exceptions/EncryptionException.php +++ b/system/Encryption/Exceptions/EncryptionException.php @@ -47,31 +47,65 @@ */ class EncryptionException extends RuntimeException implements ExceptionInterface { + /** + * Thrown when no driver is present in the active encryption session. + * + * @return static + */ public static function forNoDriverRequested() { return new static(lang('Encryption.noDriverRequested')); } + /** + * Thrown when the handler requested is not available. + * + * @param string $handler + * + * @return static + */ public static function forNoHandlerAvailable(string $handler) { return new static(lang('Encryption.noHandlerAvailable', [$handler])); } + /** + * Thrown when the handler requested is unknown. + * + * @param string $driver + * + * @return static + */ public static function forUnKnownHandler(string $driver = null) { return new static(lang('Encryption.unKnownHandler', [$driver])); } + /** + * Thrown when no starter key is provided for the current encryption session. + * + * @return static + */ public static function forNeedsStarterKey() { return new static(lang('Encryption.starterKeyNeeded')); } + /** + * Thrown during data decryption when a problem or error occurred. + * + * @return static + */ public static function forAuthenticationFailed() { return new static(lang('Encryption.authenticationFailed')); } + /** + * Thrown during data encryption when a problem or error occurred. + * + * @return static + */ public static function forEncryptionFailed() { return new static(lang('Encryption.encryptionFailed')); diff --git a/system/Encryption/Handlers/SodiumHandler.php b/system/Encryption/Handlers/SodiumHandler.php index 008490664023..109501cecb82 100644 --- a/system/Encryption/Handlers/SodiumHandler.php +++ b/system/Encryption/Handlers/SodiumHandler.php @@ -60,7 +60,7 @@ class SodiumHandler extends BaseHandler * * @var integer */ - protected $blockSize = 512; + protected $blockSize = 16; /** * {@inheritDoc} @@ -77,12 +77,16 @@ public function encrypt($data, $params = null) // create a nonce for this operation $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes - // pad to a maximum of 512 bytes chunks - $paddedLength = $this->blockSize <= 512 ? $this->blockSize : 512; - $paddedMessage = sodium_pad($data, $paddedLength); + // add padding before we encrypt the data + if ($this->blockSize <= 0) + { + throw EncryptionException::forEncryptionFailed(); + } + + $data = sodium_pad($data, $this->blockSize); // encrypt message and combine with nonce - $ciphertext = $nonce . sodium_crypto_secretbox($paddedMessage, $nonce, $this->key); + $ciphertext = $nonce . sodium_crypto_secretbox($data, $nonce, $this->key); // cleanup buffers sodium_memzero($data); @@ -113,23 +117,28 @@ public function decrypt($data, $params = null) $nonce = self::substr($data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $ciphertext = self::substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null); - // decrypt and account for extra padding - $paddedLength = $this->blockSize <= 512 ? $this->blockSize : 512; - $paddedPlaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key); + // decrypt data + $data = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key); - if ($paddedPlaintext === false) + if ($data === false) { // message was tampered in transit throw EncryptionException::forAuthenticationFailed(); // @codeCoverageIgnore } - $plaintext = sodium_unpad($paddedPlaintext, $paddedLength); + // remove extra padding during encryption + if ($this->blockSize <= 0) + { + throw EncryptionException::forAuthenticationFailed(); + } + + $data = sodium_unpad($data, $this->blockSize); // cleanup buffers sodium_memzero($ciphertext); sodium_memzero($this->key); - return $plaintext; + return $data; } /** @@ -155,9 +164,9 @@ protected function parseParams($params) $this->key = $params['key']; } - if (isset($params['block_size'])) + if (isset($params['blockSize'])) { - $this->blockSize = $params['block_size']; + $this->blockSize = $params['blockSize']; } return; diff --git a/tests/system/Encryption/Handlers/SodiumHandlerTest.php b/tests/system/Encryption/Handlers/SodiumHandlerTest.php index 23a867dac3b3..dd0ffdf30fc9 100644 --- a/tests/system/Encryption/Handlers/SodiumHandlerTest.php +++ b/tests/system/Encryption/Handlers/SodiumHandlerTest.php @@ -60,6 +60,15 @@ public function testEmptyKeyThrowsErrorOnEncrypt() $encrypter->encrypt('Some message to encrypt', ''); } + public function testInvalidBlockSizeThrowsErrorOnEncrypt() + { + $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); + $this->config->blockSize = -1; + + $encrypter = $this->encryption->initialize($this->config); + $encrypter->encrypt('Some message.'); + } + public function testEmptyKeyThrowsErrorOnDecrypt() { $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); @@ -70,6 +79,17 @@ public function testEmptyKeyThrowsErrorOnDecrypt() $encrypter->decrypt($ciphertext); } + public function testInvalidBlockSizeThrowsErrorOnDecrypt() + { + $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); + $key = $this->config->key; + + $encrypter = $this->encryption->initialize($this->config); + $ciphertext = $encrypter->encrypt('Some message.'); + // After encrypt, the message and key are wiped from buffer. + $encrypter->decrypt($ciphertext, ['key' => $key, 'blockSize' => 0]); + } + public function testTruncatedMessageThrowsErrorOnDecrypt() { $this->expectException('CodeIgniter\Encryption\Exceptions\EncryptionException'); @@ -77,7 +97,7 @@ public function testTruncatedMessageThrowsErrorOnDecrypt() $encrypter = $this->encryption->initialize($this->config); $ciphertext = $encrypter->encrypt('Some message to encrypt'); $truncated = mb_substr($ciphertext, 0, 24, '8bit'); - $encrypter->decrypt($truncated, ['block_size' => 256, 'key' => sodium_crypto_secretbox_keygen()]); + $encrypter->decrypt($truncated, ['blockSize' => 256, 'key' => sodium_crypto_secretbox_keygen()]); } public function testDecryptingMessages() diff --git a/user_guide_src/source/libraries/encryption.rst b/user_guide_src/source/libraries/encryption.rst index 3cbbf76acb5f..1ebb742ef2cc 100644 --- a/user_guide_src/source/libraries/encryption.rst +++ b/user_guide_src/source/libraries/encryption.rst @@ -73,7 +73,7 @@ Option Possible values (default in parentheses) ========== ==================================================== key Encryption key starter driver Preferred handler, e.g. OpenSSL or Sodium (``OpenSSL``) -blockSize Padding size in bytes for SodiumHandler (``512``) +blockSize Padding length in bytes for SodiumHandler (``16``) digest Message digest algorithm (``SHA512``) ========== ==================================================== @@ -108,9 +108,6 @@ you can use the Encryption library's ``createKey()`` method. // $key will be assigned a 32-byte (256-bit) random key $key = Encryption::createKey(); - // $key will be assigned a 24-byte random key - $key = Encryption::createKey(24); - // for the SodiumHandler, you can use either: $key = sodium_crypto_secretbox_keygen(); $key = Encryption::createKey(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); @@ -169,6 +166,27 @@ Similarly, you can use these prefixes in your ``.env`` file, too! // or encryption.key = base64: +Padding +======= + +Sometimes, the length of a message may provide a lot of information about its nature. If +a message is one of "yes", "no" and "maybe", encrypting the message doesn't help: knowing +the length is enough to know what the message is. + +Padding is a technique to mitigate this, by making the length a multiple of a given block size. + +Padding is implemented in ``SodiumHandler`` using libsodium's native ``sodium_pad`` and ``sodium_unpad`` +functions. This requires the use of a padding length (in bytes) that is added to the plaintext +message prior to encryption, and removed after decryption. Padding is configurable via the +``$blockSize`` property of ``Config\Encryption``. This value should be greater than zero. + +.. important:: You are advised not to devise your own padding implementation. You must always use + the more secure implementation of a library. Also, passwords should not be padded. Usage of + padding in order to hide the length of a password is not recommended. A client willing to send + a password to a server should hash it instead (even with a single iteration of the hash function). + This ensures that the length of the transmitted data is constant, and that the server doesn't + effortlessly get a copy of the password. + Encryption Handler Notes ======================== @@ -268,15 +286,20 @@ Class Reference Encrypts the input data and returns its ciphertext. - If you pass parameters as the second argument, the ``key`` element - will be used as the starting key for this operation if ``$params`` - is an array; or the starting key may be passed as a string. + If you pass parameters as the second argument, the ``key`` element + will be used as the starting key for this operation if ``$params`` + is an array; or the starting key may be passed as a string. + + If you are using the SodiumHandler and want to pass a different ``blockSize`` + on runtime, pass the ``blockSize`` key in the ``$params`` array. Examples:: $ciphertext = $encrypter->encrypt('My secret message'); $ciphertext = $encrypter->encrypt('My secret message', ['key' => 'New secret key']); + $ciphertext = $encrypter->encrypt('My secret message', ['key' => 'New secret key', 'blockSize' => 32]); $ciphertext = $encrypter->encrypt('My secret message', 'New secret key'); + $ciphertext = $encrypter->encrypt('My secret message', ['blockSize' => 32]); .. php:method:: decrypt($data[, $params = null]) @@ -288,12 +311,17 @@ Class Reference Decrypts the input data and returns it in plain-text. - If you pass parameters as the second argument, the ``key`` element - will be used as the starting key for this operation if ``$params`` - is an array; or the starting key may be passed as a string. + If you pass parameters as the second argument, the ``key`` element + will be used as the starting key for this operation if ``$params`` + is an array; or the starting key may be passed as a string. + + If you are using the SodiumHandler and want to pass a different ``blockSize`` + on runtime, pass the ``blockSize`` key in the ``$params`` array. Examples:: echo $encrypter->decrypt($ciphertext); echo $encrypter->decrypt($ciphertext, ['key' => 'New secret key']); + echo $encrypter->decrypt($ciphertext, ['key' => 'New secret key', 'blockSize' => 32]); echo $encrypter->decrypt($ciphertext, 'New secret key'); + echo $encrypter->decrypt($ciphertext, ['blockSize' => 32]); From 1843206fba72f89269d691781a416de6eee136f2 Mon Sep 17 00:00:00 2001 From: michalsn Date: Mon, 28 Sep 2020 17:42:14 +0200 Subject: [PATCH 170/328] Fix transparency handling for webp --- system/Images/Handlers/GDHandler.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/Images/Handlers/GDHandler.php b/system/Images/Handlers/GDHandler.php index de5278fbd6a6..2aa3cc967e9e 100644 --- a/system/Images/Handlers/GDHandler.php +++ b/system/Images/Handlers/GDHandler.php @@ -246,7 +246,8 @@ protected function process(string $action) $dest = $create($this->width, $this->height); - if ($this->image()->imageType === IMAGETYPE_PNG) // png we can actually preserve transparency + // for png and webp we can actually preserve transparency + if (in_array($this->image()->imageType, [IMAGETYPE_PNG, IMAGETYPE_WEBP], true)) { imagealphablending($dest, false); imagesavealpha($dest, true); From 7207b700f79e47bf38b8b8a44b90e17ec16f4410 Mon Sep 17 00:00:00 2001 From: michalsn Date: Mon, 28 Sep 2020 19:41:25 +0200 Subject: [PATCH 171/328] Add $supportTransparency class variable --- system/Images/Handlers/BaseHandler.php | 10 ++++++++++ system/Images/Handlers/GDHandler.php | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 7191cd1ad25b..f272a0fb8c42 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -132,6 +132,16 @@ abstract class BaseHandler implements ImageHandlerInterface 'shadowOffset' => 3, ]; + /** + * Image types with support for transparency. + * + * @var array + */ + protected $supportTransparency = [ + IMAGETYPE_PNG, + IMAGETYPE_WEBP, + ]; + /** * Temporary image used by the different engines. * diff --git a/system/Images/Handlers/GDHandler.php b/system/Images/Handlers/GDHandler.php index 2aa3cc967e9e..a27a337d8182 100644 --- a/system/Images/Handlers/GDHandler.php +++ b/system/Images/Handlers/GDHandler.php @@ -247,7 +247,7 @@ protected function process(string $action) $dest = $create($this->width, $this->height); // for png and webp we can actually preserve transparency - if (in_array($this->image()->imageType, [IMAGETYPE_PNG, IMAGETYPE_WEBP], true)) + if (in_array($this->image()->imageType, $this->supportTransparency, true)) { imagealphablending($dest, false); imagesavealpha($dest, true); From 0c5da41dc7cc8a0af1cf3150812525f5661a79bf Mon Sep 17 00:00:00 2001 From: Kang Jing Date: Sun, 27 Sep 2020 11:26:20 +0800 Subject: [PATCH 172/328] [CI SKIP] Update Array/Json casting use in set --- user_guide_src/source/models/entities.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/models/entities.rst b/user_guide_src/source/models/entities.rst index 5ca5436edd9b..ab5ca35fe26f 100644 --- a/user_guide_src/source/models/entities.rst +++ b/user_guide_src/source/models/entities.rst @@ -341,7 +341,7 @@ Array/Json casting is especially useful with fields that store serialized arrays * a **json**, they will automatically be set as an value of json_decode($value, false), * a **json-array**, they will automatically be set as an value of json_decode($value, true), -when you read the property's value. +when you set the property's value. Unlike the rest of the data types that you can cast properties into, the: * **array** cast type will serialize, From cdf62ca25e1ef5f4ad90c149987517c9763d145f Mon Sep 17 00:00:00 2001 From: Kang Jing Date: Sun, 27 Sep 2020 11:30:39 +0800 Subject: [PATCH 173/328] [CI SKIP] update style --- user_guide_src/source/models/entities.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/models/entities.rst b/user_guide_src/source/models/entities.rst index ab5ca35fe26f..746764f36f9a 100644 --- a/user_guide_src/source/models/entities.rst +++ b/user_guide_src/source/models/entities.rst @@ -357,8 +357,8 @@ the value whenever the property is set:: { protected $casts = [ 'options' => 'array', - 'options_object' => 'json', - 'options_array' => 'json-array' + 'options_object' => 'json', + 'options_array' => 'json-array' ]; } From 3dee600a406be6ca7aa52853b336b31571260afd Mon Sep 17 00:00:00 2001 From: Kang Jing Date: Sun, 27 Sep 2020 11:44:48 +0800 Subject: [PATCH 174/328] [CI SKIP] Separator display --- user_guide_src/source/models/entities.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/models/entities.rst b/user_guide_src/source/models/entities.rst index 746764f36f9a..b49b74ba1b66 100644 --- a/user_guide_src/source/models/entities.rst +++ b/user_guide_src/source/models/entities.rst @@ -102,7 +102,7 @@ Now that all of the pieces are in place, you would work with the Entity class as $userModel->save($user); You may have noticed that the User class has not set any properties for the columns, but you can still -access them as if they were public properties. The base class, **CodeIgniter\Entity**, takes care of this for you, as +access them as if they were public properties. The base class, **CodeIgniter\\Entity**, takes care of this for you, as well as providing the ability to check the properties with **isset()**, or **unset()** the property, and keep track of what columns have changed since the object was created or pulled from the database. From ef2f0336ec8a50869487713eb22509444add3fcf Mon Sep 17 00:00:00 2001 From: Kang Jing Date: Sun, 27 Sep 2020 15:25:17 +0800 Subject: [PATCH 175/328] [CI SKIP] fix. delete replace update --- user_guide_src/source/incoming/restful.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/incoming/restful.rst b/user_guide_src/source/incoming/restful.rst index 40f39661a1ab..55f32acea2dd 100644 --- a/user_guide_src/source/incoming/restful.rst +++ b/user_guide_src/source/incoming/restful.rst @@ -146,7 +146,7 @@ Its usage is similar to the resource routing:: $routes->get('photos/edit/(:segment)', 'Photos::edit/$1'); $routes->post('photos/update/(:segment)', 'Photos::update/$1'); $routes->get('photos/remove/(:segment)', 'Photos::remove/$1'); - $routes->post('photos/delete/(:segment)', 'Photos::update/$1'); + $routes->post('photos/delete/(:segment)', 'Photos::delete/$1'); .. note:: The ordering above is for clarity, whereas the actual order the routes are created in, in RouteCollection, ensures proper route resolution From 82f8b2a2461d0fb07f32d11d0cd8658def22f4af Mon Sep 17 00:00:00 2001 From: MGatner Date: Mon, 28 Sep 2020 17:44:16 +0000 Subject: [PATCH 176/328] Enable registrars during testing --- system/Config/BaseConfig.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index df26d1a36033..78a36d8cd4a3 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -107,13 +107,7 @@ public function __construct() } } - if (defined('ENVIRONMENT') && ENVIRONMENT !== 'testing') - { - // well, this won't happen during unit testing - // @codeCoverageIgnoreStart - $this->registerProperties(); - // @codeCoverageIgnoreEnd - } + $this->registerProperties(); } //-------------------------------------------------------------------- From 554db78a6d2dcbf5829ea3e564beaf7a36815445 Mon Sep 17 00:00:00 2001 From: MGatner Date: Mon, 28 Sep 2020 17:49:51 +0000 Subject: [PATCH 177/328] Clean, expand, and correct Registrar docs --- .../source/general/configuration.rst | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/user_guide_src/source/general/configuration.rst b/user_guide_src/source/general/configuration.rst index 858be5f5da9c..ede817ca98a5 100644 --- a/user_guide_src/source/general/configuration.rst +++ b/user_guide_src/source/general/configuration.rst @@ -229,21 +229,20 @@ Registrars A configuration file can also specify any number of "registrars", which are any other classes which might provide additional configuration properties. -This is done by adding a ``registrars`` property to your configuration file, +This is done by adding a ``$registrars`` property to your configuration file, holding an array of the names of candidate registrars.:: - protected $registrars = [ + public static $registrars = [ SupportingPackageRegistrar::class ]; In order to act as a "registrar" the classes so identified must have a -static function named the same as the configuration class, and it should return an associative +static function with the same name as the configuration class, and it should return an associative array of property settings. When your configuration object is instantiated, it will loop over the -designated classes in ``$registrars``. For each of these classes, which contains a method name matching -the configuration class, it will invoke that method, and incorporate any returned properties -the same way as described for namespaced variables. +designated classes in ``$registrars``. For each of these classes it will invoke +the method named for the configuration class and incorporate any returned properties. A sample configuration class setup for this:: @@ -253,9 +252,9 @@ A sample configuration class setup for this:: class MySalesConfig extends BaseConfig { - public $target = 100; - public $campaign = "Winter Wonderland"; - protected $registrars = [ + public $target = 100; + public $campaign = "Winter Wonderland"; + public static $registrars = [ '\App\Models\RegionalSales'; ]; } @@ -272,9 +271,14 @@ A sample configuration class setup for this:: } } -With the above example, when `MySalesConfig` is instantiated, it will end up with -the two properties declared, but the value of the `$target` property will be over-ridden -by treating `RegionalSalesModel` as a "registrar". The resulting configuration properties:: +With the above example, when ``MySalesConfig`` is instantiated, it will end up with +the two properties declared, but the value of the ``$target`` property will be overridden +by treating ``RegionalSales`` as a "registrar". The resulting configuration properties:: $target = 45; $campaign = "Winter Wonderland"; + +In addition to explicit registrars defined by the ``$registrars`` property, you may also +define registrars in any namespace using the **Config/Registrars.php** file, if discovery +is enabled in :doc:`Modules `. These files work the same as the classes +described above, using methods named for each configuration class you wish to extend. From fde5748b384bdf3dc1715154695041d2a3b68cba Mon Sep 17 00:00:00 2001 From: MGatner Date: Mon, 28 Sep 2020 19:57:28 +0000 Subject: [PATCH 178/328] Fix registrar collision --- tests/_support/Config/{Registrar.php => TestRegistrar.php} | 2 +- tests/system/Config/BaseConfigTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/_support/Config/{Registrar.php => TestRegistrar.php} (94%) diff --git a/tests/_support/Config/Registrar.php b/tests/_support/Config/TestRegistrar.php similarity index 94% rename from tests/_support/Config/Registrar.php rename to tests/_support/Config/TestRegistrar.php index 9d00c6a24c46..4651e0fea9bb 100644 --- a/tests/_support/Config/Registrar.php +++ b/tests/_support/Config/TestRegistrar.php @@ -6,7 +6,7 @@ * Provides a basic registrar class for testing BaseConfig registration functions. */ -class Registrar +class TestRegistrar { public static function RegistrarConfig() diff --git a/tests/system/Config/BaseConfigTest.php b/tests/system/Config/BaseConfigTest.php index f48bed64ff6b..c941305cf923 100644 --- a/tests/system/Config/BaseConfigTest.php +++ b/tests/system/Config/BaseConfigTest.php @@ -227,7 +227,7 @@ public function testRecognizesLooseValues() public function testRegistrars() { $config = new \RegistrarConfig(); - $config::$registrars = ['\Tests\Support\Config\Registrar']; + $config::$registrars = ['\Tests\Support\Config\TestRegistrar']; $this->setPrivateProperty($config, 'didDiscovery', true); $method = $this->getPrivateMethodInvoker($config, 'registerProperties'); $method(); From abf10f8d663bde55da529aa78d25feddd01d99c6 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 25 Sep 2020 10:41:42 +0700 Subject: [PATCH 179/328] apply rector: set local variable name to camel case --- .github/workflows/test-rector.yml | 45 +++++++++++ composer.json | 3 +- rector.php | 27 +++++++ system/Autoloader/Autoloader.php | 8 +- system/Autoloader/FileLocator.php | 16 ++-- system/CLI/CLI.php | 60 +++++++-------- system/Cache/Handlers/FileHandler.php | 26 +++---- system/Cache/Handlers/MemcachedHandler.php | 4 +- system/Cache/Handlers/PredisHandler.php | 4 +- system/Cache/Handlers/RedisHandler.php | 4 +- system/Common.php | 8 +- system/Database/BaseBuilder.php | 76 +++++++++---------- system/Database/BaseConnection.php | 20 ++--- system/Database/BaseResult.php | 16 +++- system/Database/BaseUtils.php | 6 +- system/Database/Forge.php | 4 +- system/Database/MySQLi/Connection.php | 10 +-- system/Database/MySQLi/Forge.php | 8 +- system/Database/MySQLi/Result.php | 4 +- system/Database/Postgre/Connection.php | 4 +- system/Database/SQLite3/Result.php | 4 +- system/Debug/Iterator.php | 8 +- system/Debug/Toolbar/Views/toolbar.tpl.php | 2 +- system/Email/Email.php | 56 +++++++------- system/Encryption/Handlers/OpenSSLHandler.php | 8 +- system/HTTP/CURLRequest.php | 20 ++--- system/HTTP/DownloadResponse.php | 20 ++--- system/HTTP/Files/FileCollection.php | 8 +- system/HTTP/Message.php | 30 ++++---- system/HTTP/Negotiate.php | 8 +- system/HTTP/Request.php | 16 ++-- system/HTTP/UserAgent.php | 6 +- system/Helpers/filesystem_helper.php | 10 +-- system/Helpers/form_helper.php | 8 +- system/Helpers/html_helper.php | 6 +- system/Helpers/number_helper.php | 28 +++---- system/Helpers/text_helper.php | 14 ++-- system/Helpers/url_helper.php | 36 ++++----- system/Honeypot/Honeypot.php | 4 +- system/Images/Handlers/GDHandler.php | 4 +- system/Log/Handlers/FileHandler.php | 8 +- system/Router/RouteCollection.php | 24 +++--- system/Router/Router.php | 6 +- system/Security/Security.php | 10 +-- system/Session/Handlers/FileHandler.php | 8 +- system/Session/Handlers/MemcachedHandler.php | 26 +++---- system/Session/Handlers/RedisHandler.php | 22 +++--- system/Session/Session.php | 28 +++---- system/Test/Mock/MockCommon.php | 6 +- system/Test/ReflectionHelper.php | 26 +++---- system/Test/TestLogger.php | 4 +- system/Typography/Typography.php | 10 +-- system/Validation/Rules.php | 8 +- system/Validation/Validation.php | 16 ++-- system/View/Cell.php | 20 ++--- system/View/Parser.php | 4 +- 56 files changed, 481 insertions(+), 394 deletions(-) create mode 100644 .github/workflows/test-rector.yml create mode 100644 rector.php diff --git a/.github/workflows/test-rector.yml b/.github/workflows/test-rector.yml new file mode 100644 index 000000000000..210398390294 --- /dev/null +++ b/.github/workflows/test-rector.yml @@ -0,0 +1,45 @@ +# When a PR is opened or a push is made, perform +# a static analysis check on the code using Rector. +name: Rector + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**' + - 'system/**' + push: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**' + - 'system/**' + +jobs: + build: + name: Analyze code (Rector) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + extensions: intl + + - name: Use latest Composer + run: composer self-update + + - name: Validate composer.json + run: composer validate --strict + + - name: Install dependencies + run: composer install --ansi --no-progress --no-suggest --no-interaction + + - name: Run static analysis + run: vendor/bin/rector process --dry-run diff --git a/composer.json b/composer.json index dbbd2661fa90..fc5856b3f33e 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^8.5", "predis/predis": "^1.1", - "squizlabs/php_codesniffer": "^3.3" + "squizlabs/php_codesniffer": "^3.3", + "rector/rector": "dev-failing-test-case-4290" }, "autoload": { "psr-4": { diff --git a/rector.php b/rector.php new file mode 100644 index 000000000000..b693ce668ea1 --- /dev/null +++ b/rector.php @@ -0,0 +1,27 @@ +parameters(); + + // paths to refactor; solid alternative to CLI arguments + $parameters->set(Option::PATHS, [__DIR__ . '/app', __DIR__ . '/system']); + + // is there a file you need to skip? + $parameters->set(Option::EXCLUDE_PATHS, [ + __DIR__ . '/app/Views', + __DIR__ . '/system/ThirdParty', + ]); + + // Rector relies on autoload setup of your project; Composer autoload is included by default; to add more: + $parameters->set(Option::AUTOLOAD_PATHS, [ + // autoload specific file + __DIR__ . '/system/Test/bootstrap.php', + ]); + + $services = $containerConfigurator->services(); + $services->set(UnderscoreToCamelCaseLocalVariableNameRector::class); +}; diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index a5ce8a4525b9..fd07d89cac73 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -262,16 +262,16 @@ public function loadClass(string $class) $class = trim($class, '\\'); $class = str_ireplace('.php', '', $class); - $mapped_file = $this->loadInNamespace($class); + $mappedFile = $this->loadInNamespace($class); // Nothing? One last chance by looking // in common CodeIgniter folders. - if (! $mapped_file) + if (! $mappedFile) { - $mapped_file = $this->loadLegacy($class); + $mappedFile = $this->loadLegacy($class); } - return $mapped_file; + return $mappedFile; } //-------------------------------------------------------------------- diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 9e516468c734..fc87beb07868 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -169,11 +169,11 @@ public function locateFile(string $file, string $folder = null, string $ext = 'p */ public function getClassname(string $file) : string { - $php = file_get_contents($file); - $tokens = token_get_all($php); - $dlm = false; - $namespace = ''; - $class_name = ''; + $php = file_get_contents($file); + $tokens = token_get_all($php); + $dlm = false; + $namespace = ''; + $className = ''; foreach ($tokens as $i => $token) { @@ -202,17 +202,17 @@ public function getClassname(string $file) : string && $tokens[$i - 1][0] === T_WHITESPACE && $token[0] === T_STRING) { - $class_name = $token[1]; + $className = $token[1]; break; } } - if (empty( $class_name )) + if (empty( $className )) { return ''; } - return $namespace . '\\' . $class_name; + return $namespace . '\\' . $className; } //-------------------------------------------------------------------- diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 1ee6d45d345c..c4343dfcbf41 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -257,37 +257,37 @@ public static function input(string $prefix = null): string */ public static function prompt(string $field, $options = null, string $validation = null): string { - $extra_output = ''; - $default = ''; + $extraOutput = ''; + $default = ''; if (is_string($options)) { - $extra_output = ' [' . static::color($options, 'white') . ']'; - $default = $options; + $extraOutput = ' [' . static::color($options, 'white') . ']'; + $default = $options; } if (is_array($options) && $options) { - $opts = $options; - $extra_output_default = static::color($opts[0], 'white'); + $opts = $options; + $extraOutputDefault = static::color($opts[0], 'white'); unset($opts[0]); if (empty($opts)) { - $extra_output = $extra_output_default; + $extraOutput = $extraOutputDefault; } else { - $extra_output = ' [' . $extra_output_default . ', ' . implode(', ', $opts) . ']'; - $validation .= '|in_list[' . implode(',', $options) . ']'; - $validation = trim($validation, '|'); + $extraOutput = ' [' . $extraOutputDefault . ', ' . implode(', ', $opts) . ']'; + $validation .= '|in_list[' . implode(',', $options) . ']'; + $validation = trim($validation, '|'); } $default = $options[0]; } - static::fwrite(STDOUT, $field . $extra_output . ': '); + static::fwrite(STDOUT, $field . $extraOutput . ': '); // Read the input from keyboard. $input = trim(static::input()) ?: $default; @@ -1070,45 +1070,45 @@ public static function getOptionString(bool $useLongOpts = false, bool $trim = f public static function table(array $tbody, array $thead = []) { // All the rows in the table will be here until the end - $table_rows = []; + $tableRows = []; // We need only indexes and not keys if (! empty($thead)) { - $table_rows[] = array_values($thead); + $tableRows[] = array_values($thead); } foreach ($tbody as $tr) { - $table_rows[] = array_values($tr); + $tableRows[] = array_values($tr); } // Yes, it really is necessary to know this count - $total_rows = count($table_rows); + $totalRows = count($tableRows); // Store all columns lengths // $all_cols_lengths[row][column] = length - $all_cols_lengths = []; + $allColsLengths = []; // Store maximum lengths by column // $max_cols_lengths[column] = length - $max_cols_lengths = []; + $maxColsLengths = []; // Read row by row and define the longest columns - for ($row = 0; $row < $total_rows; $row ++) + for ($row = 0; $row < $totalRows; $row ++) { $column = 0; // Current column index - foreach ($table_rows[$row] as $col) + foreach ($tableRows[$row] as $col) { // Sets the size of this column in the current row - $all_cols_lengths[$row][$column] = static::strlen($col); + $allColsLengths[$row][$column] = static::strlen($col); // If the current column does not have a value among the larger ones // or the value of this is greater than the existing one // then, now, this assumes the maximum length - if (! isset($max_cols_lengths[$column]) || $all_cols_lengths[$row][$column] > $max_cols_lengths[$column]) + if (! isset($maxColsLengths[$column]) || $allColsLengths[$row][$column] > $maxColsLengths[$column]) { - $max_cols_lengths[$column] = $all_cols_lengths[$row][$column]; + $maxColsLengths[$column] = $allColsLengths[$row][$column]; } // We can go check the size of the next column... @@ -1118,15 +1118,15 @@ public static function table(array $tbody, array $thead = []) // Read row by row and add spaces at the end of the columns // to match the exact column length - for ($row = 0; $row < $total_rows; $row ++) + for ($row = 0; $row < $totalRows; $row ++) { $column = 0; - foreach ($table_rows[$row] as $col) + foreach ($tableRows[$row] as $col) { - $diff = $max_cols_lengths[$column] - static::strlen($col); + $diff = $maxColsLengths[$column] - static::strlen($col); if ($diff) { - $table_rows[$row][$column] = $table_rows[$row][$column] . str_repeat(' ', $diff); + $tableRows[$row][$column] = $tableRows[$row][$column] . str_repeat(' ', $diff); } $column ++; } @@ -1135,13 +1135,13 @@ public static function table(array $tbody, array $thead = []) $table = ''; // Joins columns and append the well formatted rows to the table - for ($row = 0; $row < $total_rows; $row ++) + for ($row = 0; $row < $totalRows; $row ++) { // Set the table border-top if ($row === 0) { $cols = '+'; - foreach ($table_rows[$row] as $col) + foreach ($tableRows[$row] as $col) { $cols .= str_repeat('-', static::strlen($col) + 2) . '+'; } @@ -1149,10 +1149,10 @@ public static function table(array $tbody, array $thead = []) } // Set the columns borders - $table .= '| ' . implode(' | ', $table_rows[$row]) . ' |' . PHP_EOL; + $table .= '| ' . implode(' | ', $tableRows[$row]) . ' |' . PHP_EOL; // Set the thead and table borders-bottom - if (isset($cols) && ($row === 0 && ! empty($thead) || $row + 1 === $total_rows)) + if (isset($cols) && ($row === 0 && ! empty($thead) || $row + 1 === $totalRows)) { $table .= $cols . PHP_EOL; } diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index f80d21567536..3291e6634473 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -185,9 +185,9 @@ public function increment(string $key, int $offset = 1) return false; } - $new_value = $data['data'] + $offset; + $newValue = $data['data'] + $offset; - return $this->save($key, $new_value, $data['ttl']) ? $new_value : false; + return $this->save($key, $newValue, $data['ttl']) ? $newValue : false; } //-------------------------------------------------------------------- @@ -218,9 +218,9 @@ public function decrement(string $key, int $offset = 1) return false; } - $new_value = $data['data'] - $offset; + $newValue = $data['data'] - $offset; - return $this->save($key, $new_value, $data['ttl']) ? $new_value : false; + return $this->save($key, $newValue, $data['ttl']) ? $newValue : false; } //-------------------------------------------------------------------- @@ -394,12 +394,12 @@ protected function deleteFiles(string $path, bool $del_dir = false, bool $htdocs // Trim the trailing slash $path = rtrim($path, '/\\'); - if (! $current_dir = @opendir($path)) + if (! $currentDir = @opendir($path)) { return false; } - while (false !== ($filename = @readdir($current_dir))) + while (false !== ($filename = @readdir($currentDir))) { if ($filename !== '.' && $filename !== '..') { @@ -414,7 +414,7 @@ protected function deleteFiles(string $path, bool $del_dir = false, bool $htdocs } } - closedir($current_dir); + closedir($currentDir); return ($del_dir === true && $_level > 0) ? @rmdir($path) : true; } @@ -437,15 +437,15 @@ protected function deleteFiles(string $path, bool $del_dir = false, bool $htdocs */ protected function getDirFileInfo(string $source_dir, bool $top_level_only = true, bool $_recursion = false) { - static $_filedata = []; - $relative_path = $source_dir; + static $filedata = []; + $relativePath = $source_dir; if ($fp = @opendir($source_dir)) { // reset the array and make sure $source_dir has a trailing slash on the initial call if ($_recursion === false) { - $_filedata = []; + $filedata = []; $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; } @@ -458,14 +458,14 @@ protected function getDirFileInfo(string $source_dir, bool $top_level_only = tru } elseif ($file[0] !== '.') { - $_filedata[$file] = $this->getFileInfo($source_dir . $file); - $_filedata[$file]['relative_path'] = $relative_path; + $filedata[$file] = $this->getFileInfo($source_dir . $file); + $filedata[$file]['relative_path'] = $relativePath; } } closedir($fp); - return $_filedata; + return $filedata; } return false; diff --git a/system/Cache/Handlers/MemcachedHandler.php b/system/Cache/Handlers/MemcachedHandler.php index d1676eefbc55..1b421c5251b3 100644 --- a/system/Cache/Handlers/MemcachedHandler.php +++ b/system/Cache/Handlers/MemcachedHandler.php @@ -148,12 +148,12 @@ public function initialize() $this->memcached = new \Memcache(); // Check if we can connect to the server - $can_connect = $this->memcached->connect( + $canConnect = $this->memcached->connect( $this->config['host'], $this->config['port'] ); // If we can't connect, throw a CriticalError exception - if ($can_connect === false) + if ($canConnect === false) { throw new CriticalError('Cache: Memcache connection failed.'); } diff --git a/system/Cache/Handlers/PredisHandler.php b/system/Cache/Handlers/PredisHandler.php index bdfcea07fa96..4e57ceef2548 100644 --- a/system/Cache/Handlers/PredisHandler.php +++ b/system/Cache/Handlers/PredisHandler.php @@ -167,7 +167,7 @@ public function get(string $key) */ public function save(string $key, $value, int $ttl = 60) { - switch ($data_type = gettype($value)) + switch ($dataType = gettype($value)) { case 'array': case 'object': @@ -184,7 +184,7 @@ public function save(string $key, $value, int $ttl = 60) return false; } - if (! $this->redis->hmset($key, ['__ci_type' => $data_type, '__ci_value' => $value])) + if (! $this->redis->hmset($key, ['__ci_type' => $dataType, '__ci_value' => $value])) { return false; } diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index d392a7959742..8ea9e5b06409 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -202,7 +202,7 @@ public function save(string $key, $value, int $ttl = 60) { $key = $this->prefix . $key; - switch ($data_type = gettype($value)) + switch ($dataType = gettype($value)) { case 'array': case 'object': @@ -219,7 +219,7 @@ public function save(string $key, $value, int $ttl = 60) return false; } - if (! $this->redis->hMSet($key, ['__ci_type' => $data_type, '__ci_value' => $value])) + if (! $this->redis->hMSet($key, ['__ci_type' => $dataType, '__ci_value' => $value])) { return false; } diff --git a/system/Common.php b/system/Common.php index 538e351d2a18..62d06ff5e66e 100644 --- a/system/Common.php +++ b/system/Common.php @@ -602,16 +602,16 @@ function force_https(int $duration = 31536000, RequestInterface $request = null, */ function function_usable(string $function_name): bool { - static $_suhosin_func_blacklist; + static $suhosinFuncBlacklist; if (function_exists($function_name)) { - if (! isset($_suhosin_func_blacklist)) + if (! isset($suhosinFuncBlacklist)) { - $_suhosin_func_blacklist = extension_loaded('suhosin') ? explode(',', trim(ini_get('suhosin.executor.func.blacklist'))) : []; + $suhosinFuncBlacklist = extension_loaded('suhosin') ? explode(',', trim(ini_get('suhosin.executor.func.blacklist'))) : []; } - return ! in_array($function_name, $_suhosin_func_blacklist, true); + return ! in_array($function_name, $suhosinFuncBlacklist, true); } return false; diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index ab3c8205ef1f..c6a06c13a111 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -1291,16 +1291,16 @@ protected function _like($field, string $match = '', string $type = 'AND ', stri $bind = $this->setBind($k, "%$v%", $escape); } - $like_statement = $this->_like_statement($prefix, $k, $not, $bind, $insensitiveSearch); + $likeStatement = $this->_like_statement($prefix, $k, $not, $bind, $insensitiveSearch); // some platforms require an escape sequence definition for LIKE wildcards if ($escape === true && $this->db->likeEscapeStr !== '') { - $like_statement .= sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar); + $likeStatement .= sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar); } $this->{$clause}[] = [ - 'condition' => $like_statement, + 'condition' => $likeStatement, 'escape' => $escape, ]; } @@ -1323,14 +1323,14 @@ protected function _like($field, string $match = '', string $type = 'AND ', stri */ protected function _like_statement(string $prefix = null, string $column, string $not = null, string $bind, bool $insensitiveSearch = false): string { - $like_statement = "{$prefix} {$column} {$not} LIKE :{$bind}:"; + $likeStatement = "{$prefix} {$column} {$not} LIKE :{$bind}:"; if ($insensitiveSearch === true) { - $like_statement = "{$prefix} LOWER({$column}) {$not} LIKE :{$bind}:"; + $likeStatement = "{$prefix} LOWER({$column}) {$not} LIKE :{$bind}:"; } - return $like_statement; + return $likeStatement; } //-------------------------------------------------------------------- @@ -1636,7 +1636,7 @@ public function orderBy(string $orderBy, string $direction = '', bool $escape = if ($escape === false) { - $qb_orderBy[] = [ + $qbOrderBy[] = [ 'field' => $orderBy, 'direction' => $direction, 'escape' => false, @@ -1644,10 +1644,10 @@ public function orderBy(string $orderBy, string $direction = '', bool $escape = } else { - $qb_orderBy = []; + $qbOrderBy = []; foreach (explode(',', $orderBy) as $field) { - $qb_orderBy[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE)) + $qbOrderBy[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE)) ? [ 'field' => ltrim(substr($field, 0, $match[0][1])), @@ -1663,7 +1663,7 @@ public function orderBy(string $orderBy, string $direction = '', bool $escape = } } - $this->QBOrderBy = array_merge($this->QBOrderBy, $qb_orderBy); + $this->QBOrderBy = array_merge($this->QBOrderBy, $qbOrderBy); return $this; } @@ -2085,19 +2085,19 @@ public function insertBatch(array $set = null, bool $escape = null, int $batchSi $table = $this->QBFrom[0]; // Batch this baby - $affected_rows = 0; + $affectedRows = 0; for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batchSize) { $sql = $this->_insertBatch($this->db->protectIdentifiers($table, true, $escape, false), $this->QBKeys, array_slice($this->QBSet, $i, $batchSize)); if ($this->testMode) { - ++ $affected_rows; + ++ $affectedRows; } else { $this->db->query($sql, $this->binds, false); - $affected_rows += $this->db->affectedRows(); + $affectedRows += $this->db->affectedRows(); } } @@ -2106,7 +2106,7 @@ public function insertBatch(array $set = null, bool $escape = null, int $batchSi $this->resetWrite(); } - return $affected_rows; + return $affectedRows; } //-------------------------------------------------------------------- @@ -2591,9 +2591,9 @@ public function updateBatch(array $set = null, string $index = null, int $batchS $table = $this->QBFrom[0]; // Batch this baby - $affected_rows = 0; - $savedSQL = []; - $savedQBWhere = $this->QBWhere; + $affectedRows = 0; + $savedSQL = []; + $savedQBWhere = $this->QBWhere; for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batchSize) { $sql = $this->_updateBatch($table, array_slice($this->QBSet, $i, $batchSize), $this->db->protectIdentifiers($index) @@ -2606,7 +2606,7 @@ public function updateBatch(array $set = null, string $index = null, int $batchS else { $this->db->query($sql, $this->binds, false); - $affected_rows += $this->db->affectedRows(); + $affectedRows += $this->db->affectedRows(); } $this->QBWhere = $savedQBWhere; @@ -2614,7 +2614,7 @@ public function updateBatch(array $set = null, string $index = null, int $batchS $this->resetWrite(); - return $this->testMode ? $savedSQL : $affected_rows; + return $this->testMode ? $savedSQL : $affectedRows; } //-------------------------------------------------------------------- @@ -2686,13 +2686,13 @@ public function setUpdateBatch($key, string $index = '', bool $escape = null) foreach ($key as $v) { - $index_set = false; - $clean = []; + $indexSet = false; + $clean = []; foreach ($v as $k2 => $v2) { if ($k2 === $index) { - $index_set = true; + $indexSet = true; } $bind = $this->setBind($k2, $v2, $escape); @@ -2700,7 +2700,7 @@ public function setUpdateBatch($key, string $index = '', bool $escape = null) $clean[$this->db->protectIdentifiers($k2, false, $escape)] = ":$bind:"; } - if ($index_set === false) + if ($indexSet === false) { throw new DatabaseException('One or more rows submitted for batch updating is missing the specified index.'); } @@ -2992,8 +2992,8 @@ protected function compileSelect($select_override = false): string // is because until the user calls the from() function we don't know if there are aliases foreach ($this->QBSelect as $key => $val) { - $no_escape = $this->QBNoEscape[$key] ?? null; - $this->QBSelect[$key] = $this->db->protectIdentifiers($val, false, $no_escape); + $noEscape = $this->QBNoEscape[$key] ?? null; + $this->QBSelect[$key] = $this->db->protectIdentifiers($val, false, $noEscape); } $sql .= implode(', ', $this->QBSelect); @@ -3300,14 +3300,14 @@ protected function isLiteral(string $str): bool return true; } - static $_str; + static $str; - if (empty($_str)) + if (empty($str)) { - $_str = ($this->db->escapeChar !== '"') ? ['"', "'"] : ["'"]; + $str = ($this->db->escapeChar !== '"') ? ['"', "'"] : ["'"]; } - return in_array($str[0], $_str, true); + return in_array($str[0], $str, true); } //-------------------------------------------------------------------- @@ -3338,9 +3338,9 @@ public function resetQuery() */ protected function resetRun(array $qb_reset_items) { - foreach ($qb_reset_items as $item => $default_value) + foreach ($qb_reset_items as $item => $defaultValue) { - $this->$item = $default_value; + $this->$item = $defaultValue; } } @@ -3422,12 +3422,12 @@ protected function hasOperator(string $str): bool */ protected function getOperator(string $str, bool $list = false) { - static $_operators; + static $operators; - if (empty($_operators)) + if (empty($operators)) { - $_les = ($this->db->likeEscapeStr !== '') ? '\s+' . preg_quote(trim(sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar)), '/') : ''; - $_operators = [ + $les = ($this->db->likeEscapeStr !== '') ? '\s+' . preg_quote(trim(sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar)), '/') : ''; + $operators = [ '\s*(?:<|>|!)?=\s*', // =, <=, >=, != '\s*<>?\s*', // <, <> '\s*>\s*', // > @@ -3438,12 +3438,12 @@ protected function getOperator(string $str, bool $list = false) '\s+BETWEEN\s+', // BETWEEN value AND value '\s+IN\s*\(.*\)', // IN(list) '\s+NOT IN\s*\(.*\)', // NOT IN (list) - '\s+LIKE\s+\S.*(' . $_les . ')?', // LIKE 'expr'[ ESCAPE '%s'] - '\s+NOT LIKE\s+\S.*(' . $_les . ')?', // NOT LIKE 'expr'[ ESCAPE '%s'] + '\s+LIKE\s+\S.*(' . $les . ')?', // LIKE 'expr'[ ESCAPE '%s'] + '\s+NOT LIKE\s+\S.*(' . $les . ')?', // NOT LIKE 'expr'[ ESCAPE '%s'] ]; } - return preg_match_all('/' . implode('|', $_operators) . '/i', $str, $match) ? ($list ? $match[0] : $match[0][0]) : false; + return preg_match_all('/' . implode('|', $operators) . '/i', $str, $match) ? ($list ? $match[0] : $match[0][0]) : false; } // -------------------------------------------------------------------- diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index c7a3bb202cb6..693ae210857e 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1106,13 +1106,13 @@ public function protectIdentifiers($item, bool $prefixSingle = false, bool $prot if (is_array($item)) { - $escaped_array = []; + $escapedArray = []; foreach ($item as $k => $v) { - $escaped_array[$this->protectIdentifiers($k)] = $this->protectIdentifiers($v, $prefixSingle, $protectIdentifiers, $fieldExists); + $escapedArray[$this->protectIdentifiers($k)] = $this->protectIdentifiers($v, $prefixSingle, $protectIdentifiers, $fieldExists); } - return $escaped_array; + return $escapedArray; } // This is basically a bug fix for queries that use MAX, MIN, etc. @@ -1296,13 +1296,13 @@ public function escapeIdentifiers($item) return $item; } - static $preg_ec = []; + static $pregEc = []; - if (empty($preg_ec)) + if (empty($pregEc)) { if (is_array($this->escapeChar)) { - $preg_ec = [ + $pregEc = [ preg_quote($this->escapeChar[0], '/'), preg_quote($this->escapeChar[1], '/'), $this->escapeChar[0], @@ -1311,8 +1311,8 @@ public function escapeIdentifiers($item) } else { - $preg_ec[0] = $preg_ec[1] = preg_quote($this->escapeChar, '/'); - $preg_ec[2] = $preg_ec[3] = $this->escapeChar; + $pregEc[0] = $pregEc[1] = preg_quote($this->escapeChar, '/'); + $pregEc[2] = $pregEc[3] = $this->escapeChar; } } @@ -1320,11 +1320,11 @@ public function escapeIdentifiers($item) { if (strpos($item, '.' . $id) !== false) { - return preg_replace('/' . $preg_ec[0] . '?([^' . $preg_ec[1] . '\.]+)' . $preg_ec[1] . '?\./i', $preg_ec[2] . '$1' . $preg_ec[3] . '.', $item); + return preg_replace('/' . $pregEc[0] . '?([^' . $pregEc[1] . '\.]+)' . $pregEc[1] . '?\./i', $pregEc[2] . '$1' . $pregEc[3] . '.', $item); } } - return preg_replace('/' . $preg_ec[0] . '?([^' . $preg_ec[1] . '\.]+)' . $preg_ec[1] . '?(\.)?/i', $preg_ec[2] . '$1' . $preg_ec[3] . '$2', $item); + return preg_replace('/' . $pregEc[0] . '?([^' . $pregEc[1] . '\.]+)' . $pregEc[1] . '?(\.)?/i', $pregEc[2] . '$1' . $pregEc[3] . '$2', $item); } //-------------------------------------------------------------------- diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index 10d2c7f444cd..f040cbb91821 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -165,22 +165,36 @@ public function getCustomResultObject(string $className) } // Don't fetch the result set again if we already have it + /** + * @noRector \Rector\CodingStyle\Rector\Variable\UnderscoreToCamelCaseLocalVariableNameRector + */ $_data = null; if (($c = count($this->resultArray)) > 0) { + /** + * @noRector \Rector\CodingStyle\Rector\Variable\UnderscoreToCamelCaseLocalVariableNameRector + */ $_data = 'resultArray'; } elseif (($c = count($this->resultObject)) > 0) { + /** + * @noRector \Rector\CodingStyle\Rector\Variable\UnderscoreToCamelCaseLocalVariableNameRector + */ $_data = 'resultObject'; } + /** + * @noRector \Rector\CodingStyle\Rector\Variable\UnderscoreToCamelCaseLocalVariableNameRector + */ if ($_data !== null) { for ($i = 0; $i < $c; $i ++) { $this->customResultObject[$className][$i] = new $className(); - + /** + * @noRector \Rector\CodingStyle\Rector\Variable\UnderscoreToCamelCaseLocalVariableNameRector + */ foreach ($this->{$_data}[$i] as $key => $value) { $this->customResultObject[$className][$i]->$key = $value; diff --git a/system/Database/BaseUtils.php b/system/Database/BaseUtils.php index 1c5fff1dcc19..567403d78168 100644 --- a/system/Database/BaseUtils.php +++ b/system/Database/BaseUtils.php @@ -193,9 +193,9 @@ public function optimizeDatabase() } $result = []; - foreach ($this->db->listTables() as $table_name) + foreach ($this->db->listTables() as $tableName) { - $res = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($table_name))); + $res = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($tableName))); if (is_bool($res)) { return $res; @@ -208,7 +208,7 @@ public function optimizeDatabase() // Postgre & SQLite3 returns empty array if (empty($res)) { - $key = $table_name; + $key = $tableName; } else { diff --git a/system/Database/Forge.php b/system/Database/Forge.php index ac0235e81bcc..c3fb1b6f2b34 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1300,9 +1300,9 @@ protected function _processForeignKeys(string $table): string { foreach ($this->foreignKeys as $field => $fkey) { - $name_index = $table . '_' . $field . '_foreign'; + $nameIndex = $table . '_' . $field . '_foreign'; - $sql .= ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers($name_index) + $sql .= ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers($nameIndex) . ' FOREIGN KEY(' . $this->db->escapeIdentifiers($field) . ') REFERENCES ' . $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['table']) . ' (' . $this->db->escapeIdentifiers($fkey['field']) . ')'; if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 1c6de4232853..4f83c7700a10 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -106,7 +106,7 @@ public function connect(bool $persistent = false) $socket = ''; } - $client_flags = ($this->compress === true) ? MYSQLI_CLIENT_COMPRESS : 0; + $clientFlags = ($this->compress === true) ? MYSQLI_CLIENT_COMPRESS : 0; $this->mysqli = mysqli_init(); mysqli_report(MYSQLI_REPORT_ALL & ~MYSQLI_REPORT_INDEX); @@ -161,11 +161,11 @@ public function connect(bool $persistent = false) // https://bugs.php.net/bug.php?id=68344 elseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT') && version_compare($this->mysqli->client_info, '5.6', '>=')) { - $client_flags += MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; + $clientFlags += MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; } } - $client_flags += MYSQLI_CLIENT_SSL; + $clientFlags += MYSQLI_CLIENT_SSL; $this->mysqli->ssl_set( $ssl['key'] ?? null, $ssl['cert'] ?? null, $ssl['ca'] ?? null, $ssl['capath'] ?? null, $ssl['cipher'] ?? null @@ -176,11 +176,11 @@ public function connect(bool $persistent = false) try { if ($this->mysqli->real_connect($hostname, $this->username, $this->password, - $this->database, $port, $socket, $client_flags) + $this->database, $port, $socket, $clientFlags) ) { // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails - if (($client_flags & MYSQLI_CLIENT_SSL) && version_compare($this->mysqli->client_info, '5.7.3', '<=') + if (($clientFlags & MYSQLI_CLIENT_SSL) && version_compare($this->mysqli->client_info, '5.7.3', '<=') && empty($this->mysqli->query("SHOW STATUS LIKE 'ssl_cipher'") ->fetch_object()->Value) ) diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index 5755c1aecf20..6dd7c40c4b08 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -212,11 +212,11 @@ protected function _alterTable(string $alter_type, string $table, $field) */ protected function _processColumn(array $field): string { - $extra_clause = isset($field['after']) ? ' AFTER ' . $this->db->escapeIdentifiers($field['after']) : ''; + $extraClause = isset($field['after']) ? ' AFTER ' . $this->db->escapeIdentifiers($field['after']) : ''; - if (empty($extra_clause) && isset($field['first']) && $field['first'] === true) + if (empty($extraClause) && isset($field['first']) && $field['first'] === true) { - $extra_clause = ' FIRST'; + $extraClause = ' FIRST'; } return $this->db->escapeIdentifiers($field['name']) @@ -228,7 +228,7 @@ protected function _processColumn(array $field): string . $field['auto_increment'] . $field['unique'] . (empty($field['comment']) ? '' : ' COMMENT ' . $field['comment']) - . $extra_clause; + . $extraClause; } //-------------------------------------------------------------------- diff --git a/system/Database/MySQLi/Result.php b/system/Database/MySQLi/Result.php index eb929b538b8d..24c0ea9b9ac1 100644 --- a/system/Database/MySQLi/Result.php +++ b/system/Database/MySQLi/Result.php @@ -87,7 +87,7 @@ public function getFieldNames(): array */ public function getFieldData(): array { - static $data_types = [ + static $dataTypes = [ MYSQLI_TYPE_DECIMAL => 'decimal', MYSQLI_TYPE_NEWDECIMAL => 'newdecimal', MYSQLI_TYPE_FLOAT => 'float', @@ -128,7 +128,7 @@ public function getFieldData(): array $retVal[$i]->name = $data->name; $retVal[$i]->type = $data->type; $retVal[$i]->type_name = in_array($data->type, [1, 247], true) - ? 'char' : (isset($data_types[$data->type]) ? $data_types[$data->type] : null); + ? 'char' : (isset($dataTypes[$data->type]) ? $dataTypes[$data->type] : null); $retVal[$i]->max_length = $data->max_length; $retVal[$i]->primary_key = (int) ($data->flags & 2); $retVal[$i]->length = $data->length; diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 40fdb2b8a881..e86c0bba7767 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -369,10 +369,10 @@ public function _indexData(string $table): array { $obj = new \stdClass(); $obj->name = $row->indexname; - $_fields = explode(',', preg_replace('/^.*\((.+?)\)$/', '$1', trim($row->indexdef))); + $fields = explode(',', preg_replace('/^.*\((.+?)\)$/', '$1', trim($row->indexdef))); $obj->fields = array_map(function ($v) { return trim($v); - }, $_fields); + }, $fields); if (strpos($row->indexdef, 'CREATE UNIQUE INDEX pk') === 0) { diff --git a/system/Database/SQLite3/Result.php b/system/Database/SQLite3/Result.php index f6b96638639d..c66df671b43b 100644 --- a/system/Database/SQLite3/Result.php +++ b/system/Database/SQLite3/Result.php @@ -86,7 +86,7 @@ public function getFieldNames(): array */ public function getFieldData(): array { - static $data_types = [ + static $dataTypes = [ SQLITE3_INTEGER => 'integer', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', @@ -103,7 +103,7 @@ public function getFieldData(): array $retVal[$i]->name = $this->resultID->columnName($i); // @phpstan-ignore-line $type = $this->resultID->columnType($i); // @phpstan-ignore-line $retVal[$i]->type = $type; - $retVal[$i]->type_name = isset($data_types[$type]) ? $data_types[$type] : null; + $retVal[$i]->type_name = isset($dataTypes[$type]) ? $dataTypes[$type] : null; $retVal[$i]->max_length = null; $retVal[$i]->length = null; } diff --git a/system/Debug/Iterator.php b/system/Debug/Iterator.php index c9101df11a31..ed8dadd34283 100644 --- a/system/Debug/Iterator.php +++ b/system/Debug/Iterator.php @@ -99,21 +99,21 @@ public function run(int $iterations = 1000, bool $output = true) // clear memory before start gc_collect_cycles(); - $start = microtime(true); - $start_mem = $max_memory = memory_get_usage(true); + $start = microtime(true); + $startMem = $maxMemory = memory_get_usage(true); for ($i = 0; $i < $iterations; $i ++) { $result = $test(); - $max_memory = max($max_memory, memory_get_usage(true)); + $maxMemory = max($maxMemory, memory_get_usage(true)); unset($result); } $this->results[$name] = [ 'time' => microtime(true) - $start, - 'memory' => $max_memory - $start_mem, + 'memory' => $maxMemory - $startMem, 'n' => $iterations, ]; } diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 5f5d7c43a6f1..8c826c0464f0 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -99,7 +99,7 @@ 89 -104 206 -162 247 -17 13 -18 12 -11 -15z"/> - + diff --git a/system/Email/Email.php b/system/Email/Email.php index 14ea1eb665d6..a86e6815411b 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -1208,27 +1208,27 @@ protected function buildMessage() $this->appendAttachments($body, $boundary); break; case 'html-attach': - $alt_boundary = uniqid('B_ALT_', true); - $last_boundary = null; + $altBoundary = uniqid('B_ALT_', true); + $lastBoundary = null; if ($this->attachmentsHaveMultipart('mixed')) { - $atc_boundary = uniqid('B_ATC_', true); - $hdr .= 'Content-Type: multipart/mixed; boundary="' . $atc_boundary . '"'; - $last_boundary = $atc_boundary; + $atcBoundary = uniqid('B_ATC_', true); + $hdr .= 'Content-Type: multipart/mixed; boundary="' . $atcBoundary . '"'; + $lastBoundary = $atcBoundary; } if ($this->attachmentsHaveMultipart('related')) { - $rel_boundary = uniqid('B_REL_', true); - $rel_boundary_header = 'Content-Type: multipart/related; boundary="' . $rel_boundary . '"'; - if (isset($last_boundary)) + $relBoundary = uniqid('B_REL_', true); + $relBoundaryHeader = 'Content-Type: multipart/related; boundary="' . $relBoundary . '"'; + if (isset($lastBoundary)) { - $body .= '--' . $last_boundary . $this->newline . $rel_boundary_header; + $body .= '--' . $lastBoundary . $this->newline . $relBoundaryHeader; } else { - $hdr .= $rel_boundary_header; + $hdr .= $relBoundaryHeader; } - $last_boundary = $rel_boundary; + $lastBoundary = $relBoundary; } if ($this->getProtocol() === 'mail') { @@ -1236,27 +1236,27 @@ protected function buildMessage() } static::strlen($body) && $body .= $this->newline . $this->newline; $body .= $this->getMimeMessage() . $this->newline . $this->newline - . '--' . $last_boundary . $this->newline - . 'Content-Type: multipart/alternative; boundary="' . $alt_boundary . '"' . $this->newline . $this->newline - . '--' . $alt_boundary . $this->newline + . '--' . $lastBoundary . $this->newline + . 'Content-Type: multipart/alternative; boundary="' . $altBoundary . '"' . $this->newline . $this->newline + . '--' . $altBoundary . $this->newline . 'Content-Type: text/plain; charset=' . $this->charset . $this->newline . 'Content-Transfer-Encoding: ' . $this->getEncoding() . $this->newline . $this->newline . $this->getAltMessage() . $this->newline . $this->newline - . '--' . $alt_boundary . $this->newline + . '--' . $altBoundary . $this->newline . 'Content-Type: text/html; charset=' . $this->charset . $this->newline . 'Content-Transfer-Encoding: quoted-printable' . $this->newline . $this->newline . $this->prepQuotedPrintable($this->body) . $this->newline . $this->newline - . '--' . $alt_boundary . '--' . $this->newline . $this->newline; - if (! empty($rel_boundary)) + . '--' . $altBoundary . '--' . $this->newline . $this->newline; + if (! empty($relBoundary)) { $body .= $this->newline . $this->newline; - $this->appendAttachments($body, $rel_boundary, 'related'); + $this->appendAttachments($body, $relBoundary, 'related'); } // multipart/mixed attachments - if (! empty($atc_boundary)) + if (! empty($atcBoundary)) { $body .= $this->newline . $this->newline; - $this->appendAttachments($body, $atc_boundary, 'mixed'); + $this->appendAttachments($body, $atcBoundary, 'mixed'); } break; } @@ -1321,7 +1321,7 @@ protected function prepQuotedPrintable($str) // ASCII code numbers for "safe" characters that can always be // used literally, without encoding, as described in RFC 2049. // http://www.ietf.org/rfc/rfc2049.txt - static $ascii_safe_chars = [ + static $asciiSafeChars = [ // ' ( ) + , - . / : = ? 39, 40, @@ -1448,7 +1448,7 @@ protected function prepQuotedPrintable($str) { $char = $escape . strtoupper(sprintf('%02s', dechex($ascii))); // =3D } - elseif (! in_array($ascii, $ascii_safe_chars, true)) + elseif (! in_array($ascii, $asciiSafeChars, true)) { $char = $escape . strtoupper(sprintf('%02s', dechex($ascii))); } @@ -2113,12 +2113,12 @@ public function printDebugger($include = ['headers', 'subject', 'body']) { $msg = implode('', $this->debugMessage); // Determine which parts of our raw data needs to be printed - $raw_data = ''; - is_array($include) || $include = [$include]; // @phpstan-ignore-line - in_array('headers', $include, true) && $raw_data = htmlspecialchars($this->headerStr) . "\n"; - in_array('subject', $include, true) && $raw_data .= htmlspecialchars($this->subject) . "\n"; - in_array('body', $include, true) && $raw_data .= htmlspecialchars($this->finalBody); - return $msg . ($raw_data === '' ? '' : '
' . $raw_data . '
'); + $rawData = ''; + is_array($include) || $include = [$include]; // @phpstan-ignore-line + in_array('headers', $include, true) && $rawData = htmlspecialchars($this->headerStr) . "\n"; + in_array('subject', $include, true) && $rawData .= htmlspecialchars($this->subject) . "\n"; + in_array('body', $include, true) && $rawData .= htmlspecialchars($this->finalBody); + return $msg . ($rawData === '' ? '' : '
' . $rawData . '
'); } //-------------------------------------------------------------------- /** diff --git a/system/Encryption/Handlers/OpenSSLHandler.php b/system/Encryption/Handlers/OpenSSLHandler.php index de65e616a3f9..488c501b9250 100644 --- a/system/Encryption/Handlers/OpenSSLHandler.php +++ b/system/Encryption/Handlers/OpenSSLHandler.php @@ -94,7 +94,7 @@ public function encrypt($data, $params = null) $secret = \hash_hkdf($this->digest, $this->key); // basic encryption - $iv = ($iv_size = \openssl_cipher_iv_length($this->cipher)) ? \openssl_random_pseudo_bytes($iv_size) : null; + $iv = ($ivSize = \openssl_cipher_iv_length($this->cipher)) ? \openssl_random_pseudo_bytes($ivSize) : null; $data = \openssl_encrypt($data, $this->cipher, $secret, OPENSSL_RAW_DATA, $iv); @@ -146,10 +146,10 @@ public function decrypt($data, $params = null) throw EncryptionException::forAuthenticationFailed(); } - if ($iv_size = \openssl_cipher_iv_length($this->cipher)) + if ($ivSize = \openssl_cipher_iv_length($this->cipher)) { - $iv = self::substr($data, 0, $iv_size); - $data = self::substr($data, $iv_size); + $iv = self::substr($data, 0, $ivSize); + $data = self::substr($data, $ivSize); } else { diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index 336f22bad093..33547796da34 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -418,7 +418,7 @@ public function getMethod(bool $upper = false): string public function send(string $method, string $url) { // Reset our curl options so we're on a fresh slate. - $curl_options = []; + $curlOptions = []; if (! empty($this->config['query']) && is_array($this->config['query'])) { @@ -429,16 +429,16 @@ public function send(string $method, string $url) unset($this->config['query']); } - $curl_options[CURLOPT_URL] = $url; - $curl_options[CURLOPT_RETURNTRANSFER] = true; - $curl_options[CURLOPT_HEADER] = true; - $curl_options[CURLOPT_FRESH_CONNECT] = true; + $curlOptions[CURLOPT_URL] = $url; + $curlOptions[CURLOPT_RETURNTRANSFER] = true; + $curlOptions[CURLOPT_HEADER] = true; + $curlOptions[CURLOPT_FRESH_CONNECT] = true; // Disable @file uploads in post data. - $curl_options[CURLOPT_SAFE_UPLOAD] = true; + $curlOptions[CURLOPT_SAFE_UPLOAD] = true; - $curl_options = $this->setCURLOptions($curl_options, $this->config); - $curl_options = $this->applyMethod($method, $curl_options); - $curl_options = $this->applyRequestHeaders($curl_options); + $curlOptions = $this->setCURLOptions($curlOptions, $this->config); + $curlOptions = $this->applyMethod($method, $curlOptions); + $curlOptions = $this->applyRequestHeaders($curlOptions); // Do we need to delay this request? if ($this->delay > 0) @@ -446,7 +446,7 @@ public function send(string $method, string $url) sleep($this->delay); // @phpstan-ignore-line } - $output = $this->sendRequest($curl_options); + $output = $this->sendRequest($curlOptions); // Set the string we want to break our response from $breakString = "\r\n\r\n"; diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index 37ae9bd17e4d..1baa30cdcc08 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -182,9 +182,9 @@ private function setContentTypeByMimeType() if ($this->setMime === true) { - if (($last_dot_position = strrpos($this->filename, '.')) !== false) + if (($lastDotPosition = strrpos($this->filename, '.')) !== false) { - $mime = Mimes::guessTypeFromExtension(substr($this->filename, $last_dot_position + 1)); + $mime = Mimes::guessTypeFromExtension(substr($this->filename, $lastDotPosition + 1)); $charset = $this->charset; } } @@ -234,20 +234,20 @@ private function getDownloadFileName(): string */ private function getContentDisposition() : string { - $download_filename = $this->getDownloadFileName(); + $downloadFilename = $this->getDownloadFileName(); - $utf8_filename = $download_filename; + $utf8Filename = $downloadFilename; if (strtoupper($this->charset) !== 'UTF-8') { - $utf8_filename = mb_convert_encoding($download_filename, 'UTF-8', $this->charset); + $utf8Filename = mb_convert_encoding($downloadFilename, 'UTF-8', $this->charset); } - $result = sprintf('attachment; filename="%s"', $download_filename); + $result = sprintf('attachment; filename="%s"', $downloadFilename); - if ($utf8_filename) + if ($utf8Filename) { - $result .= '; filename*=UTF-8\'\'' . rawurlencode($utf8_filename); + $result .= '; filename*=UTF-8\'\'' . rawurlencode($utf8Filename); } return $result; @@ -526,10 +526,10 @@ public function sendBody() */ private function sendBodyByFilePath() { - $spl_file_object = $this->file->openFile('rb'); + $splFileObject = $this->file->openFile('rb'); // Flush 1MB chunks of data - while (! $spl_file_object->eof() && ($data = $spl_file_object->fread(1048576)) !== false) + while (! $splFileObject->eof() && ($data = $splFileObject->fread(1048576)) !== false) { echo $data; } diff --git a/system/HTTP/Files/FileCollection.php b/system/HTTP/Files/FileCollection.php index fb62ccd036d6..7cdcc724f135 100644 --- a/system/HTTP/Files/FileCollection.php +++ b/system/HTTP/Files/FileCollection.php @@ -312,14 +312,14 @@ protected function getValueDotNotationSyntax(array $index, array $value) { if (! empty($index)) { - $current_index = array_shift($index); + $currentIndex = array_shift($index); } - if (isset($current_index) && is_array($index) && $index && is_array($value[$current_index]) && $value[$current_index]) + if (isset($currentIndex) && is_array($index) && $index && is_array($value[$currentIndex]) && $value[$currentIndex]) { - return $this->getValueDotNotationSyntax($index, $value[$current_index]); + return $this->getValueDotNotationSyntax($index, $value[$currentIndex]); } - return (isset($current_index) && isset($value[$current_index])) ? $value[$current_index] : null; + return (isset($currentIndex) && isset($value[$currentIndex])) ? $value[$currentIndex] : null; } } diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 3f58de9c15b1..0c70b4b278da 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -189,14 +189,14 @@ public function getHeaders(): array */ public function getHeader(string $name) { - $orig_name = $this->getHeaderName($name); + $origName = $this->getHeaderName($name); - if (! isset($this->headers[$orig_name])) + if (! isset($this->headers[$origName])) { return null; } - return $this->headers[$orig_name]; + return $this->headers[$origName]; } /** @@ -208,9 +208,9 @@ public function getHeader(string $name) */ public function hasHeader(string $name): bool { - $orig_name = $this->getHeaderName($name); + $origName = $this->getHeaderName($name); - return isset($this->headers[$orig_name]); + return isset($this->headers[$origName]); } /** @@ -230,14 +230,14 @@ public function hasHeader(string $name): bool */ public function getHeaderLine(string $name): string { - $orig_name = $this->getHeaderName($name); + $origName = $this->getHeaderName($name); - if (! array_key_exists($orig_name, $this->headers)) + if (! array_key_exists($origName, $this->headers)) { return ''; } - return $this->headers[$orig_name]->getValueLine(); + return $this->headers[$origName]->getValueLine(); } /** @@ -282,9 +282,9 @@ public function setHeader(string $name, $value): self */ public function removeHeader(string $name): self { - $orig_name = $this->getHeaderName($name); + $origName = $this->getHeaderName($name); - unset($this->headers[$orig_name]); + unset($this->headers[$origName]); unset($this->headerMap[strtolower($name)]); return $this; @@ -301,10 +301,10 @@ public function removeHeader(string $name): self */ public function appendHeader(string $name, ?string $value): self { - $orig_name = $this->getHeaderName($name); + $origName = $this->getHeaderName($name); - array_key_exists($orig_name, $this->headers) - ? $this->headers[$orig_name]->appendValue($value) + array_key_exists($origName, $this->headers) + ? $this->headers[$origName]->appendValue($value) : $this->setHeader($name, $value); return $this; @@ -321,9 +321,9 @@ public function appendHeader(string $name, ?string $value): self */ public function prependHeader(string $name, string $value): self { - $orig_name = $this->getHeaderName($name); + $origName = $this->getHeaderName($name); - $this->headers[$orig_name]->prependValue($value); + $this->headers[$origName]->prependValue($value); return $this; } diff --git a/system/HTTP/Negotiate.php b/system/HTTP/Negotiate.php index 793e1344945b..46ee46ac587e 100644 --- a/system/HTTP/Negotiate.php +++ b/system/HTTP/Negotiate.php @@ -297,8 +297,8 @@ public function parseHeader(string $header): array usort($results, function ($a, $b) { if ($a['q'] === $b['q']) { - $a_ast = substr_count($a['value'], '*'); - $b_ast = substr_count($b['value'], '*'); + $aAst = substr_count($a['value'], '*'); + $bAst = substr_count($b['value'], '*'); // '*/*' has lower precedence than 'text/*', // and 'text/*' has lower priority than 'text/plain' @@ -306,7 +306,7 @@ public function parseHeader(string $header): array // This seems backwards, but needs to be that way // due to the way PHP7 handles ordering or array // elements created by reference. - if ($a_ast > $b_ast) + if ($aAst > $bAst) { return 1; } @@ -317,7 +317,7 @@ public function parseHeader(string $header): array // This seems backwards, but needs to be that way // due to the way PHP7 handles ordering or array // elements created by reference. - if ($a_ast === $b_ast) + if ($aAst === $bAst) { return count($b['params']) - count($a['params']); } diff --git a/system/HTTP/Request.php b/system/HTTP/Request.php index 8c64f1623ed2..8e0ff0e0ac53 100644 --- a/system/HTTP/Request.php +++ b/system/HTTP/Request.php @@ -105,15 +105,15 @@ public function getIPAddress(): string return $this->ipAddress; } - $proxy_ips = $this->proxyIPs; + $proxyIps = $this->proxyIPs; if (! empty($this->proxyIPs) && ! is_array($this->proxyIPs)) { - $proxy_ips = explode(',', str_replace(' ', '', $this->proxyIPs)); + $proxyIps = explode(',', str_replace(' ', '', $this->proxyIPs)); } $this->ipAddress = $this->getServer('REMOTE_ADDR'); - if ($proxy_ips) + if ($proxyIps) { foreach (['HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP'] as $header) { @@ -137,14 +137,14 @@ public function getIPAddress(): string if ($spoof) { - foreach ($proxy_ips as $proxy_ip) + foreach ($proxyIps as $proxyIp) { // Check if we have an IP address or a subnet - if (strpos($proxy_ip, '/') === false) + if (strpos($proxyIp, '/') === false) { // An IP address (and not a subnet) is specified. // We can compare right away. - if ($proxy_ip === $this->ipAddress) + if ($proxyIp === $this->ipAddress) { $this->ipAddress = $spoof; break; @@ -158,7 +158,7 @@ public function getIPAddress(): string isset($separator) || $separator = $this->isValidIP($this->ipAddress, 'ipv6') ? ':' : '.'; // If the proxy entry doesn't match the IP protocol - skip it - if (strpos($proxy_ip, $separator) === false) // @phpstan-ignore-line + if (strpos($proxyIp, $separator) === false) // @phpstan-ignore-line { continue; } @@ -190,7 +190,7 @@ public function getIPAddress(): string } // Split the netmask length off the network address - sscanf($proxy_ip, '%[^/]/%d', $netaddr, $masklen); + sscanf($proxyIp, '%[^/]/%d', $netaddr, $masklen); // Again, an IPv6 address is most likely in a compressed form if ($separator === ':') // @phpstan-ignore-line diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 4f7777ac75f6..83ff93ea7455 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -238,10 +238,10 @@ public function isReferral(): bool } else { - $referer_host = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); - $own_host = parse_url(\base_url(), PHP_URL_HOST); + $refererHost = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); + $ownHost = parse_url(\base_url(), PHP_URL_HOST); - $this->referrer = ($referer_host && $referer_host !== $own_host); + $this->referrer = ($refererHost && $refererHost !== $ownHost); } } diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 54a18d502533..368803548792 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -68,7 +68,7 @@ function directory_map(string $source_dir, int $directory_depth = 0, bool $hidde $fp = opendir($source_dir); $fileData = []; - $new_depth = $directory_depth - 1; + $newDepth = $directory_depth - 1; $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; while (false !== ($file = readdir($fp))) @@ -81,9 +81,9 @@ function directory_map(string $source_dir, int $directory_depth = 0, bool $hidde is_dir($source_dir . $file) && $file .= DIRECTORY_SEPARATOR; - if (($directory_depth < 1 || $new_depth > 0) && is_dir($source_dir . $file)) + if (($directory_depth < 1 || $newDepth > 0) && is_dir($source_dir . $file)) { - $fileData[$file] = directory_map($source_dir . $file, $new_depth, $hidden); + $fileData[$file] = directory_map($source_dir . $file, $newDepth, $hidden); } else { @@ -290,7 +290,7 @@ function get_filenames(string $source_dir, ?bool $include_path = false, bool $hi function get_dir_file_info(string $source_dir, bool $top_level_only = true, bool $recursion = false): array { static $fileData = []; - $relative_path = $source_dir; + $relativePath = $source_dir; try { @@ -312,7 +312,7 @@ function get_dir_file_info(string $source_dir, bool $top_level_only = true, bool elseif ($file[0] !== '.') { $fileData[$file] = get_file_info($source_dir . $file); - $fileData[$file]['relative_path'] = $relative_path; + $fileData[$file]['relative_path'] = $relativePath; } } diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 554c54940984..e6aefb76f47c 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -421,11 +421,11 @@ function form_dropdown($data = '', $options = [], $selected = [], $extra = ''): continue; } $form .= '\n"; - foreach ($val as $optgroup_key => $optgroup_val) + foreach ($val as $optgroupKey => $optgroupVal) { - $sel = in_array($optgroup_key, $selected, true) ? ' selected="selected"' : ''; - $form .= '\n"; + $sel = in_array($optgroupKey, $selected, true) ? ' selected="selected"' : ''; + $form .= '\n"; } $form .= "\n"; } diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 53ba7b0b7747..85455a7b0be6 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -109,10 +109,10 @@ function _list(string $type = 'ul', $list = [], $attributes = '', int $depth = 0 // Cycle through the list elements. If an array is // encountered we will recursively call _list() - static $_last_list_item = ''; + static $lastListItem = ''; foreach ($list as $key => $val) { - $_last_list_item = $key; + $lastListItem = $key; $out .= str_repeat(' ', $depth + 2) . '
  • '; @@ -122,7 +122,7 @@ function _list(string $type = 'ul', $list = [], $attributes = '', int $depth = 0 } else { - $out .= $_last_list_item + $out .= $lastListItem . "\n" . _list($type, $val, '', $depth + 4) . str_repeat(' ', $depth + 2); diff --git a/system/Helpers/number_helper.php b/system/Helpers/number_helper.php index 57d3db70a0d6..e8d253cb4d00 100644 --- a/system/Helpers/number_helper.php +++ b/system/Helpers/number_helper.php @@ -280,25 +280,25 @@ function number_to_roman(string $num): ?string return null; } - $_number_to_roman = function ($num, $th) use (&$_number_to_roman) { + $numberToRoman = function ($num, $th) use (&$numberToRoman) { $return = ''; $key1 = null; $key2 = null; switch ($th) { case 1: - $key1 = 'I'; - $key2 = 'V'; - $key_f = 'X'; + $key1 = 'I'; + $key2 = 'V'; + $keyF = 'X'; break; case 2: - $key1 = 'X'; - $key2 = 'L'; - $key_f = 'C'; + $key1 = 'X'; + $key2 = 'L'; + $keyF = 'C'; break; case 3: - $key1 = 'C'; - $key2 = 'D'; - $key_f = 'M'; + $key1 = 'C'; + $key2 = 'D'; + $keyF = 'M'; break; case 4: $key1 = 'M'; @@ -323,20 +323,20 @@ function number_to_roman(string $num): ?string $return = $key2 . str_repeat($key1, $n - 5); break; case 9: - $return = $key1 . $key_f; // @phpstan-ignore-line + $return = $key1 . $keyF; // @phpstan-ignore-line break; } switch ($num) { case 10: - $return = $key_f; // @phpstan-ignore-line + $return = $keyF; // @phpstan-ignore-line break; } if ($num > 10) { - $return = $_number_to_roman($num / 10, ++ $th) . $return; + $return = $numberToRoman($num / 10, ++ $th) . $return; } return $return; }; - return $_number_to_roman($num, 1); + return $numberToRoman($num, 1); } } diff --git a/system/Helpers/text_helper.php b/system/Helpers/text_helper.php index 6f4ef5b413ab..0dfababbdc47 100755 --- a/system/Helpers/text_helper.php +++ b/system/Helpers/text_helper.php @@ -423,26 +423,26 @@ function highlight_phrase(string $str, string $phrase, string $tag_open = 'characterList) || ! is_array($config->characterList)) { - $array_from = []; - $array_to = []; + $arrayFrom = []; + $arrayTo = []; return $str; } - $array_from = array_keys($config->characterList); - $array_to = array_values($config->characterList); + $arrayFrom = array_keys($config->characterList); + $arrayTo = array_values($config->characterList); unset($config); } - return preg_replace($array_from, $array_to, $str); + return preg_replace($arrayFrom, $arrayTo, $str); } } diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index 7ad2ad84a7a4..38c5d2ac3bfa 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -270,13 +270,13 @@ function anchor($uri = '', string $title = '', $attributes = '', \Config\App $al // use alternate config if provided, else default one $config = $altConfig ?? config(\Config\App::class); - $site_url = is_array($uri) ? site_url($uri, null, $config) : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null, $config)); + $siteUrl = is_array($uri) ? site_url($uri, null, $config) : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null, $config)); // eliminate trailing slash - $site_url = rtrim($site_url, '/'); + $siteUrl = rtrim($siteUrl, '/'); if ($title === '') { - $title = $site_url; + $title = $siteUrl; } if ($attributes !== '') @@ -284,7 +284,7 @@ function anchor($uri = '', string $title = '', $attributes = '', \Config\App $al $attributes = stringify_attributes($attributes); } - return '' . $title . ''; + return '' . $title . ''; } } @@ -310,17 +310,17 @@ function anchor_popup($uri = '', string $title = '', $attributes = false, \Confi // use alternate config if provided, else default one $config = $altConfig ?? config(\Config\App::class); - $site_url = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, '', $config); - $site_url = rtrim($site_url, '/'); + $siteUrl = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, '', $config); + $siteUrl = rtrim($siteUrl, '/'); if ($title === '') { - $title = $site_url; + $title = $siteUrl; } if ($attributes === false) { - return '" . $title . ''; + return '" . $title . ''; } if (! is_array($attributes)) @@ -328,16 +328,16 @@ function anchor_popup($uri = '', string $title = '', $attributes = false, \Confi $attributes = [$attributes]; // Ref: http://www.w3schools.com/jsref/met_win_open.asp - $window_name = '_blank'; + $windowName = '_blank'; } elseif (! empty($attributes['window_name'])) { - $window_name = $attributes['window_name']; + $windowName = $attributes['window_name']; unset($attributes['window_name']); } else { - $window_name = '_blank'; + $windowName = '_blank'; } foreach (['width' => '800', 'height' => '600', 'scrollbars' => 'yes', 'menubar' => 'no', 'status' => 'yes', 'resizable' => 'yes', 'screenx' => '0', 'screeny' => '0'] as $key => $val) @@ -348,8 +348,8 @@ function anchor_popup($uri = '', string $title = '', $attributes = false, \Confi $attributes = stringify_attributes($attributes); - return '' . $title . ''; } } @@ -591,13 +591,13 @@ function prep_url(string $str = ''): string */ function url_title(string $str, string $separator = '-', bool $lowercase = false): string { - $q_separator = preg_quote($separator, '#'); + $qSeparator = preg_quote($separator, '#'); $trans = [ - '&.+?;' => '', - '[^\w\d\pL\pM _-]' => '', - '\s+' => $separator, - '(' . $q_separator . ')+' => $separator, + '&.+?;' => '', + '[^\w\d\pL\pM _-]' => '', + '\s+' => $separator, + '(' . $qSeparator . ')+' => $separator, ]; $str = strip_tags($str); diff --git a/system/Honeypot/Honeypot.php b/system/Honeypot/Honeypot.php index 9bdfff107793..2dffd6abad48 100644 --- a/system/Honeypot/Honeypot.php +++ b/system/Honeypot/Honeypot.php @@ -108,10 +108,10 @@ public function hasContent(RequestInterface $request) */ public function attachHoneypot(ResponseInterface $response) { - $prep_field = $this->prepareTemplate($this->config->template); + $prepField = $this->prepareTemplate($this->config->template); $body = $response->getBody(); - $body = str_ireplace('', $prep_field . '', $body); + $body = str_ireplace('', $prepField . '', $body); $response->setBody($body); } diff --git a/system/Images/Handlers/GDHandler.php b/system/Images/Handlers/GDHandler.php index a27a337d8182..860b0e1ef271 100644 --- a/system/Images/Handlers/GDHandler.php +++ b/system/Images/Handlers/GDHandler.php @@ -171,9 +171,9 @@ public function getVersion() { if (function_exists('gd_info')) { - $gd_version = @gd_info(); + $gdVersion = @gd_info(); - return preg_replace('/\D/', '', $gd_version['GD Version']); + return preg_replace('/\D/', '', $gdVersion['GD Version']); } return false; diff --git a/system/Log/Handlers/FileHandler.php b/system/Log/Handlers/FileHandler.php index 85cb95768837..2a478840c576 100644 --- a/system/Log/Handlers/FileHandler.php +++ b/system/Log/Handlers/FileHandler.php @@ -124,10 +124,10 @@ public function handle($level, $message): bool // Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format if (strpos($this->dateFormat, 'u') !== false) { - $microtime_full = microtime(true); - $microtime_short = sprintf('%06d', ($microtime_full - floor($microtime_full)) * 1000000); - $date = new \DateTime(date('Y-m-d H:i:s.' . $microtime_short, (int) $microtime_full)); - $date = $date->format($this->dateFormat); + $microtimeFull = microtime(true); + $microtimeShort = sprintf('%06d', ($microtimeFull - floor($microtimeFull)) * 1000000); + $date = new \DateTime(date('Y-m-d H:i:s.' . $microtimeShort, (int) $microtimeFull)); + $date = $date->format($this->dateFormat); } else { diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 77491b1fe580..a06c56a4f5a4 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -808,12 +808,12 @@ public function resource(string $name, array $options = null): RouteCollectionIn // In order to allow customization of the route the // resources are sent to, we need to have a new name // to store the values in. - $new_name = implode('\\', array_map('ucfirst', explode('/', $name))); + $newName = implode('\\', array_map('ucfirst', explode('/', $name))); // If a new controller is specified, then we replace the // $name value with the name of the new controller. if (isset($options['controller'])) { - $new_name = ucfirst(filter_var($options['controller'], FILTER_SANITIZE_STRING)); + $newName = ucfirst(filter_var($options['controller'], FILTER_SANITIZE_STRING)); } // In order to allow customization of allowed id values @@ -844,32 +844,32 @@ public function resource(string $name, array $options = null): RouteCollectionIn if (in_array('index', $methods, true)) { - $this->get($name, $new_name . '::index', $options); + $this->get($name, $newName . '::index', $options); } if (in_array('new', $methods, true)) { - $this->get($name . '/new', $new_name . '::new', $options); + $this->get($name . '/new', $newName . '::new', $options); } if (in_array('edit', $methods, true)) { - $this->get($name . '/' . $id . '/edit', $new_name . '::edit/$1', $options); + $this->get($name . '/' . $id . '/edit', $newName . '::edit/$1', $options); } if (in_array('show', $methods, true)) { - $this->get($name . '/' . $id, $new_name . '::show/$1', $options); + $this->get($name . '/' . $id, $newName . '::show/$1', $options); } if (in_array('create', $methods, true)) { - $this->post($name, $new_name . '::create', $options); + $this->post($name, $newName . '::create', $options); } if (in_array('update', $methods, true)) { - $this->put($name . '/' . $id, $new_name . '::update/$1', $options); - $this->patch($name . '/' . $id, $new_name . '::update/$1', $options); + $this->put($name . '/' . $id, $newName . '::update/$1', $options); + $this->patch($name . '/' . $id, $newName . '::update/$1', $options); } if (in_array('delete', $methods, true)) { - $this->delete($name . '/' . $id, $new_name . '::delete/$1', $options); + $this->delete($name . '/' . $id, $newName . '::delete/$1', $options); } // Web Safe? delete needs checking before update because of method name @@ -877,11 +877,11 @@ public function resource(string $name, array $options = null): RouteCollectionIn { if (in_array('delete', $methods, true)) { - $this->post($name . '/' . $id . '/delete', $new_name . '::delete/$1', $options); + $this->post($name . '/' . $id . '/delete', $newName . '::delete/$1', $options); } if (in_array('update', $methods, true)) { - $this->post($name . '/' . $id, $new_name . '::update/$1', $options); + $this->post($name . '/' . $id, $newName . '::update/$1', $options); } } diff --git a/system/Router/Router.php b/system/Router/Router.php index abf91e26379f..aa44a77e77f1 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -614,8 +614,8 @@ protected function validateRequest(array $segments): array }); $segments = array_values($segments); - $c = count($segments); - $directory_override = isset($this->directory); + $c = count($segments); + $directoryOverride = isset($this->directory); // Loop through our segments and return as soon as a controller // is found or when such a directory doesn't exist @@ -623,7 +623,7 @@ protected function validateRequest(array $segments): array { $test = $this->directory . ucfirst($this->translateURIDashes === true ? str_replace('-', '_', $segments[0]) : $segments[0]); - if (! is_file(APPPATH . 'Controllers/' . $test . '.php') && $directory_override === false && is_dir(APPPATH . 'Controllers/' . $this->directory . ucfirst($segments[0]))) + if (! is_file(APPPATH . 'Controllers/' . $test . '.php') && $directoryOverride === false && is_dir(APPPATH . 'Controllers/' . $this->directory . ucfirst($segments[0]))) { $this->setDirectory(array_shift($segments), true); continue; diff --git a/system/Security/Security.php b/system/Security/Security.php index 3bc3a261e3d1..906e81da9a1f 100644 --- a/system/Security/Security.php +++ b/system/Security/Security.php @@ -288,10 +288,10 @@ public function CSRFVerify(RequestInterface $request) */ public function CSRFSetCookie(RequestInterface $request) { - $expire = $this->CSRFExpire === 0 ? $this->CSRFExpire : time() + $this->CSRFExpire; - $secure_cookie = (bool) $this->cookieSecure; + $expire = $this->CSRFExpire === 0 ? $this->CSRFExpire : time() + $this->CSRFExpire; + $secureCookie = (bool) $this->cookieSecure; - if ($secure_cookie && ! $request->isSecure()) + if ($secureCookie && ! $request->isSecure()) { return false; } @@ -311,7 +311,7 @@ public function CSRFSetCookie(RequestInterface $request) $expire, $this->cookiePath . $samesite, $this->cookieDomain, - $secure_cookie, + $secureCookie, true // Enforce HTTP only cookie for security ); } @@ -322,7 +322,7 @@ public function CSRFSetCookie(RequestInterface $request) 'expires' => $expire, 'path' => $this->cookiePath, 'domain' => $this->cookieDomain, - 'secure' => $secure_cookie, + 'secure' => $secureCookie, 'httponly' => true,// Enforce HTTP only cookie for security ]; diff --git a/system/Session/Handlers/FileHandler.php b/system/Session/Handlers/FileHandler.php index a63c39cff514..ec38183896fd 100644 --- a/system/Session/Handlers/FileHandler.php +++ b/system/Session/Handlers/FileHandler.php @@ -213,7 +213,7 @@ public function read($sessionID) rewind($this->fileHandle); } - $session_data = ''; + $sessionData = ''; clearstatcache(); // Address https://github.com/codeigniter4/CodeIgniter4/issues/2056 for ($read = 0, $length = filesize($this->filePath . $sessionID); $read < $length; $read += strlen($buffer)) { @@ -222,12 +222,12 @@ public function read($sessionID) break; } - $session_data .= $buffer; + $sessionData .= $buffer; } - $this->fingerprint = md5($session_data); + $this->fingerprint = md5($sessionData); - return $session_data; + return $sessionData; } //-------------------------------------------------------------------- diff --git a/system/Session/Handlers/MemcachedHandler.php b/system/Session/Handlers/MemcachedHandler.php index 51537982274f..95a786901bb2 100644 --- a/system/Session/Handlers/MemcachedHandler.php +++ b/system/Session/Handlers/MemcachedHandler.php @@ -123,11 +123,11 @@ public function open($save_path, $name): bool $this->memcached = new \Memcached(); $this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); // required for touch() usage - $server_list = []; + $serverList = []; foreach ($this->memcached->getServerList() as $server) { - $server_list[] = $server['host'] . ':' . $server['port']; + $serverList[] = $server['host'] . ':' . $server['port']; } if (! preg_match_all('#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->savePath, $matches, PREG_SET_ORDER) @@ -142,7 +142,7 @@ public function open($save_path, $name): bool foreach ($matches as $match) { // If Memcached already has this server (or if the port is invalid), skip it - if (in_array($match[1] . ':' . $match[2], $server_list, true)) + if (in_array($match[1] . ':' . $match[2], $serverList, true)) { $this->logger->debug('Session: Memcached server pool already has ' . $match[1] . ':' . $match[2]); continue; @@ -154,11 +154,11 @@ public function open($save_path, $name): bool } else { - $server_list[] = $match[1] . ':' . $match[2]; + $serverList[] = $match[1] . ':' . $match[2]; } } - if (empty($server_list)) + if (empty($serverList)) { $this->logger->error('Session: Memcached server pool is empty.'); @@ -189,10 +189,10 @@ public function read($sessionID): string $this->sessionID = $sessionID; } - $session_data = (string) $this->memcached->get($this->keyPrefix . $sessionID); - $this->fingerprint = md5($session_data); + $sessionData = (string) $this->memcached->get($this->keyPrefix . $sessionID); + $this->fingerprint = md5($sessionData); - return $session_data; + return $sessionData; } return ''; @@ -338,25 +338,25 @@ protected function lockSession(string $sessionID): bool } // 30 attempts to obtain a lock, in case another request already has it - $lock_key = $this->keyPrefix . $sessionID . ':lock'; - $attempt = 0; + $lockKey = $this->keyPrefix . $sessionID . ':lock'; + $attempt = 0; do { - if ($this->memcached->get($lock_key)) + if ($this->memcached->get($lockKey)) { sleep(1); continue; } - if (! $this->memcached->set($lock_key, time(), 300)) + if (! $this->memcached->set($lockKey, time(), 300)) { $this->logger->error('Session: Error while trying to obtain lock for ' . $this->keyPrefix . $sessionID); return false; } - $this->lockKey = $lock_key; + $this->lockKey = $lockKey; break; } while (++ $attempt < 30); diff --git a/system/Session/Handlers/RedisHandler.php b/system/Session/Handlers/RedisHandler.php index 1b4370f9350d..82224e49d8b7 100644 --- a/system/Session/Handlers/RedisHandler.php +++ b/system/Session/Handlers/RedisHandler.php @@ -194,12 +194,12 @@ public function read($sessionID): string $this->sessionID = $sessionID; } - $session_data = $this->redis->get($this->keyPrefix . $sessionID); - is_string($session_data) ? $this->keyExists = true : $session_data = ''; + $sessionData = $this->redis->get($this->keyPrefix . $sessionID); + is_string($sessionData) ? $this->keyExists = true : $sessionData = ''; - $this->fingerprint = md5($session_data); + $this->fingerprint = md5($sessionData); - return $session_data; + return $sessionData; } return ''; @@ -273,9 +273,9 @@ public function close(): bool { try { - $ping_reply = $this->redis->ping(); + $pingReply = $this->redis->ping(); // @phpstan-ignore-next-line - if (($ping_reply === true) || ($ping_reply === '+PONG')) + if (($pingReply === true) || ($pingReply === '+PONG')) { isset($this->lockKey) && $this->redis->del($this->lockKey); @@ -362,24 +362,24 @@ protected function lockSession(string $sessionID): bool } // 30 attempts to obtain a lock, in case another request already has it - $lock_key = $this->keyPrefix . $sessionID . ':lock'; - $attempt = 0; + $lockKey = $this->keyPrefix . $sessionID . ':lock'; + $attempt = 0; do { - if (($ttl = $this->redis->ttl($lock_key)) > 0) + if (($ttl = $this->redis->ttl($lockKey)) > 0) { sleep(1); continue; } - if (! $this->redis->setex($lock_key, 300, (string) time())) + if (! $this->redis->setex($lockKey, 300, (string) time())) { $this->logger->error('Session: Error while trying to obtain lock for ' . $this->keyPrefix . $sessionID); return false; } - $this->lockKey = $lock_key; + $this->lockKey = $lockKey; break; } while (++ $attempt < 30); diff --git a/system/Session/Session.php b/system/Session/Session.php index f5ce19b75b64..d9d57dc14510 100644 --- a/system/Session/Session.php +++ b/system/Session/Session.php @@ -252,14 +252,14 @@ public function start() // Is session ID auto-regeneration configured? (ignoring ajax requests) if ((empty($_SERVER['HTTP_X_REQUESTED_WITH']) || - strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') && ($regenerate_time = $this->sessionTimeToUpdate) > 0 + strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') && ($regenerateTime = $this->sessionTimeToUpdate) > 0 ) { if (! isset($_SESSION['__ci_last_regenerate'])) { $_SESSION['__ci_last_regenerate'] = time(); } - elseif ($_SESSION['__ci_last_regenerate'] < (time() - $regenerate_time)) + elseif ($_SESSION['__ci_last_regenerate'] < (time() - $regenerateTime)) { $this->regenerate((bool) $this->sessionRegenerateDestroy); } @@ -393,22 +393,22 @@ protected function configure() */ protected function configureSidLength() { - $bits_per_character = (int) (ini_get('session.sid_bits_per_character') !== false + $bitsPerCharacter = (int) (ini_get('session.sid_bits_per_character') !== false ? ini_get('session.sid_bits_per_character') : 4); - $sid_length = (int) (ini_get('session.sid_length') !== false + $sidLength = (int) (ini_get('session.sid_length') !== false ? ini_get('session.sid_length') : 40); - if (($sid_length * $bits_per_character) < 160) + if (($sidLength * $bitsPerCharacter) < 160) { - $bits = ($sid_length * $bits_per_character); + $bits = ($sidLength * $bitsPerCharacter); // Add as many more characters as necessary to reach at least 160 bits - $sid_length += (int) ceil((160 % $bits) / $bits_per_character); - ini_set('session.sid_length', (string) $sid_length); + $sidLength += (int) ceil((160 % $bits) / $bitsPerCharacter); + ini_set('session.sid_length', (string) $sidLength); } // Yes, 4,5,6 are the only known possible values as of 2016-10-27 - switch ($bits_per_character) + switch ($bitsPerCharacter) { case 4: $this->sidRegexp = '[0-9a-f]'; @@ -421,7 +421,7 @@ protected function configureSidLength() break; } - $this->sidRegexp .= '{' . $sid_length . '}'; + $this->sidRegexp .= '{' . $sidLength . '}'; } //-------------------------------------------------------------------- @@ -439,7 +439,7 @@ protected function initVars() return; } - $current_time = time(); + $currentTime = time(); foreach ($_SESSION['__ci_vars'] as $key => &$value) { @@ -449,7 +449,7 @@ protected function initVars() } // Hacky, but 'old' will (implicitly) always be less than time() ;) // DO NOT move this above the 'new' check! - elseif ($value < $current_time) + elseif ($value < $currentTime) { unset($_SESSION[$key], $_SESSION['__ci_vars'][$key]); } @@ -558,14 +558,14 @@ public function get(string $key = null) } $userdata = []; - $_exclude = array_merge( + $exclude = array_merge( ['__ci_vars'], $this->getFlashKeys(), $this->getTempKeys() ); $keys = array_keys($_SESSION); foreach ($keys as $key) { - if (! in_array($key, $_exclude, true)) + if (! in_array($key, $exclude, true)) { $userdata[$key] = $_SESSION[$key]; } diff --git a/system/Test/Mock/MockCommon.php b/system/Test/Mock/MockCommon.php index 21fd54261401..3778ad1bf9ae 100644 --- a/system/Test/Mock/MockCommon.php +++ b/system/Test/Mock/MockCommon.php @@ -23,14 +23,14 @@ function is_cli(bool $new_return = null): bool { // PHPUnit always runs via CLI. - static $return_value = true; + static $returnValue = true; if ($new_return !== null) { - $return_value = $new_return; + $returnValue = $new_return; } - return $return_value; + return $returnValue; } } diff --git a/system/Test/ReflectionHelper.php b/system/Test/ReflectionHelper.php index bb52908603b1..a780e0ae5e8b 100644 --- a/system/Test/ReflectionHelper.php +++ b/system/Test/ReflectionHelper.php @@ -59,13 +59,13 @@ trait ReflectionHelper */ public static function getPrivateMethodInvoker($obj, $method) { - $ref_method = new ReflectionMethod($obj, $method); - $ref_method->setAccessible(true); + $refMethod = new ReflectionMethod($obj, $method); + $refMethod->setAccessible(true); $obj = (gettype($obj) === 'object') ? $obj : null; - return function () use ($obj, $ref_method) { + return function () use ($obj, $refMethod) { $args = func_get_args(); - return $ref_method->invokeArgs($obj, $args); + return $refMethod->invokeArgs($obj, $args); }; } @@ -82,17 +82,17 @@ private static function getAccessibleRefProperty($obj, $property) { if (is_object($obj)) { - $ref_class = new ReflectionObject($obj); + $refClass = new ReflectionObject($obj); } else { - $ref_class = new ReflectionClass($obj); + $refClass = new ReflectionClass($obj); } - $ref_property = $ref_class->getProperty($property); - $ref_property->setAccessible(true); + $refProperty = $refClass->getProperty($property); + $refProperty->setAccessible(true); - return $ref_property; + return $refProperty; } /** @@ -106,8 +106,8 @@ private static function getAccessibleRefProperty($obj, $property) */ public static function setPrivateProperty($obj, $property, $value) { - $ref_property = self::getAccessibleRefProperty($obj, $property); - $ref_property->setValue($obj, $value); + $refProperty = self::getAccessibleRefProperty($obj, $property); + $refProperty->setValue($obj, $value); } /** @@ -121,8 +121,8 @@ public static function setPrivateProperty($obj, $property, $value) */ public static function getPrivateProperty($obj, $property) { - $ref_property = self::getAccessibleRefProperty($obj, $property); - return $ref_property->getValue($obj); + $refProperty = self::getAccessibleRefProperty($obj, $property); + return $refProperty->getValue($obj); } } diff --git a/system/Test/TestLogger.php b/system/Test/TestLogger.php index 450d15b8f021..8fb6bf1cf03f 100644 --- a/system/Test/TestLogger.php +++ b/system/Test/TestLogger.php @@ -23,7 +23,7 @@ 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); + $logMessage = $this->interpolate($message, $context); // Determine the file and line by finding the first // backtrace that is not part of our logging system. @@ -41,7 +41,7 @@ public function log($level, $message, array $context = []): bool self::$op_logs[] = [ 'level' => $level, - 'message' => $log_message, + 'message' => $logMessage, 'file' => $file, ]; diff --git a/system/Typography/Typography.php b/system/Typography/Typography.php index 8b60a57793cb..5643d7cf4b06 100644 --- a/system/Typography/Typography.php +++ b/system/Typography/Typography.php @@ -124,13 +124,13 @@ public function autoTypography(string $str, bool $reduce_linebreaks = false): st } // HTML comment tags don't conform to patterns of normal tags, so pull them out separately, only if needed - $html_comments = []; + $htmlComments = []; if (strpos($str, '