From 152767d81e79a138d27dab988318f8a0628598dc Mon Sep 17 00:00:00 2001 From: ping-yee <611077101@mail.nknu.edu.tw> Date: Mon, 15 Aug 2022 16:33:14 +0800 Subject: [PATCH 01/77] fix: add a new param in processRules and getErrorMessage function to judge if validation fields have asterisk --- system/Validation/Validation.php | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index 7e4a55db55cf..ed370a2f5c08 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -168,7 +168,7 @@ public function run(?array $data = null, ?string $group = null, ?string $dbGroup if (strpos($field, '*') !== false) { // Process multiple fields foreach ($values as $dotField => $value) { - $this->processRules($dotField, $setup['label'] ?? $field, $value, $rules, $data); + $this->processRules($dotField, $setup['label'] ?? $field, $value, $rules, $data, $field); } } else { // Process single field @@ -201,10 +201,17 @@ public function check($value, string $rule, array $errors = []): bool * * @param array|string $value * @param array|null $rules - * @param array $data + * @param array $data The array of data to validate, with `DBGroup`. + * @param string|null $originalField The original asterisk field name like "foo.*.bar". */ - protected function processRules(string $field, ?string $label, $value, $rules = null, ?array $data = null): bool - { + protected function processRules( + string $field, + ?string $label, + $value, + $rules = null, + ?array $data = null, + ?string $originalField = null + ): bool { if ($data === null) { throw new InvalidArgumentException('You must supply the parameter: data.'); } @@ -333,7 +340,8 @@ protected function processRules(string $field, ?string $label, $value, $rules = $field, $label, $param, - (string) $value + (string) $value, + $originalField ); return false; @@ -706,13 +714,21 @@ public function setError(string $field, string $error): ValidationInterface * * @param string|null $value The value that caused the validation to fail. */ - protected function getErrorMessage(string $rule, string $field, ?string $label = null, ?string $param = null, ?string $value = null): string - { + protected function getErrorMessage( + string $rule, + string $field, + ?string $label = null, + ?string $param = null, + ?string $value = null, + ?string $originalField = null + ): string { $param ??= ''; // Check if custom message has been defined by user if (isset($this->customErrors[$field][$rule])) { $message = lang($this->customErrors[$field][$rule]); + } elseif (null !== $originalField && isset($this->customErrors[$originalField][$rule])) { + $message = lang($this->customErrors[$originalField][$rule]); } else { // Try to grab a localized version of the message... // lang() will return the rule name back if not found, From 9888bba6143194a7a576d6e2665edd22eb776718 Mon Sep 17 00:00:00 2001 From: ping-yee <611077101@mail.nknu.edu.tw> Date: Mon, 15 Aug 2022 16:41:45 +0800 Subject: [PATCH 02/77] add : add a description to the changelog in v4.2.5. --- user_guide_src/source/changelogs/v4.2.5.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/changelogs/v4.2.5.rst b/user_guide_src/source/changelogs/v4.2.5.rst index a670705e4153..e4c45e70cfda 100644 --- a/user_guide_src/source/changelogs/v4.2.5.rst +++ b/user_guide_src/source/changelogs/v4.2.5.rst @@ -14,6 +14,7 @@ BREAKING - The method signature of ``BaseConnection::tableExists()`` has been changed. A second optional parameter ``$cached`` was added. This directs whether to use cache data or not. Default is ``true``, use cache data. - The abstract method signature of ``BaseBuilder::_listTables()`` has been changed. A second optional parameter ``$tableName`` was added. Providing a table name will generate SQL listing only that table. +- The method signature of ``Validation.php::processRules()`` and ``Validation.php::getErrorMessage()`` have been changed. Both of these methods add new ``$originalField`` parameter. Enhancements ************ From 574feb7d86d1fb982317ef25c867639ffcd1c3ef Mon Sep 17 00:00:00 2001 From: ping-yee <611077101@mail.nknu.edu.tw> Date: Wed, 17 Aug 2022 02:27:36 +0800 Subject: [PATCH 03/77] changelog: change changelog --- user_guide_src/source/changelogs/v4.2.5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.2.5.rst b/user_guide_src/source/changelogs/v4.2.5.rst index e4c45e70cfda..eb9c8f7ec643 100644 --- a/user_guide_src/source/changelogs/v4.2.5.rst +++ b/user_guide_src/source/changelogs/v4.2.5.rst @@ -14,7 +14,7 @@ BREAKING - The method signature of ``BaseConnection::tableExists()`` has been changed. A second optional parameter ``$cached`` was added. This directs whether to use cache data or not. Default is ``true``, use cache data. - The abstract method signature of ``BaseBuilder::_listTables()`` has been changed. A second optional parameter ``$tableName`` was added. Providing a table name will generate SQL listing only that table. -- The method signature of ``Validation.php::processRules()`` and ``Validation.php::getErrorMessage()`` have been changed. Both of these methods add new ``$originalField`` parameter. +- The method signature of ``Validation::processRules()`` and ``Validation::getErrorMessage()`` have been changed. Both of these methods add new ``$originalField`` parameter. Enhancements ************ From 964fe7183d9a8b70669c537d41eabdbc0df6e11e Mon Sep 17 00:00:00 2001 From: ping-yee <611077101@mail.nknu.edu.tw> Date: Sat, 20 Aug 2022 01:31:40 +0800 Subject: [PATCH 04/77] test: add test for validation error with asterisk field. --- tests/system/Validation/ValidationTest.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index c6339139044d..1d2049a9d965 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -463,6 +463,28 @@ public function testRunGroupWithCustomErrorMessage(): void ], $this->validation->getErrors()); } + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/6245 + */ + public function testRunWithCustomErrorsAndAsteriskField(): void + { + $data = [ + 'foo' => [ + ['bar' => null], + ['bar' => null], + ] + ]; + $this->validation->setRules( + ['foo.*.bar' => ['label' => 'foo bar', 'rules' => 'required']], + ['foo.*.bar' => ['required' => 'Required']] + ); + $this->validation->run($data); + $this->assertSame([ + 'foo.0.bar' => 'Required', + 'foo.1.bar' => 'Required' + ], $this->validation->getErrors()); + } + /** * @dataProvider rulesSetupProvider * From 8b4b16a2e5ec4948add6fc4e879356d53fa64027 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 19 Aug 2022 17:57:28 -0700 Subject: [PATCH 05/77] Update Table.php --- system/Database/SQLite3/Table.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 1ecb28fdf8b7..99d14f408fe1 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -240,6 +240,14 @@ protected function createTable() $this->forge->addField($fields); + $fieldNames = array_keys($fields); + + $this->keys = array_filter( + $this->keys, + static fn ($index) => count(array_intersect($index['fields'], $fieldNames)) + === count($index['fields']) + ); + // Unique/Index keys if (is_array($this->keys)) { foreach ($this->keys as $key) { From 1cc0f9b974024013c08bb57c63ca108045a261c2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 21 Aug 2022 08:39:47 +0900 Subject: [PATCH 06/77] test: refactor tests for getFieldData() --- tests/system/Database/Live/ForgeTest.php | 206 ++++++++++++++++++----- 1 file changed, 162 insertions(+), 44 deletions(-) diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 11fb663787c9..8fc646872d25 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -866,64 +866,182 @@ public function testAddFields() $this->assertIsArray($fieldsNames); - $fields = ['id', 'name', 'username', 'active']; - $this->assertContains($fieldsData[0]->name, $fields); - $this->assertContains($fieldsData[1]->name, $fields); - unset($fields); - if ($this->db->DBDriver === 'MySQLi') { - $this->assertSame('int', $fieldsData[0]->type); - $this->assertSame('varchar', $fieldsData[1]->type); + $expected = [ + 0 => [ + 'name' => 'id', + 'type' => 'int', + 'max_length' => 11, + 'nullable' => false, + 'default' => null, + 'primary_key' => 1, + ], + 1 => [ + 'name' => 'username', + 'type' => 'varchar', + 'max_length' => 255, + 'nullable' => false, + 'default' => null, + 'primary_key' => 0, + ], + 2 => [ + 'name' => 'name', + 'type' => 'varchar', + 'max_length' => 255, + 'nullable' => false, + 'default' => null, + 'primary_key' => 0, + ], + 3 => [ + 'name' => 'active', + 'type' => 'int', + 'max_length' => 11, + 'nullable' => false, + 'default' => '0', + 'primary_key' => 0, + ], + ]; - if (version_compare($this->db->getVersion(), '8.0.17', '<')) { + if (version_compare($this->db->getVersion(), '8.0.17', '>=')) { // As of MySQL 8.0.17, the display width attribute for integer data types // is deprecated and is not reported back anymore. // @see https://dev.mysql.com/doc/refman/8.0/en/numeric-type-attributes.html - $this->assertSame(11, $fieldsData[0]->max_length); + $expected[0]['max_length'] = null; + $expected[3]['max_length'] = null; } - - $this->assertNull($fieldsData[0]->default); - $this->assertNull($fieldsData[1]->default); - - $this->assertSame(1, (int) $fieldsData[0]->primary_key); - - $this->assertSame(255, (int) $fieldsData[1]->max_length); } elseif ($this->db->DBDriver === 'Postgre') { - $this->assertSame('integer', $fieldsData[0]->type); - $this->assertSame('character varying', $fieldsData[1]->type); - - $this->assertFalse($fieldsData[0]->nullable); - $this->assertFalse($fieldsData[1]->nullable); - - $this->assertSame(32, (int) $fieldsData[0]->max_length); - $this->assertSame(255, (int) $fieldsData[1]->max_length); - - $this->assertNull($fieldsData[1]->default); + $expected = [ + 0 => [ + 'name' => 'id', + 'type' => 'integer', + 'nullable' => false, + 'default' => "nextval('db_forge_test_fields_id_seq'::regclass)", + 'max_length' => '32', + ], + 1 => [ + 'name' => 'username', + 'type' => 'character varying', + 'nullable' => false, + 'default' => null, + 'max_length' => '255', + ], + 2 => [ + 'name' => 'name', + 'type' => 'character varying', + 'nullable' => false, + 'default' => null, + 'max_length' => '255', + ], + 3 => [ + 'name' => 'active', + 'type' => 'integer', + 'nullable' => false, + 'default' => '0', + 'max_length' => '32', + ], + ]; } elseif ($this->db->DBDriver === 'SQLite3') { - $this->assertSame('integer', strtolower($fieldsData[0]->type)); - $this->assertSame('varchar', strtolower($fieldsData[1]->type)); - - $this->assertNull($fieldsData[1]->default); + $expected = [ + 0 => [ + 'name' => 'id', + 'type' => 'INTEGER', + 'max_length' => null, + 'default' => null, + 'primary_key' => true, + 'nullable' => true, + ], + 1 => [ + 'name' => 'username', + 'type' => 'VARCHAR', + 'max_length' => null, + 'default' => null, + 'primary_key' => false, + 'nullable' => false, + ], + 2 => [ + 'name' => 'name', + 'type' => 'VARCHAR', + 'max_length' => null, + 'default' => null, + 'primary_key' => false, + 'nullable' => false, + ], + 3 => [ + 'name' => 'active', + 'type' => 'INTEGER', + 'max_length' => null, + 'default' => '0', + 'primary_key' => false, + 'nullable' => false, + ], + ]; } elseif ($this->db->DBDriver === 'SQLSRV') { - $this->assertSame('int', $fieldsData[0]->type); - $this->assertSame('varchar', $fieldsData[1]->type); - - $this->assertSame(10, (int) $fieldsData[0]->max_length); - $this->assertSame(255, (int) $fieldsData[1]->max_length); - - $this->assertNull($fieldsData[1]->default); + $expected = [ + 0 => [ + 'name' => 'id', + 'type' => 'int', + 'default' => null, + 'max_length' => 10, + ], + 1 => [ + 'name' => 'username', + 'type' => 'varchar', + 'default' => null, + 'max_length' => 255, + ], + 2 => [ + 'name' => 'name', + 'type' => 'varchar', + 'default' => null, + 'max_length' => 255, + ], + 3 => [ + 'name' => 'active', + 'type' => 'int', + 'default' => '((0))', // Why? + 'max_length' => 10, + ], + ]; } elseif ($this->db->DBDriver === 'OCI8') { - // Check types - $this->assertSame('NUMBER', $fieldsData[0]->type); - $this->assertSame('VARCHAR2', $fieldsData[1]->type); - - $this->assertSame('11', $fieldsData[0]->max_length); - $this->assertSame('255', $fieldsData[1]->max_length); + $expected = [ + 0 => [ + 'name' => 'id', + 'type' => 'NUMBER', + 'max_length' => '11', + 'default' => '"ORACLE"."ISEQ$$_80229".nextval', // Sequence id may change + 'nullable' => false, + ], + 1 => [ + 'name' => 'username', + 'type' => 'VARCHAR2', + 'max_length' => '255', + 'default' => '', + 'nullable' => false, + ], + 2 => [ + 'name' => 'name', + 'type' => 'VARCHAR2', + 'max_length' => '255', + 'default' => '', + 'nullable' => false, + ], + 3 => [ + 'name' => 'active', + 'type' => 'NUMBER', + 'max_length' => '11', + 'default' => '0 ', // Why? + 'nullable' => false, + ], + ]; - $this->assertSame('', $fieldsData[1]->default); + // Sequence id may change + $this->assertMatchesRegularExpression('/"ORACLE"."ISEQ\\$\\$_\d+".nextval/', $fieldsData[0]->default); + $expected[0]['default'] = $fieldsData[0]->default; } else { $this->fail(sprintf('DB driver "%s" is not supported.', $this->db->DBDriver)); } + + $this->assertSame($expected, json_decode(json_encode($fieldsData), true)); } public function testCompositeKey() From e48296ed972bf2470d0780c05a04f3a3caf73ba4 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 21 Aug 2022 14:20:28 +0800 Subject: [PATCH 07/77] Exclude non-monolithic files from CS --- .php-cs-fixer.dist.php | 6 +++++- .php-cs-fixer.no-header.php | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index a0502a6d9cae..37093e287e13 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -25,7 +25,11 @@ __DIR__ . '/tests', __DIR__ . '/utils', ]) - ->exclude(['ThirdParty']) + ->exclude([ + 'Pager/Views', + 'ThirdParty', + 'Validation/Views', + ]) ->notName('#Foobar.php$#') ->append([ __FILE__, diff --git a/.php-cs-fixer.no-header.php b/.php-cs-fixer.no-header.php index 6c136cd4b164..e795b5a75577 100644 --- a/.php-cs-fixer.no-header.php +++ b/.php-cs-fixer.no-header.php @@ -25,6 +25,7 @@ __DIR__ . '/app', __DIR__ . '/public', ]) + ->exclude(['Views/errors/html']) ->notName('#Logger\.php$#') ->append([ __DIR__ . '/admin/starter/builds', From 66af82214427f0b55e82bed2d80861f24a4ef691 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 21 Aug 2022 20:16:17 -0700 Subject: [PATCH 08/77] Update system/Database/SQLite3/Table.php Co-authored-by: kenjis --- system/Database/SQLite3/Table.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 99d14f408fe1..fbb8d0d0f6d6 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -244,8 +244,7 @@ protected function createTable() $this->keys = array_filter( $this->keys, - static fn ($index) => count(array_intersect($index['fields'], $fieldNames)) - === count($index['fields']) + static fn ($index) => count(array_intersect($index['fields'], $fieldNames)) === count($index['fields']) ); // Unique/Index keys From b6751aad5a0f7f1f57f86594e92f7344e5121029 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Mon, 22 Aug 2022 14:03:27 +0300 Subject: [PATCH 09/77] Fix broken caching system when array of allowed parameters configuration used Cache Include Query String in the /Config/Cache.php (line 73) suggest to the user 3 options to choose from: false = Disabled true = Enabled, take all query parameters into account. Please be aware that this may result in numerous cache files generated for the same page over and over again. array('q') = Enabled, but only take into account the specified list of query parameters. However, last option with array of important parameters just doesn't work. We were using third option, because we had one important parameter (color) on the site, and content were dynamic based on this parameter. But then we`ve seen huge amount of cache files on our server, thousands and thousands. Turned out, even when using array of parameters in Config/Cache $cacheQueryString option - CodeIgniter was creating new cache file for any requests just like it was set to 'true'. Cache fles generated for the same page over and over again, performance were slow. Since Google Ads, Bing, Yahoo and all marketing platforms always adding dynamic parameters like 'click_id' for example, 'utm_source', 'utm_medium' and other, cache file was creating for all requests from the paid traffic. It was almost impossible to catch, but we saw weird amount of caching files, and that helped us to see the problem. I`ve made testing on the clean CodeIgniter 4 installations, latest version. Happened there too. So I`ve checked the source code, and I saw there there is no difference between true/array options, theyjust work same. I believe this is very missleading, because Config/Cache showing 'array' option and suggest to use it. This fixes should help, and take into the account array option. Thank you --- system/CodeIgniter.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 157723b188d8..a7b4da24a988 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -730,9 +730,12 @@ protected function generateCacheName(Cache $config): string } $uri = $this->request->getUri(); - if ($config->cacheQueryString) { - $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()); + if (is_array($config->cacheQueryString)){ + $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery(array('only' => $config->cacheQueryString))); + }else{ + $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()); + } } else { $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath()); } From 80eb98745688f8a0ce804de1f0c7d3a0a7df2fb6 Mon Sep 17 00:00:00 2001 From: chirag jagani Date: Mon, 22 Aug 2022 17:52:13 +0530 Subject: [PATCH 10/77] =?UTF-8?q?docs=20:=20fix=20typo=20in=20"General=20T?= =?UTF-8?q?opics=20=C2=BB=20Managing=20your=20Applications"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- user_guide_src/source/general/managing_apps.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/general/managing_apps.rst b/user_guide_src/source/general/managing_apps.rst index 616631e647f1..e3c6df0119b0 100644 --- a/user_guide_src/source/general/managing_apps.rst +++ b/user_guide_src/source/general/managing_apps.rst @@ -8,7 +8,7 @@ directory. It is possible, however, to have multiple sets of applications that share a single CodeIgniter installation, or even to rename or relocate your application directory. -.. important:: When you installed CodeIgniter v4.1.9 or before, and if there are ``App\\`` and ``Config\\`` namespaces in your ``/composer.json``'s ``autoload.psr-4`` like the following, you need to remove these lines, and run ``composer dump-autolod``. +.. important:: When you installed CodeIgniter v4.1.9 or before, and if there are ``App\\`` and ``Config\\`` namespaces in your ``/composer.json``'s ``autoload.psr-4`` like the following, you need to remove these lines, and run ``composer dump-autoload``. .. code-block:: text From 512f2539516a521e15cc3e42a09906c6accf0690 Mon Sep 17 00:00:00 2001 From: ping-yee <611077101@mail.nknu.edu.tw> Date: Mon, 22 Aug 2022 23:59:00 +0800 Subject: [PATCH 11/77] fix: cs-fix --- tests/system/Validation/ValidationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 1d2049a9d965..5a48615b6c05 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -472,7 +472,7 @@ public function testRunWithCustomErrorsAndAsteriskField(): void 'foo' => [ ['bar' => null], ['bar' => null], - ] + ], ]; $this->validation->setRules( ['foo.*.bar' => ['label' => 'foo bar', 'rules' => 'required']], @@ -481,7 +481,7 @@ public function testRunWithCustomErrorsAndAsteriskField(): void $this->validation->run($data); $this->assertSame([ 'foo.0.bar' => 'Required', - 'foo.1.bar' => 'Required' + 'foo.1.bar' => 'Required', ], $this->validation->getErrors()); } From 1760b0909531c3b1790c5e4ceaa6fc64c2283d75 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Aug 2022 18:05:15 +0900 Subject: [PATCH 12/77] docs: update about test database --- tests/README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/README.md b/tests/README.md index 5a7ddbbbed72..f5579f083ee6 100644 --- a/tests/README.md +++ b/tests/README.md @@ -34,9 +34,24 @@ for code coverage to be calculated successfully. After installing `XDebug`, you ## Setting Up A number of the tests use a running database. -In order to set up the database edit the details for the `tests` group in -**app/Config/Database.php** or **phpunit.xml**. +The default configuration uses SQLite3 memory database, so it works if you can use SQLite3. + +In order to change the database for testing, edit the details for the `tests` group in +**app/Config/Database.php** or use **.env** file. + +E.g.: +``` +database.tests.hostname = localhost +database.tests.database = ci4_test +database.tests.username = root +database.tests.password = root +database.tests.DBDriver = MySQLi +database.tests.DBPrefix = db_ +database.default.port = 3306 +``` + Make sure that you provide a database engine that is currently running on your machine. + More details on a test database setup are in the [Testing Your Database](https://codeigniter4.github.io/CodeIgniter4/testing/database.html) section of the documentation. From 7100bc6316a212c270cd4cf6b7d2c55fd543103d Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Aug 2022 18:18:18 +0900 Subject: [PATCH 13/77] docs: update test database config sample in .env --- user_guide_src/source/testing/database.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/testing/database.rst b/user_guide_src/source/testing/database.rst index 382b4ee4aa56..e8d04f2aec72 100644 --- a/user_guide_src/source/testing/database.rst +++ b/user_guide_src/source/testing/database.rst @@ -32,10 +32,13 @@ If you have multiple developers on your team, you will likely want to keep your the **.env** file. To do so, edit the file to ensure the following lines are present and have the correct information:: - database.tests.dbdriver = 'MySQLi'; - database.tests.username = 'root'; - database.tests.password = ''; - database.tests.database = ''; + database.tests.hostname = localhost + database.tests.database = ci4_test + database.tests.username = root + database.tests.password = root + database.tests.DBDriver = MySQLi + database.tests.DBPrefix = + database.tests.port = 3306 Migrations and Seeds ==================== From fa474add948e0ddb0dd7b868138a4f9c8b23bac3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Aug 2022 18:18:59 +0900 Subject: [PATCH 14/77] config: change commented database.tests.database value It is better to use dedicated database for testing. --- env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env b/env index cc0681c67711..9dede2eadf81 100644 --- a/env +++ b/env @@ -48,7 +48,7 @@ # database.default.port = 3306 # database.tests.hostname = localhost -# database.tests.database = ci4 +# database.tests.database = ci4_test # database.tests.username = root # database.tests.password = root # database.tests.DBDriver = MySQLi From a04b27d11d9c4cc8be16645a8ffec70a504ee3a0 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Tue, 23 Aug 2022 12:24:37 +0300 Subject: [PATCH 15/77] Fix coding style --- system/CodeIgniter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index a7b4da24a988..d502bb09c4d7 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -731,9 +731,9 @@ protected function generateCacheName(Cache $config): string $uri = $this->request->getUri(); if ($config->cacheQueryString) { - if (is_array($config->cacheQueryString)){ - $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery(array('only' => $config->cacheQueryString))); - }else{ + if (is_array($config->cacheQueryString)) { + $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery(['only' => $config->cacheQueryString])); + } else { $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()); } } else { From 0fc684e21d6360b651c428c98a26b477a9905057 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Aug 2022 20:29:29 +0900 Subject: [PATCH 16/77] docs: restore phpunit.xml --- tests/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/README.md b/tests/README.md index f5579f083ee6..b244f0afe46b 100644 --- a/tests/README.md +++ b/tests/README.md @@ -37,7 +37,7 @@ A number of the tests use a running database. The default configuration uses SQLite3 memory database, so it works if you can use SQLite3. In order to change the database for testing, edit the details for the `tests` group in -**app/Config/Database.php** or use **.env** file. +**app/Config/Database.php** or **phpunit.xml** or use **.env** file. E.g.: ``` From 7b4f4823b7cce8f2e441fbbad2a809e16cc2a67d Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Tue, 23 Aug 2022 18:43:44 +0300 Subject: [PATCH 17/77] Created 3 tests for caching with different cacheQueryString options Three new tests, for all suggested options of $cacheQueryString in the Config/Cache.php cacheQueryString = true cacheQueryString = false cacheQueryString = array('q') --- tests/system/CodeIgniterTest.php | 199 +++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index d1b6793e4e89..9ae846550f17 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -18,6 +18,7 @@ use CodeIgniter\Test\Filters\CITestStreamFilter; use CodeIgniter\Test\Mock\MockCodeIgniter; use Config\App; +use Config\Cache; use Config\Filters; use Config\Modules; use Tests\Support\Filters\Customfilter; @@ -622,4 +623,202 @@ public function testPageCacheSendSecureHeaders() stream_filter_remove($outputStreamFilter); stream_filter_remove($errorStreamFilter); } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 + */ + public function testPageCacheWithCacheQueryStringFalse() + { + // Suppress command() output + CITestStreamFilter::$buffer = ''; + $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Create cache config with disabled cacheQueryString + $cacheConfig = new Cache(); + $cacheConfig->cacheQueryString = false; + + // Clear cache before starting the test + command('cache:clear'); + + // Calculate amount of items in the cache before the test + $cache = \Config\Services::cache(); + $cache_start_counter = count($cache->getCacheInfo()); + + // URLs for testing cache configuration. Only one cache file should be created for all 5 URL`s, since we have disabled cacheQueryString + $testing_urls = [ + 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page + 'test?important_parameter=1', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + 'test?important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + ]; + + // Generate request to each URL from the testing array + foreach ($testing_urls as $key => $testing_url) { + $_SERVER['REQUEST_URI'] = '/' . $testing_url; + $routes = Services::routes(true); + $routes->add($testing_url, static function () { + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + $response = Services::response(); + $string = 'This is a test page, to check cache configuration'; + + return $response->setBody($string); + }); + + // Inject router + $router = Services::router($routes, Services::request(null, false)); + Services::injectMock('router', $router); + + // Cache the page output using default caching function and our own $cacheConfig + $this->codeigniter->useSafeOutput(true)->run(); + $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration + } + + // Calculate how much cached items exist in the cache after the test requests + $cache_end_counter = count($cache->getCacheInfo()); + $new_pages_cached = $cache_end_counter - $cache_start_counter; + + // Clear cache after the test + command('cache:clear'); + + // We expect only one new page in the cache, because GET parameters should be ignored with cacheQueryString = false + $this->assertSame(1, $new_pages_cached); + + // Remove stream filters + stream_filter_remove($outputStreamFilter); + stream_filter_remove($errorStreamFilter); + } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 + */ + public function testPageCacheWithCacheQueryStringTrue() + { + // Suppress command() output + CITestStreamFilter::$buffer = ''; + $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Create cache config with enabled cacheQueryString + $cacheConfig = new Cache(); + $cacheConfig->cacheQueryString = true; + + // Clear cache before starting the test + command('cache:clear'); + + // Calculate amount of items in the cache before the test + $cache = \Config\Services::cache(); + $cache_start_counter = count($cache->getCacheInfo()); + + // URLs for testing cache configuration. New 5 files should be created for all 5 URL`s, since we have enabled cacheQueryString, and all GET parameters combinations is unique + $testing_urls = [ + 'test', // Should be cached because its a unique URL and not exist in the cache + 'test?important_parameter=1', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test?important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test?important_parameter=1¬_important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + ]; + + // Generate request to each URL from the testing array + foreach ($testing_urls as $key => $testing_url) { + $_SERVER['REQUEST_URI'] = '/' . $testing_url; + $routes = Services::routes(true); + $routes->add($testing_url, static function () { + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + $response = Services::response(); + $string = 'This is a test page, to check cache configuration'; + + return $response->setBody($string); + }); + + // Inject router + $router = Services::router($routes, Services::request(null, false)); + Services::injectMock('router', $router); + + // Cache the page output using default caching function and our own $cacheConfig + $this->codeigniter->useSafeOutput(true)->run(); + $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration + } + + // Calculate how much cached items exist in the cache after the test requests + $cache_end_counter = count($cache->getCacheInfo()); + $new_pages_cached = $cache_end_counter - $cache_start_counter; + + // Clear cache after the test + command('cache:clear'); + + // We expect 5 new pages in the cache, because GET parameters should be counted as unique pages with cacheQueryString = true + $this->assertSame(5, $new_pages_cached); + + // Remove stream filters + stream_filter_remove($outputStreamFilter); + stream_filter_remove($errorStreamFilter); + } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 + */ + public function testPageCacheWithCacheQueryStringArray() + { + // Suppress command() output + CITestStreamFilter::$buffer = ''; + $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Create cache config with enabled cacheQueryString + $cacheConfig = new Cache(); + $cacheConfig->cacheQueryString = ['important_parameter']; + + // Clear cache before starting the test + command('cache:clear'); + + // Calculate amount of items in the cache before the test + $cache = \Config\Services::cache(); + $cache_start_counter = count($cache->getCacheInfo()); + + // URLs for testing cache configuration. Only 3 files should be created for this 5 URL`s, since cacheQueryString is an array of important parameters only, and all other GET parameters (not in the array) should be ignored + $testing_urls = [ + 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page + 'test?important_parameter=1', // Should be cached because important parameter exist and have unique value 1 + 'test?important_parameter=2', // Should be cached because important parameter exist and have unique value 2 + 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored + ]; + + // Generate request to each URL from the testing array + foreach ($testing_urls as $key => $testing_url) { + $_SERVER['REQUEST_URI'] = '/' . $testing_url; + $routes = Services::routes(true); + $routes->add($testing_url, static function () { + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + $response = Services::response(); + $string = 'This is a test page, to check cache configuration'; + + return $response->setBody($string); + }); + + // Inject router + $router = Services::router($routes, Services::request(null, false)); + Services::injectMock('router', $router); + + // Cache the page output using default caching function and our own $cacheConfig + $this->codeigniter->useSafeOutput(true)->run(); + $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration + } + + // Calculate how much cached items exist in the cache after the test requests + $cache_end_counter = count($cache->getCacheInfo()); + $new_pages_cached = $cache_end_counter - $cache_start_counter; + + // Clear cache after the test + command('cache:clear'); + + // We expect 3 new pages in the cache, because only important GET parameters from array should be counted as unique pages with cacheQueryString = ['important_parameter'] + $this->assertSame(3, $new_pages_cached); + + // Remove stream filters + stream_filter_remove($outputStreamFilter); + stream_filter_remove($errorStreamFilter); + } } From 8adacb00750692c790629a09490d7caece5afd04 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Tue, 23 Aug 2022 20:47:51 +0300 Subject: [PATCH 18/77] Refactoring syntax edits to pass Rector 7.4 and 8.0 check --- tests/system/CodeIgniterTest.php | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 9ae846550f17..4ee789f43f2b 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -642,11 +642,11 @@ public function testPageCacheWithCacheQueryStringFalse() command('cache:clear'); // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cache_start_counter = count($cache->getCacheInfo()); + $cache = \Config\Services::cache(); + $cacheStartCounter = count($cache->getCacheInfo()); // URLs for testing cache configuration. Only one cache file should be created for all 5 URL`s, since we have disabled cacheQueryString - $testing_urls = [ + $testingUrls = [ 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page 'test?important_parameter=1', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration 'test?important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration @@ -655,10 +655,10 @@ public function testPageCacheWithCacheQueryStringFalse() ]; // Generate request to each URL from the testing array - foreach ($testing_urls as $key => $testing_url) { - $_SERVER['REQUEST_URI'] = '/' . $testing_url; + foreach ($testingUrls as $testingUrl) { + $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); - $routes->add($testing_url, static function () { + $routes->add($testingUrl, static function () { CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -676,14 +676,14 @@ public function testPageCacheWithCacheQueryStringFalse() } // Calculate how much cached items exist in the cache after the test requests - $cache_end_counter = count($cache->getCacheInfo()); - $new_pages_cached = $cache_end_counter - $cache_start_counter; + $cacheEndCounter = count($cache->getCacheInfo()); + $newPagesCached = $cacheEndCounter - $cacheStartCounter; // Clear cache after the test command('cache:clear'); // We expect only one new page in the cache, because GET parameters should be ignored with cacheQueryString = false - $this->assertSame(1, $new_pages_cached); + $this->assertSame(1, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); @@ -708,11 +708,11 @@ public function testPageCacheWithCacheQueryStringTrue() command('cache:clear'); // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cache_start_counter = count($cache->getCacheInfo()); + $cache = \Config\Services::cache(); + $cacheStartCounter = count($cache->getCacheInfo()); // URLs for testing cache configuration. New 5 files should be created for all 5 URL`s, since we have enabled cacheQueryString, and all GET parameters combinations is unique - $testing_urls = [ + $testingUrls = [ 'test', // Should be cached because its a unique URL and not exist in the cache 'test?important_parameter=1', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache 'test?important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache @@ -721,10 +721,10 @@ public function testPageCacheWithCacheQueryStringTrue() ]; // Generate request to each URL from the testing array - foreach ($testing_urls as $key => $testing_url) { - $_SERVER['REQUEST_URI'] = '/' . $testing_url; + foreach ($testingUrls as $testingUrl) { + $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); - $routes->add($testing_url, static function () { + $routes->add($testingUrl, static function () { CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -742,14 +742,14 @@ public function testPageCacheWithCacheQueryStringTrue() } // Calculate how much cached items exist in the cache after the test requests - $cache_end_counter = count($cache->getCacheInfo()); - $new_pages_cached = $cache_end_counter - $cache_start_counter; + $cacheEndCounter = count($cache->getCacheInfo()); + $newPagesCached = $cacheEndCounter - $cacheStartCounter; // Clear cache after the test command('cache:clear'); // We expect 5 new pages in the cache, because GET parameters should be counted as unique pages with cacheQueryString = true - $this->assertSame(5, $new_pages_cached); + $this->assertSame(5, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); @@ -774,11 +774,11 @@ public function testPageCacheWithCacheQueryStringArray() command('cache:clear'); // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cache_start_counter = count($cache->getCacheInfo()); + $cache = \Config\Services::cache(); + $cacheStartCounter = count($cache->getCacheInfo()); // URLs for testing cache configuration. Only 3 files should be created for this 5 URL`s, since cacheQueryString is an array of important parameters only, and all other GET parameters (not in the array) should be ignored - $testing_urls = [ + $testingUrls = [ 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page 'test?important_parameter=1', // Should be cached because important parameter exist and have unique value 1 'test?important_parameter=2', // Should be cached because important parameter exist and have unique value 2 @@ -787,10 +787,10 @@ public function testPageCacheWithCacheQueryStringArray() ]; // Generate request to each URL from the testing array - foreach ($testing_urls as $key => $testing_url) { - $_SERVER['REQUEST_URI'] = '/' . $testing_url; + foreach ($testingUrls as $testingUrl) { + $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); - $routes->add($testing_url, static function () { + $routes->add($testingUrl, static function () { CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -808,14 +808,14 @@ public function testPageCacheWithCacheQueryStringArray() } // Calculate how much cached items exist in the cache after the test requests - $cache_end_counter = count($cache->getCacheInfo()); - $new_pages_cached = $cache_end_counter - $cache_start_counter; + $cacheEndCounter = count($cache->getCacheInfo()); + $newPagesCached = $cacheEndCounter - $cacheStartCounter; // Clear cache after the test command('cache:clear'); // We expect 3 new pages in the cache, because only important GET parameters from array should be counted as unique pages with cacheQueryString = ['important_parameter'] - $this->assertSame(3, $new_pages_cached); + $this->assertSame(3, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); From b2ad447cf18c7dcde0d584e57aac84ebdc1c9cce Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 23 Aug 2022 11:40:58 -0700 Subject: [PATCH 19/77] Add testDropColumnDropCompositeKey() --- .../Database/Live/SQLite/AlterTableTest.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index 7eb2216139e5..d58d8dd20976 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -156,6 +156,42 @@ public function testDropColumnMaintainsKeys() $this->assertTrue($result); } + public function testDropColumnDropCompositeKey() + { + $this->forge->dropTable('actions', true); + + $fields = [ + 'category' => ['type' => 'varchar', 'constraint' => 63], + 'name' => ['type' => 'varchar', 'constraint' => 63], + 'created_at' => ['type' => 'datetime', 'null' => true], + ]; + + $this->forge->addField('id'); + $this->forge->addField($fields); + + $this->forge->addKey('name'); + $this->forge->addKey(['category', 'name']); + $this->forge->addKey('created_at'); + + $this->forge->createTable('actions'); + + $indexes = $this->db->getIndexData('actions'); + + // the composite index was created + $this->assertSame(['category', 'name'], $indexes['actions_category_name']->fields); + + // drop one of the columns in the composite index + $this->forge->dropColumn('actions', 'category'); + + // get indexes again + $indexes = $this->db->getIndexData('actions'); + + // check that composite index was dropped. + $this->assertFalse(isset($indexes['actions_category_name'])); + + $this->forge->dropTable('actions'); + } + public function testModifyColumnSuccess() { $this->createTable('janky'); From 7717097c9636ff7f142dc58bde141eab0e1e867d Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 23 Aug 2022 11:44:47 -0700 Subject: [PATCH 20/77] Fix test --- tests/system/Database/Live/SQLite/AlterTableTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index d58d8dd20976..fbce09ffb8d2 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -189,6 +189,10 @@ public function testDropColumnDropCompositeKey() // check that composite index was dropped. $this->assertFalse(isset($indexes['actions_category_name'])); + // check that that other keys are present + $this->assertTrue(isset($indexes['actions_name'])); + $this->assertTrue(isset($indexes['actions_created_at'])); + $this->forge->dropTable('actions'); } From 9a4ee1b3e189342d79c93e0e3a1398a46d61a041 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 23 Aug 2022 11:53:21 -0700 Subject: [PATCH 21/77] fix --- tests/system/Database/Live/SQLite/AlterTableTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index fbce09ffb8d2..60baaae441f8 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -187,11 +187,11 @@ public function testDropColumnDropCompositeKey() $indexes = $this->db->getIndexData('actions'); // check that composite index was dropped. - $this->assertFalse(isset($indexes['actions_category_name'])); + $this->assertArrayNotHasKey('actions_category_name', $indexes); // check that that other keys are present - $this->assertTrue(isset($indexes['actions_name'])); - $this->assertTrue(isset($indexes['actions_created_at'])); + $this->assertArrayHasKey('actions_name', $indexes); + $this->assertArrayHasKey('actions_created_at', $indexes); $this->forge->dropTable('actions'); } From 7824a2051d14ee94d3a339f84259864980d17b8a Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Wed, 24 Aug 2022 00:59:55 +0300 Subject: [PATCH 22/77] Updating the Cache test to use data provider Using data provider for 1 cache test instead of 3 different tests. https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html#writing-tests-for-phpunit-data-providers --- tests/system/CodeIgniterTest.php | 163 +++++-------------------------- 1 file changed, 22 insertions(+), 141 deletions(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 4ee789f43f2b..d14173a4ad35 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -625,18 +625,23 @@ public function testPageCacheSendSecureHeaders() } /** + * @param mixed $cacheQueryStringValue + * @param int $expectedPagesInCache + * @param array $testingUrls + * @dataProvider cacheQueryStringProvider + * * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 */ - public function testPageCacheWithCacheQueryStringFalse() + public function testPageCacheWithCacheQueryString($cacheQueryStringValue, $expectedPagesInCache, $testingUrls) { // Suppress command() output CITestStreamFilter::$buffer = ''; $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); - // Create cache config with disabled cacheQueryString + // Create cache config with cacheQueryString value from the dataProvider $cacheConfig = new Cache(); - $cacheConfig->cacheQueryString = false; + $cacheConfig->cacheQueryString = $cacheQueryStringValue; // Clear cache before starting the test command('cache:clear'); @@ -645,21 +650,12 @@ public function testPageCacheWithCacheQueryStringFalse() $cache = \Config\Services::cache(); $cacheStartCounter = count($cache->getCacheInfo()); - // URLs for testing cache configuration. Only one cache file should be created for all 5 URL`s, since we have disabled cacheQueryString - $testingUrls = [ - 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page - 'test?important_parameter=1', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - 'test?important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - ]; - // Generate request to each URL from the testing array foreach ($testingUrls as $testingUrl) { $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); $routes->add($testingUrl, static function () { - CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings from the dataProvider $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -670,7 +666,7 @@ public function testPageCacheWithCacheQueryStringFalse() $router = Services::router($routes, Services::request(null, false)); Services::injectMock('router', $router); - // Cache the page output using default caching function and our own $cacheConfig + // Cache the page output using default caching function and $cacheConfig with value from the data provider $this->codeigniter->useSafeOutput(true)->run(); $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration } @@ -682,143 +678,28 @@ public function testPageCacheWithCacheQueryStringFalse() // Clear cache after the test command('cache:clear'); - // We expect only one new page in the cache, because GET parameters should be ignored with cacheQueryString = false - $this->assertSame(1, $newPagesCached); + // Check that amount of new items created in the cache matching expected value from the data provider + $this->assertSame($expectedPagesInCache, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); stream_filter_remove($errorStreamFilter); } - /** - * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 - */ - public function testPageCacheWithCacheQueryStringTrue() + public function cacheQueryStringProvider(): array { - // Suppress command() output - CITestStreamFilter::$buffer = ''; - $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); - $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); - - // Create cache config with enabled cacheQueryString - $cacheConfig = new Cache(); - $cacheConfig->cacheQueryString = true; - - // Clear cache before starting the test - command('cache:clear'); - - // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cacheStartCounter = count($cache->getCacheInfo()); - - // URLs for testing cache configuration. New 5 files should be created for all 5 URL`s, since we have enabled cacheQueryString, and all GET parameters combinations is unique $testingUrls = [ - 'test', // Should be cached because its a unique URL and not exist in the cache - 'test?important_parameter=1', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache - 'test?important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache - 'test?important_parameter=1¬_important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache - 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test', // URL #1 + 'test?important_parameter=1', // URL #2 + 'test?important_parameter=2', // URL #3 + 'test?important_parameter=1¬_important_parameter=2', // URL #4 + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // URL #5 ]; - // Generate request to each URL from the testing array - foreach ($testingUrls as $testingUrl) { - $_SERVER['REQUEST_URI'] = '/' . $testingUrl; - $routes = Services::routes(true); - $routes->add($testingUrl, static function () { - CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test - $response = Services::response(); - $string = 'This is a test page, to check cache configuration'; - - return $response->setBody($string); - }); - - // Inject router - $router = Services::router($routes, Services::request(null, false)); - Services::injectMock('router', $router); - - // Cache the page output using default caching function and our own $cacheConfig - $this->codeigniter->useSafeOutput(true)->run(); - $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration - } - - // Calculate how much cached items exist in the cache after the test requests - $cacheEndCounter = count($cache->getCacheInfo()); - $newPagesCached = $cacheEndCounter - $cacheStartCounter; - - // Clear cache after the test - command('cache:clear'); - - // We expect 5 new pages in the cache, because GET parameters should be counted as unique pages with cacheQueryString = true - $this->assertSame(5, $newPagesCached); - - // Remove stream filters - stream_filter_remove($outputStreamFilter); - stream_filter_remove($errorStreamFilter); - } - - /** - * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 - */ - public function testPageCacheWithCacheQueryStringArray() - { - // Suppress command() output - CITestStreamFilter::$buffer = ''; - $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); - $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); - - // Create cache config with enabled cacheQueryString - $cacheConfig = new Cache(); - $cacheConfig->cacheQueryString = ['important_parameter']; - - // Clear cache before starting the test - command('cache:clear'); - - // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cacheStartCounter = count($cache->getCacheInfo()); - - // URLs for testing cache configuration. Only 3 files should be created for this 5 URL`s, since cacheQueryString is an array of important parameters only, and all other GET parameters (not in the array) should be ignored - $testingUrls = [ - 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page - 'test?important_parameter=1', // Should be cached because important parameter exist and have unique value 1 - 'test?important_parameter=2', // Should be cached because important parameter exist and have unique value 2 - 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored - 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored + return [ + '$cacheQueryString=false' => [false, 1, $testingUrls], // We expect only 1 page in the cache, because when cacheQueryString is set to false, all GET parameter should be ignored, and page URI will be absolutely same "/test" string for all 5 requests + '$cacheQueryString=true' => [true, 5, $testingUrls], // We expect all 5 pages in the cache, because when cacheQueryString is set to true, all GET parameter should be processed as unique requests + '$cacheQueryString=array' => [['important_parameter'], 3, $testingUrls], // We expect only 3 pages in the cache, because when cacheQueryString is set to array with important parameters, we should ignore all parameters thats not in the array. Only URL #1, URL #2 and URL #3 should be cached. URL #4 and URL #5 is duplication of URL #2 (with value ?important_parameter=1), so they should not be processed as new unique requests and application should return already cached page for URL #2 ]; - - // Generate request to each URL from the testing array - foreach ($testingUrls as $testingUrl) { - $_SERVER['REQUEST_URI'] = '/' . $testingUrl; - $routes = Services::routes(true); - $routes->add($testingUrl, static function () { - CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test - $response = Services::response(); - $string = 'This is a test page, to check cache configuration'; - - return $response->setBody($string); - }); - - // Inject router - $router = Services::router($routes, Services::request(null, false)); - Services::injectMock('router', $router); - - // Cache the page output using default caching function and our own $cacheConfig - $this->codeigniter->useSafeOutput(true)->run(); - $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration - } - - // Calculate how much cached items exist in the cache after the test requests - $cacheEndCounter = count($cache->getCacheInfo()); - $newPagesCached = $cacheEndCounter - $cacheStartCounter; - - // Clear cache after the test - command('cache:clear'); - - // We expect 3 new pages in the cache, because only important GET parameters from array should be counted as unique pages with cacheQueryString = ['important_parameter'] - $this->assertSame(3, $newPagesCached); - - // Remove stream filters - stream_filter_remove($outputStreamFilter); - stream_filter_remove($errorStreamFilter); } } From 70d27e04af2b680353fad3d446a98162f6270fea Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 24 Aug 2022 11:17:22 +0900 Subject: [PATCH 23/77] docs: fix by proofreading Co-authored-by: John Paul E. Balandan, CPA <51850998+paulbalandan@users.noreply.github.com> --- tests/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/README.md b/tests/README.md index b244f0afe46b..6ea177ddb588 100644 --- a/tests/README.md +++ b/tests/README.md @@ -34,7 +34,7 @@ for code coverage to be calculated successfully. After installing `XDebug`, you ## Setting Up A number of the tests use a running database. -The default configuration uses SQLite3 memory database, so it works if you can use SQLite3. +The default configuration uses SQLite3 transient in-memory database, so it works if you can use SQLite3. In order to change the database for testing, edit the details for the `tests` group in **app/Config/Database.php** or **phpunit.xml** or use **.env** file. From 39d1162a0b45bdcd45dc76b3726bcd05c6b40513 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 24 Aug 2022 17:31:41 +0900 Subject: [PATCH 24/77] docs: add Backward Compatibility Notes --- .../backward_compatibility_notes.rst | 16 ++++++++++++++++ user_guide_src/source/installation/upgrading.rst | 4 ++++ 2 files changed, 20 insertions(+) create mode 100644 user_guide_src/source/installation/backward_compatibility_notes.rst diff --git a/user_guide_src/source/installation/backward_compatibility_notes.rst b/user_guide_src/source/installation/backward_compatibility_notes.rst new file mode 100644 index 000000000000..9b7272edf148 --- /dev/null +++ b/user_guide_src/source/installation/backward_compatibility_notes.rst @@ -0,0 +1,16 @@ +############################ +Backward Compatibility Notes +############################ + +We try to develop our products to be as backward compatible (BC) as possible. + +Only major releases (such as 4.0, 5.0 etc.) are allowed to break backward compatibility. +Minor releases (such as 4.2, 4.3 etc.) may introduce new features, but must do so without breaking the existing API. + +However, the code is not mature and bug fixes may break compatibility in minor releases, or even in patch releases (such as 4.2.5). In that case, all the breaking changes are described in the :doc:`../changelogs/index`. + +***************************** +What are not Breaking Changes +***************************** + +- System messages defined in **system/Language/en/** are strictly for internal framework use and are not covered by backwards compatibility (BC) promise. If developers are relying on language string output they should be checking it against the function call (``lang('...')``), not the content. diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index 8e89b617fdd5..84695eb58ac2 100644 --- a/user_guide_src/source/installation/upgrading.rst +++ b/user_guide_src/source/installation/upgrading.rst @@ -5,6 +5,8 @@ Upgrading From a Previous Version Please read the upgrade notes corresponding to the version you are upgrading from. +See also :doc:`./backward_compatibility_notes`. + .. note:: If you don't know what version of CodeIgniter you are currently running, you can get it from :ref:`the Debug Toolbar `, or simply echo the constant ``\CodeIgniter\CodeIgniter::CI_VERSION``. @@ -12,6 +14,8 @@ upgrading from. .. toctree:: :titlesonly: + backward_compatibility_notes + upgrade_423 upgrade_422 upgrade_421 From ac68aada6ef5398f40c39d8b7a05bf3c6e205bbd Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Wed, 24 Aug 2022 14:51:28 +0300 Subject: [PATCH 25/77] Use parameter type declaration instead of doc comment | Change mixed type to strict type for the --- tests/system/CodeIgniterTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index d14173a4ad35..11b7542c3dff 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -625,14 +625,12 @@ public function testPageCacheSendSecureHeaders() } /** - * @param mixed $cacheQueryStringValue - * @param int $expectedPagesInCache - * @param array $testingUrls + * @param array|bool $cacheQueryStringValue * @dataProvider cacheQueryStringProvider * * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 */ - public function testPageCacheWithCacheQueryString($cacheQueryStringValue, $expectedPagesInCache, $testingUrls) + public function testPageCacheWithCacheQueryString($cacheQueryStringValue, int $expectedPagesInCache, array $testingUrls) { // Suppress command() output CITestStreamFilter::$buffer = ''; From fb042fc9348eddd74d6a50428ab0226c90fa56e5 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 25 Aug 2022 02:50:07 +0700 Subject: [PATCH 26/77] [Rector] Clean up skip UnwrapFutureCompatibleIfPhpVersionRector --- rector.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rector.php b/rector.php index 660b086aeb63..fdae04ac7af3 100644 --- a/rector.php +++ b/rector.php @@ -29,7 +29,6 @@ use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector; -use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector; use Rector\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector; use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector; use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector; @@ -93,11 +92,6 @@ __DIR__ . '/tests', ], - // check on constant compare - UnwrapFutureCompatibleIfPhpVersionRector::class => [ - __DIR__ . '/system/CodeIgniter.php', - ], - // session handlers have the gc() method with underscored parameter `$max_lifetime` UnderscoreToCamelCaseVariableNameRector::class => [ __DIR__ . '/system/Session/Handlers', From 31c929601ed643d137bffc324fb955272be1ecda Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 25 Aug 2022 02:51:53 +0700 Subject: [PATCH 27/77] run rector.php --- .github/workflows/test-rector.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-rector.yml b/.github/workflows/test-rector.yml index bf4146edd3df..0a9a8d41d15b 100644 --- a/.github/workflows/test-rector.yml +++ b/.github/workflows/test-rector.yml @@ -14,6 +14,7 @@ on: - 'utils/**.php' - '.github/workflows/test-rector.yml' - composer.json + - rector.php push: branches: From 8ffd54dd796c8886e03023d29dacf0293bad8ffb Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 25 Aug 2022 02:52:04 +0700 Subject: [PATCH 28/77] run rector.php --- .github/workflows/test-rector.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-rector.yml b/.github/workflows/test-rector.yml index 0a9a8d41d15b..9db9ea63af58 100644 --- a/.github/workflows/test-rector.yml +++ b/.github/workflows/test-rector.yml @@ -27,6 +27,7 @@ on: - 'utils/**.php' - '.github/workflows/test-rector.yml' - composer.json + - rector.php jobs: build: From 7acd0fe242708a02170f41e3dfe53cb1e04f657a Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 25 Aug 2022 15:30:25 +0900 Subject: [PATCH 29/77] docs: fix git command example 4.3 branch may not exist if a contributor forked upstream long ago. --- contributing/workflow.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contributing/workflow.md b/contributing/workflow.md index 03e567d7fe29..7c7c1ca7cc21 100644 --- a/contributing/workflow.md +++ b/contributing/workflow.md @@ -83,8 +83,8 @@ is normally done locally, so that you can resolve any merge conflicts. For instance, to synchronize **develop** branches: ```console -> git switch develop > git fetch upstream +> git switch develop > git merge upstream/develop > git push origin develop ``` @@ -181,8 +181,8 @@ It is a lot easier to resolve conflicts at this stage. Synchronize your repository: ```console -> git switch develop > git fetch upstream +> git switch develop > git merge upstream/develop > git push origin develop ``` @@ -253,8 +253,8 @@ do the following: Synchronize your repository: ```console -> git switch develop > git fetch upstream +> git switch develop > git merge upstream/develop > git push origin develop ``` @@ -298,8 +298,8 @@ Copy the IDs of any commits you made that you want to keep: Update your `4.3` branch: ```console -> git switch 4.3 > git fetch upstream +> git switch 4.3 > git merge upstream/4.3 > git push origin 4.3 ``` From 2f244e3003e6f41daef2fe76b8f2d5211cf22ee9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 26 Aug 2022 11:26:07 +0900 Subject: [PATCH 30/77] docs: replace mixed in Database doc comments --- system/Database/BaseConnection.php | 2 +- system/Database/Forge.php | 2 +- system/Database/MySQLi/Connection.php | 2 +- system/Database/MySQLi/Forge.php | 6 +++--- system/Database/OCI8/Connection.php | 2 +- system/Database/OCI8/Forge.php | 6 +++--- system/Database/Postgre/Connection.php | 2 +- system/Database/Postgre/Forge.php | 2 +- system/Database/SQLSRV/Connection.php | 2 +- system/Database/SQLSRV/Forge.php | 2 +- system/Database/SQLite3/Connection.php | 3 ++- system/Database/SQLite3/Forge.php | 2 +- system/Test/Mock/MockConnection.php | 2 +- 13 files changed, 18 insertions(+), 17 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 5aaf843a07f7..865a1893b902 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -562,7 +562,7 @@ public function addTableAlias(string $table) /** * Executes the query against the database. * - * @return mixed + * @return bool|object|resource */ abstract protected function execute(string $sql); diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 458e60b22442..5c13d27feb34 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -776,7 +776,7 @@ public function modifyColumn(string $table, $field): bool } /** - * @param mixed $fields + * @param array|string $fields * * @return false|string|string[] */ diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 5488852318c5..23a4525114db 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -277,7 +277,7 @@ public function getVersion(): string /** * Executes the query against the database. * - * @return mixed + * @return bool|object */ protected function execute(string $sql) { diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index d00c26dd1ca7..0bee007c6341 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -128,9 +128,9 @@ protected function _createTableAttributes(array $attributes): string /** * ALTER TABLE * - * @param string $alterType ALTER type - * @param string $table Table name - * @param mixed $field Column definition + * @param string $alterType ALTER type + * @param string $table Table name + * @param array|string $field Column definition * * @return string|string[] */ diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index 081a2777a12e..1cb9eb083c55 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -184,7 +184,7 @@ public function getVersion(): string /** * Executes the query against the database. * - * @return false|resource + * @return bool */ protected function execute(string $sql) { diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 42393cd63aa9..add01540177a 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -86,9 +86,9 @@ class Forge extends BaseForge /** * ALTER TABLE * - * @param string $alterType ALTER type - * @param string $table Table name - * @param mixed $field Column definition + * @param string $alterType ALTER type + * @param string $table Table name + * @param array|string $field Column definition * * @return string|string[] */ diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index f7efdb9b4f36..4452eae3a5a9 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -132,7 +132,7 @@ public function getVersion(): string /** * Executes the query against the database. * - * @return mixed + * @return false|resource */ protected function execute(string $sql) { diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index a050c6888e38..4f7c75ea4520 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -76,7 +76,7 @@ protected function _createTableAttributes(array $attributes): string } /** - * @param mixed $field + * @param array|string $field * * @return array|bool|string */ diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index e4d0005c80fe..74398fd204bb 100755 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -451,7 +451,7 @@ public function setDatabase(?string $databaseName = null) /** * Executes the query against the database. * - * @return mixed + * @return false|resource */ protected function execute(string $sql) { diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 95ec9ff0b601..bb802149edaf 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -119,7 +119,7 @@ protected function _createTableAttributes(array $attributes): string } /** - * @param mixed $field + * @param array|string $field * * @return false|string|string[] */ diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 4d7ae0ee45e8..5ceef09a6f94 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -16,6 +16,7 @@ use ErrorException; use Exception; use SQLite3; +use SQLite3Result; use stdClass; /** @@ -120,7 +121,7 @@ public function getVersion(): string /** * Execute the query * - * @return mixed \SQLite3Result object or bool + * @return bool|SQLite3Result */ protected function execute(string $sql) { diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index 154c9f97b554..9175dd2bd978 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -109,7 +109,7 @@ public function dropDatabase(string $dbName): bool } /** - * @param mixed $field + * @param array|string $field * * @return array|string|null */ diff --git a/system/Test/Mock/MockConnection.php b/system/Test/Mock/MockConnection.php index f515550569a9..16693f260308 100644 --- a/system/Test/Mock/MockConnection.php +++ b/system/Test/Mock/MockConnection.php @@ -139,7 +139,7 @@ public function getVersion(): string /** * Executes the query against the database. * - * @return mixed + * @return bool|object */ protected function execute(string $sql) { From 0454d7372ae96dbaded420f00e946e2c2992a390 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 26 Aug 2022 14:26:59 +0900 Subject: [PATCH 31/77] docs: update the reference to Time, and make it a note --- user_guide_src/source/helpers/date_helper.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst index 533364d4249d..0177eccae5aa 100644 --- a/user_guide_src/source/helpers/date_helper.rst +++ b/user_guide_src/source/helpers/date_helper.rst @@ -9,6 +9,8 @@ dates. :local: :depth: 2 +.. note:: Many functions previously found in the CodeIgniter 3 ``date_helper`` have been moved to the :doc:`Time <../libraries/time>` class in CodeIgniter 4. + Loading this Helper =================== @@ -53,6 +55,3 @@ The following functions are available: selected value. .. literalinclude:: date_helper/003.php - -Many functions previously found in the CodeIgniter 3 ``date_helper`` have been moved to the ``I18n`` -module in CodeIgniter 4. From f9f606da65ec5d4e2951cecc62337cf4b1166e76 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 26 Aug 2022 14:27:54 +0900 Subject: [PATCH 32/77] docs: fix text decoration --- user_guide_src/source/helpers/date_helper.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst index 0177eccae5aa..fce403fe1a78 100644 --- a/user_guide_src/source/helpers/date_helper.rst +++ b/user_guide_src/source/helpers/date_helper.rst @@ -50,7 +50,7 @@ The following functions are available: :returns: Preformatted HTML select field :rtype: string - Generates a `select` form field of available timezones (optionally filtered by `$what` and `$country`). + Generates a `select` form field of available timezones (optionally filtered by ``$what`` and ``$country``). You can supply an option class to apply to the field to make formatting easier, as well as a default selected value. From 90dda258e5f195d8cd2583e898ca7e695f80aee0 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 26 Aug 2022 14:29:35 +0900 Subject: [PATCH 33/77] docs: Correct the explanation of now() To be honest, I don't understand the behavior of this function. The UNIX timestamp has nothing to do with time zone. --- user_guide_src/source/helpers/date_helper.rst | 17 +++++++++-------- .../source/helpers/date_helper/002.php | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst index fce403fe1a78..2aeae38ddad6 100644 --- a/user_guide_src/source/helpers/date_helper.rst +++ b/user_guide_src/source/helpers/date_helper.rst @@ -29,17 +29,18 @@ The following functions are available: :returns: UNIX timestamp :rtype: int - Returns the current time as a UNIX timestamp, referenced either to your server's - local time or any PHP supported timezone, based on the "time reference" setting - in your config file. If you do not intend to set your master time reference to - any other PHP supported timezone (which you'll typically do if you run a site - that lets each user set their own timezone settings) there is no benefit to using - this function over PHP's ``time()`` function. + .. note:: It is recommended to use the :doc:`Time <../libraries/time>` class instead. Use ``Time::now()->getTimestamp()`` to get the current UNIX timestamp. + + If a timezone is not provided, it will return the current UNIX timestamp by ``time()``. .. literalinclude:: date_helper/002.php - If a timezone is not provided, it will return ``time()`` based on the - **time_reference** setting. + If any PHP supported timezone is provided, it will return a timestamp that is off by the time difference. It is not the same as the current UNIX timestamp. + + If you do not intend to set your master time reference to + any other PHP supported timezone (which you'll typically do if you run a site + that lets each user set their own timezone settings) there is no benefit to using + this function over PHP's ``time()`` function. .. php:function:: timezone_select([$class = '', $default = '', $what = \DateTimeZone::ALL, $country = null]) diff --git a/user_guide_src/source/helpers/date_helper/002.php b/user_guide_src/source/helpers/date_helper/002.php index 6631bccdd4cc..e0361c0b3575 100644 --- a/user_guide_src/source/helpers/date_helper/002.php +++ b/user_guide_src/source/helpers/date_helper/002.php @@ -1,3 +1,3 @@ Date: Fri, 26 Aug 2022 14:41:51 +0900 Subject: [PATCH 34/77] refactor: remove unused key '_parent_name' --- system/Database/BaseBuilder.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 58fe0da786e2..39e832d13d76 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -2705,8 +2705,7 @@ protected function objectToArray($object) $array = []; foreach (get_object_vars($object) as $key => $val) { - // There are some built in keys we need to ignore for this conversion - if (! is_object($val) && ! is_array($val) && $key !== '_parent_name') { + if (! is_object($val) && ! is_array($val)) { $array[$key] = $val; } } @@ -2732,13 +2731,10 @@ protected function batchObjectToArray($object) $fields = array_keys($out); foreach ($fields as $val) { - // There are some built in keys we need to ignore for this conversion - if ($val !== '_parent_name') { - $i = 0; + $i = 0; - foreach ($out[$val] as $data) { - $array[$i++][$val] = $data; - } + foreach ($out[$val] as $data) { + $array[$i++][$val] = $data; } } From b53d482699a5f454e537544fc87c132d740545b1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 25 Aug 2022 07:31:28 +0900 Subject: [PATCH 35/77] fix: remove harmful @mixin in RequestInterface In a class that implements RequestInterface, a method that does not actually exist was displayed in the IDE as if it existed. --- system/Common.php | 5 +++++ system/Filters/CSRF.php | 3 ++- system/Filters/InvalidChars.php | 3 ++- system/HTTP/Negotiate.php | 6 +++++- system/HTTP/RequestInterface.php | 4 ---- system/Security/Security.php | 7 +++++++ system/Validation/FileRules.php | 8 +++++--- 7 files changed, 26 insertions(+), 10 deletions(-) diff --git a/system/Common.php b/system/Common.php index 962483bb68b2..be13c36faabb 100644 --- a/system/Common.php +++ b/system/Common.php @@ -19,6 +19,7 @@ use CodeIgniter\Debug\Timer; use CodeIgniter\Files\Exceptions\FileNotFoundException; use CodeIgniter\HTTP\Exceptions\HTTPException; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; @@ -487,6 +488,10 @@ function force_https(int $duration = 31_536_000, ?RequestInterface $request = nu $response = Services::response(null, true); } + if (! $request instanceof IncomingRequest) { + return; + } + if ((ENVIRONMENT !== 'testing' && (is_cli() || $request->isSecure())) || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'test')) { // @codeCoverageIgnoreStart return; diff --git a/system/Filters/CSRF.php b/system/Filters/CSRF.php index 9cce85fb7ada..6bc83405b7f0 100644 --- a/system/Filters/CSRF.php +++ b/system/Filters/CSRF.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Filters; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; @@ -44,7 +45,7 @@ class CSRF implements FilterInterface */ public function before(RequestInterface $request, $arguments = null) { - if ($request->isCLI()) { + if (! $request instanceof IncomingRequest) { return; } diff --git a/system/Filters/InvalidChars.php b/system/Filters/InvalidChars.php index 4b1d8f7f9b6b..42aa15b19675 100644 --- a/system/Filters/InvalidChars.php +++ b/system/Filters/InvalidChars.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Filters; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Security\Exceptions\SecurityException; @@ -48,7 +49,7 @@ class InvalidChars implements FilterInterface */ public function before(RequestInterface $request, $arguments = null) { - if ($request->isCLI()) { + if (! $request instanceof IncomingRequest) { return; } diff --git a/system/HTTP/Negotiate.php b/system/HTTP/Negotiate.php index dfb9dcedf9b4..d0e822574b85 100644 --- a/system/HTTP/Negotiate.php +++ b/system/HTTP/Negotiate.php @@ -27,7 +27,7 @@ class Negotiate /** * Request * - * @var IncomingRequest|RequestInterface + * @var IncomingRequest */ protected $request; @@ -37,6 +37,8 @@ class Negotiate public function __construct(?RequestInterface $request = null) { if ($request !== null) { + assert($request instanceof IncomingRequest); + $this->request = $request; } } @@ -48,6 +50,8 @@ public function __construct(?RequestInterface $request = null) */ public function setRequest(RequestInterface $request) { + assert($request instanceof IncomingRequest); + $this->request = $request; return $this; diff --git a/system/HTTP/RequestInterface.php b/system/HTTP/RequestInterface.php index e7bc14a3f361..ca3229db904c 100644 --- a/system/HTTP/RequestInterface.php +++ b/system/HTTP/RequestInterface.php @@ -13,10 +13,6 @@ /** * Expected behavior of an HTTP request - * - * @mixin IncomingRequest - * @mixin CLIRequest - * @mixin CURLRequest */ interface RequestInterface { diff --git a/system/Security/Security.php b/system/Security/Security.php index 36f4a730da5f..df5bb17e3876 100644 --- a/system/Security/Security.php +++ b/system/Security/Security.php @@ -13,6 +13,7 @@ use CodeIgniter\Cookie\Cookie; use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\Request; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\Response; use CodeIgniter\Security\Exceptions\SecurityException; @@ -321,6 +322,8 @@ public function verify(RequestInterface $request) */ private function removeTokenInRequest(RequestInterface $request): void { + assert($request instanceof Request); + $json = json_decode($request->getBody() ?? ''); if (isset($_POST[$this->tokenName])) { @@ -336,6 +339,8 @@ private function removeTokenInRequest(RequestInterface $request): void private function getPostedToken(RequestInterface $request): ?string { + assert($request instanceof IncomingRequest); + // Does the token exist in POST, HEADER or optionally php:://input - json data. if ($request->hasHeader($this->headerName) && ! empty($request->header($this->headerName)->getValue())) { $tokenName = $request->header($this->headerName)->getValue(); @@ -580,6 +585,8 @@ private function saveHashInCookie(): void */ protected function sendCookie(RequestInterface $request) { + assert($request instanceof IncomingRequest); + if ($this->cookie->isSecure() && ! $request->isSecure()) { return false; } diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index 89b91a7fdbf8..aec9d9efa2a4 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -11,6 +11,8 @@ namespace CodeIgniter\Validation; +use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\Request; use CodeIgniter\HTTP\RequestInterface; use Config\Mimes; use Config\Services; @@ -24,14 +26,12 @@ class FileRules /** * Request instance. So we can get access to the files. * - * @var RequestInterface + * @var IncomingRequest */ protected $request; /** * Constructor. - * - * @param RequestInterface $request */ public function __construct(?RequestInterface $request = null) { @@ -39,6 +39,8 @@ public function __construct(?RequestInterface $request = null) $request = Services::request(); } + assert($request instanceof IncomingRequest); + $this->request = $request; } From dff94db328b7b6775c68e033d8f23a5b461c059c Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 25 Aug 2022 08:37:29 +0900 Subject: [PATCH 36/77] fix: add missing get*() methods in CLIRequest --- system/HTTP/CLIRequest.php | 56 ++++++++++++++++++++++++++++ tests/system/HTTP/CLIRequestTest.php | 20 ++++++++++ 2 files changed, 76 insertions(+) diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 5f2c70b19ff7..0c767db4c38f 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -214,4 +214,60 @@ public function isCLI(): bool { return true; } + + /** + * Fetch an item from GET data. + * + * @param array|string|null $index Index for item to fetch from $_GET. + * @param int|null $filter A filter name to apply. + * @param mixed|null $flags + * + * @return null + */ + public function getGet($index = null, $filter = null, $flags = null) + { + return null; + } + + /** + * Fetch an item from POST. + * + * @param array|string|null $index Index for item to fetch from $_POST. + * @param int|null $filter A filter name to apply + * @param mixed $flags + * + * @return null + */ + public function getPost($index = null, $filter = null, $flags = null) + { + return null; + } + + /** + * Fetch an item from POST data with fallback to GET. + * + * @param array|string|null $index Index for item to fetch from $_POST or $_GET + * @param int|null $filter A filter name to apply + * @param mixed $flags + * + * @return null + */ + public function getPostGet($index = null, $filter = null, $flags = null) + { + return null; + } + + /** + * Fetch an item from GET data with fallback to POST. + * + * @param array|string|null $index Index for item to be fetched from $_GET or $_POST + * @param int|null $filter A filter name to apply + * @param mixed $flags + * + * @return null + */ + public function getGetPost($index = null, $filter = null, $flags = null) + { + return null; + } } diff --git a/tests/system/HTTP/CLIRequestTest.php b/tests/system/HTTP/CLIRequestTest.php index bf78ccc6e752..e01596351b23 100644 --- a/tests/system/HTTP/CLIRequestTest.php +++ b/tests/system/HTTP/CLIRequestTest.php @@ -589,4 +589,24 @@ public function testMethodIsCliReturnsAlwaysTrue() { $this->assertTrue($this->request->isCLI()); } + + public function testGetGet() + { + $this->assertNull($this->request->getGet()); + } + + public function testGetPost() + { + $this->assertNull($this->request->getPost()); + } + + public function testGetPostGet() + { + $this->assertNull($this->request->getPostGet()); + } + + public function testGetGetPost() + { + $this->assertNull($this->request->getGetPost()); + } } From 873f616fc17256f2eae54efbeed1a69581bb6e51 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 25 Aug 2022 08:49:57 +0900 Subject: [PATCH 37/77] fix: Honeypot filter causes error Call to undefined method CodeIgniter\HTTP\CLIRequest::getPost() --- system/Filters/Honeypot.php | 5 +++++ system/Honeypot/Honeypot.php | 3 +++ 2 files changed, 8 insertions(+) diff --git a/system/Filters/Honeypot.php b/system/Filters/Honeypot.php index 539513e7cb8a..419c2b47e75a 100644 --- a/system/Filters/Honeypot.php +++ b/system/Filters/Honeypot.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Filters; use CodeIgniter\Honeypot\Exceptions\HoneypotException; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Config\Services; @@ -31,6 +32,10 @@ class Honeypot implements FilterInterface */ public function before(RequestInterface $request, $arguments = null) { + if (! $request instanceof IncomingRequest) { + return; + } + if (Services::honeypot()->hasContent($request)) { throw HoneypotException::isBot(); } diff --git a/system/Honeypot/Honeypot.php b/system/Honeypot/Honeypot.php index 178f56621618..fdd8fe95168b 100644 --- a/system/Honeypot/Honeypot.php +++ b/system/Honeypot/Honeypot.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Honeypot; use CodeIgniter\Honeypot\Exceptions\HoneypotException; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Config\Honeypot as HoneypotConfig; @@ -59,6 +60,8 @@ public function __construct(HoneypotConfig $config) */ public function hasContent(RequestInterface $request) { + assert($request instanceof IncomingRequest); + return ! empty($request->getPost($this->config->name)); } From 8c37dd1c7e19b5a54efcfd67a92638ec53f5d7d7 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 25 Aug 2022 09:08:32 +0900 Subject: [PATCH 38/77] test: update NegotiateTest --- tests/system/HTTP/NegotiateTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/HTTP/NegotiateTest.php b/tests/system/HTTP/NegotiateTest.php index c1ad8aa4da19..ba0f52663e59 100644 --- a/tests/system/HTTP/NegotiateTest.php +++ b/tests/system/HTTP/NegotiateTest.php @@ -20,14 +20,14 @@ */ final class NegotiateTest extends CIUnitTestCase { - private ?Request $request; + private ?IncomingRequest $request; private ?Negotiate $negotiate; protected function setUp(): void { parent::setUp(); - $this->request = new Request(new App()); + $this->request = new IncomingRequest(new App(), new URI(), null, new UserAgent()); $this->negotiate = new Negotiate($this->request); } From 42ac32d906ca83fc9dcf57f2314ea1f87da539d6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 25 Aug 2022 09:13:01 +0900 Subject: [PATCH 39/77] test: remove unneeded tearDown() --- tests/system/HTTP/NegotiateTest.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/system/HTTP/NegotiateTest.php b/tests/system/HTTP/NegotiateTest.php index ba0f52663e59..2f1eaba11a35 100644 --- a/tests/system/HTTP/NegotiateTest.php +++ b/tests/system/HTTP/NegotiateTest.php @@ -27,17 +27,10 @@ protected function setUp(): void { parent::setUp(); - $this->request = new IncomingRequest(new App(), new URI(), null, new UserAgent()); - + $this->request = new IncomingRequest(new App(), new URI(), null, new UserAgent()); $this->negotiate = new Negotiate($this->request); } - protected function tearDown(): void - { - $this->request = $this->negotiate = null; - unset($this->request, $this->negotiate); - } - public function testNegotiateMediaFindsHighestMatch() { $this->request->setHeader('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c'); From 5d7f529488c8e6a968ca28c44449404e9c079873 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Aug 2022 12:06:08 +0900 Subject: [PATCH 40/77] fix: return type of added get*() methods --- system/HTTP/CLIRequest.php | 18 ++++++++++++++---- tests/system/HTTP/CLIRequestTest.php | 10 ++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 0c767db4c38f..0b6c2b83a12d 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -226,7 +226,7 @@ public function isCLI(): bool */ public function getGet($index = null, $filter = null, $flags = null) { - return null; + return $this->returnNullOrEmptyArray($index); } /** @@ -240,7 +240,7 @@ public function getGet($index = null, $filter = null, $flags = null) */ public function getPost($index = null, $filter = null, $flags = null) { - return null; + return $this->returnNullOrEmptyArray($index); } /** @@ -254,7 +254,7 @@ public function getPost($index = null, $filter = null, $flags = null) */ public function getPostGet($index = null, $filter = null, $flags = null) { - return null; + return $this->returnNullOrEmptyArray($index); } /** @@ -268,6 +268,16 @@ public function getPostGet($index = null, $filter = null, $flags = null) */ public function getGetPost($index = null, $filter = null, $flags = null) { - return null; + return $this->returnNullOrEmptyArray($index); + } + + /** + * @param array|string|null $index + * + * @return array|null + */ + private function returnNullOrEmptyArray($index) + { + return ($index === null || is_array($index)) ? [] : null; } } diff --git a/tests/system/HTTP/CLIRequestTest.php b/tests/system/HTTP/CLIRequestTest.php index e01596351b23..e8e57dbc611b 100644 --- a/tests/system/HTTP/CLIRequestTest.php +++ b/tests/system/HTTP/CLIRequestTest.php @@ -592,21 +592,23 @@ public function testMethodIsCliReturnsAlwaysTrue() public function testGetGet() { - $this->assertNull($this->request->getGet()); + $this->assertSame([], $this->request->getGet()); + $this->assertNull($this->request->getGet('test')); + $this->assertSame([], $this->request->getGet(['test', 'abc'])); } public function testGetPost() { - $this->assertNull($this->request->getPost()); + $this->assertSame([], $this->request->getGet()); } public function testGetPostGet() { - $this->assertNull($this->request->getPostGet()); + $this->assertSame([], $this->request->getGet()); } public function testGetGetPost() { - $this->assertNull($this->request->getGetPost()); + $this->assertSame([], $this->request->getGet()); } } From b8e0e64f39deac72e4cdb84a5de93a37d11df6d4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Aug 2022 18:03:56 +0900 Subject: [PATCH 41/77] docs: add explanation about a shared instance and parameters --- user_guide_src/source/concepts/services.rst | 11 ++++++++++- user_guide_src/source/concepts/services/015.php | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 user_guide_src/source/concepts/services/015.php diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index 3e14a87acba0..0e20441faee2 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -36,7 +36,7 @@ come in handy. Instead of creating the instance ourself, we let a central class create an instance of the class for us. This class is kept very simple. It only contains a method for each class that we want -to use as a service. The method typically returns a shared instance of that class, passing any dependencies +to use as a service. The method typically returns a **shared instance** of that class, passing any dependencies it might have into it. Then, we would replace our timer creation code with code that calls this new class: .. literalinclude:: services/002.php @@ -57,6 +57,15 @@ As many CodeIgniter classes are provided as services, you can get them like the The ``$typography`` is an instance of the Typography class, and if you call ``\Config\Services::typography()`` again, you will get the exactly same instance. +The Services typically return a **shared instance** of the class. The following code creates a ``CURLRequest`` instance at the first call. And the second call returns the exactly same instance. + +.. literalinclude:: services/015.php + +Therefore, the parameter ``$options2`` for the ``$client2`` does not work. It is just ignored. + +Getting a New Instance +====================== + If you want to get a new instance of the Typography class, you need to pass ``false`` to the argument ``$getShared``: .. literalinclude:: services/014.php diff --git a/user_guide_src/source/concepts/services/015.php b/user_guide_src/source/concepts/services/015.php new file mode 100644 index 000000000000..efaf1be9f00d --- /dev/null +++ b/user_guide_src/source/concepts/services/015.php @@ -0,0 +1,15 @@ + 'http://example.com/api/v1/', + 'timeout' => 3, +]; +$client1 = \Config\Services::curlrequest($options1); + +$options2 = [ + 'baseURI' => 'http://another.example.com/api/v2/', + 'timeout' => 10, +]; +$client2 = \Config\Services::curlrequest($options2); +// $options2 does not work. +// $client2 is the exactly same instance as $client1. From 210b60f18060679c5c89d487769bed6a72fd6f8c Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Aug 2022 18:19:16 +0900 Subject: [PATCH 42/77] docs: add @method groupBy() in Model --- system/Model.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/Model.php b/system/Model.php index 04bd6b15fd1f..5353d9a58558 100644 --- a/system/Model.php +++ b/system/Model.php @@ -40,6 +40,7 @@ * * @property BaseConnection $db * + * @method $this groupBy($by, ?bool $escape = null) * @method $this havingIn(?string $key = null, $values = null, ?bool $escape = null) * @method $this havingLike($field, string $match = '', string $side = 'both', ?bool $escape = null, bool $insensitiveSearch = false) * @method $this havingNotIn(?string $key = null, $values = null, ?bool $escape = null) From 1621263110abed40b666f7feaf5a72f95e580048 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Aug 2022 21:23:35 +0900 Subject: [PATCH 43/77] docs: fix by proofreading Co-authored-by: MGatner --- user_guide_src/source/helpers/date_helper.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst index 2aeae38ddad6..fe1e62bbe3dc 100644 --- a/user_guide_src/source/helpers/date_helper.rst +++ b/user_guide_src/source/helpers/date_helper.rst @@ -35,7 +35,7 @@ The following functions are available: .. literalinclude:: date_helper/002.php - If any PHP supported timezone is provided, it will return a timestamp that is off by the time difference. It is not the same as the current UNIX timestamp. + If any PHP supported timezone is provided, it will return a timestamp that is offset by the time difference. It is not the same as the current UNIX timestamp. If you do not intend to set your master time reference to any other PHP supported timezone (which you'll typically do if you run a site From 7cc7283caa88ff5c2614de162d8533aeb149441e Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" <51850998+paulbalandan@users.noreply.github.com> Date: Sat, 27 Aug 2022 23:40:10 +0800 Subject: [PATCH 44/77] Remove unneeded abstract `handle()` method --- system/Log/Handlers/BaseHandler.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/system/Log/Handlers/BaseHandler.php b/system/Log/Handlers/BaseHandler.php index 97d1281deb1a..c19ecb0b8066 100644 --- a/system/Log/Handlers/BaseHandler.php +++ b/system/Log/Handlers/BaseHandler.php @@ -47,17 +47,6 @@ public function canHandle(string $level): bool return in_array($level, $this->handles, true); } - /** - * Handles logging the message. - * If the handler returns false, then execution of handlers - * will stop. Any handlers that have not run, yet, will not - * be run. - * - * @param string $level - * @param string $message - */ - abstract public function handle($level, $message): bool; - /** * Stores the date format to use while logging messages. */ From 7ec384b4d846977967c27e353155bf880ea8161b Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 28 Aug 2022 06:40:19 +0900 Subject: [PATCH 45/77] chore: update Kint to 4.2.0 https://github.com/kint-php/kint/releases/tag/4.2.0 --- system/ThirdParty/Kint/Kint.php | 1 + .../Kint/Parser/ClassStaticsPlugin.php | 6 ++ system/ThirdParty/Kint/Parser/EnumPlugin.php | 86 +++++++++++++++++++ .../Kint/Renderer/Text/EnumPlugin.php | 44 ++++++++++ .../ThirdParty/Kint/Renderer/TextRenderer.php | 2 + system/ThirdParty/Kint/Zval/EnumValue.php | 62 +++++++++++++ .../Kint/resources/compiled/rich.js | 2 +- 7 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 system/ThirdParty/Kint/Parser/EnumPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/EnumPlugin.php create mode 100644 system/ThirdParty/Kint/Zval/EnumValue.php diff --git a/system/ThirdParty/Kint/Kint.php b/system/ThirdParty/Kint/Kint.php index e2145b062cf4..701283c2848d 100644 --- a/system/ThirdParty/Kint/Kint.php +++ b/system/ThirdParty/Kint/Kint.php @@ -150,6 +150,7 @@ class Kint 'Kint\\Parser\\ClosurePlugin', 'Kint\\Parser\\ColorPlugin', 'Kint\\Parser\\DateTimePlugin', + 'Kint\\Parser\\EnumPlugin', 'Kint\\Parser\\FsPathPlugin', 'Kint\\Parser\\IteratorPlugin', 'Kint\\Parser\\JsonPlugin', diff --git a/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php b/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php index 89601af2f9f7..7d2673f849d2 100644 --- a/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php +++ b/system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php @@ -30,6 +30,7 @@ use Kint\Zval\Value; use ReflectionClass; use ReflectionProperty; +use UnitEnum; class ClassStaticsPlugin extends Plugin { @@ -56,6 +57,11 @@ public function parse(&$var, Value &$o, $trigger) $consts = []; foreach ($reflection->getConstants() as $name => $val) { + // Skip enum constants + if ($var instanceof UnitEnum && $val instanceof UnitEnum && $o->classname == \get_class($val)) { + continue; + } + $const = Value::blank($name, '\\'.$class.'::'.$name); $const->const = true; $const->depth = $o->depth + 1; diff --git a/system/ThirdParty/Kint/Parser/EnumPlugin.php b/system/ThirdParty/Kint/Parser/EnumPlugin.php new file mode 100644 index 000000000000..3fe25f4eb516 --- /dev/null +++ b/system/ThirdParty/Kint/Parser/EnumPlugin.php @@ -0,0 +1,86 @@ +contents = []; + + foreach ($var->cases() as $case) { + $base_obj = Value::blank($class.'::'.$case->name, '\\'.$class.'::'.$case->name); + $base_obj->depth = $o->depth + 1; + + if ($var instanceof BackedEnum) { + $c = $case->value; + $cases->contents[] = $this->parser->parse($c, $base_obj); + } else { + $cases->contents[] = $base_obj; + } + } + + self::$cache[$class] = $cases; + } + + $object = new EnumValue($var); + $object->transplant($o); + + $object->addRepresentation(self::$cache[$class], 0); + + $o = $object; + } +} diff --git a/system/ThirdParty/Kint/Renderer/Text/EnumPlugin.php b/system/ThirdParty/Kint/Renderer/Text/EnumPlugin.php new file mode 100644 index 000000000000..efb5ba5d193f --- /dev/null +++ b/system/ThirdParty/Kint/Renderer/Text/EnumPlugin.php @@ -0,0 +1,44 @@ +depth) { + $out .= $this->renderer->colorTitle($this->renderer->renderTitle($o)).PHP_EOL; + } + + $out .= $this->renderer->renderHeader($o).PHP_EOL; + + return $out; + } +} diff --git a/system/ThirdParty/Kint/Renderer/TextRenderer.php b/system/ThirdParty/Kint/Renderer/TextRenderer.php index 0cfba527c78b..3421e132b21e 100644 --- a/system/ThirdParty/Kint/Renderer/TextRenderer.php +++ b/system/ThirdParty/Kint/Renderer/TextRenderer.php @@ -42,6 +42,7 @@ class TextRenderer extends Renderer 'microtime' => 'Kint\\Renderer\\Text\\MicrotimePlugin', 'recursion' => 'Kint\\Renderer\\Text\\RecursionPlugin', 'trace' => 'Kint\\Renderer\\Text\\TracePlugin', + 'enum' => 'Kint\\Renderer\\Text\\EnumPlugin', ]; /** @@ -55,6 +56,7 @@ class TextRenderer extends Renderer 'Kint\\Parser\\MicrotimePlugin', 'Kint\\Parser\\StreamPlugin', 'Kint\\Parser\\TracePlugin', + 'Kint\\Parser\\EnumPlugin', ]; /** diff --git a/system/ThirdParty/Kint/Zval/EnumValue.php b/system/ThirdParty/Kint/Zval/EnumValue.php new file mode 100644 index 000000000000..018eb68f0541 --- /dev/null +++ b/system/ThirdParty/Kint/Zval/EnumValue.php @@ -0,0 +1,62 @@ +enumval = $enumval; + } + + public function getValueShort() + { + if ($this->enumval instanceof BackedEnum) { + if (\is_string($this->enumval->value)) { + return '"'.$this->enumval->value.'"'; + } + if (\is_int($this->enumval->value)) { + return (string) $this->enumval->value; + } + } + } + + public function getType() + { + return $this->classname.'::'.$this->enumval->name; + } + + public function getSize() + { + } +} diff --git a/system/ThirdParty/Kint/resources/compiled/rich.js b/system/ThirdParty/Kint/resources/compiled/rich.js index f15d38ff0616..2f0ef6a1d0e2 100644 --- a/system/ThirdParty/Kint/resources/compiled/rich.js +++ b/system/ThirdParty/Kint/resources/compiled/rich.js @@ -1 +1 @@ -void 0===window.kintRich&&(window.kintRich=function(){"use strict";var l={selectText:function(e){var t=window.getSelection(),a=document.createRange();a.selectNodeContents(e),t.removeAllRanges(),t.addRange(a)},toggle:function(e,t){var a=l.getChildren(e);a&&(e.classList.toggle("kint-show",t),1===a.childNodes.length&&(a=a.childNodes[0].childNodes[0])&&a.classList&&a.classList.contains("kint-parent")&&l.toggle(a,t))},toggleChildren:function(e,t){var a=l.getChildren(e);if(a){var o=a.getElementsByClassName("kint-parent"),n=o.length;for(void 0===t&&(t=e.classList.contains("kint-show"));n--;)l.toggle(o[n],t)}},switchTab:function(e){var t=e.previousSibling,a=0;for(e.parentNode.getElementsByClassName("kint-active-tab")[0].classList.remove("kint-active-tab"),e.classList.add("kint-active-tab");t;)1===t.nodeType&&a++,t=t.previousSibling;for(var o=e.parentNode.nextSibling.childNodes,n=0;n"},openInNewWindow:function(e){var t=window.open();t&&(t.document.open(),t.document.write(l.mktag("html")+l.mktag("head")+l.mktag("title")+"Kint ("+(new Date).toISOString()+")"+l.mktag("/title")+l.mktag('meta charset="utf-8"')+l.mktag('script class="kint-rich-script" nonce="'+l.script.nonce+'"')+l.script.innerHTML+l.mktag("/script")+l.mktag('style class="kint-rich-style" nonce="'+l.style.nonce+'"')+l.style.innerHTML+l.mktag("/style")+l.mktag("/head")+l.mktag("body")+'
'+e.parentNode.outerHTML+"
"+l.mktag("/body")),t.document.close())},sortTable:function(e,a){var t=e.tBodies[0];[].slice.call(e.tBodies[0].rows).sort(function(e,t){if(e=e.cells[a].textContent.trim().toLocaleLowerCase(),t=t.cells[a].textContent.trim().toLocaleLowerCase(),isNaN(e)||isNaN(t)){if(isNaN(e)&&!isNaN(t))return 1;if(isNaN(t)&&!isNaN(e))return-1}else e=parseFloat(e),t=parseFloat(t);return eli:not(.kint-active-tab)").forEach(function(e){l.isFolderOpen()&&!l.folder.contains(e)||0===e.offsetWidth&&0===e.offsetHeight||l.keyboardNav.targets.push(e)}),e&&-1!==l.keyboardNav.targets.indexOf(e)&&(l.keyboardNav.target=l.keyboardNav.targets.indexOf(e))},sync:function(e){var t=document.querySelector(".kint-focused");t&&t.classList.remove("kint-focused"),l.keyboardNav.active&&((t=l.keyboardNav.targets[l.keyboardNav.target]).classList.add("kint-focused"),e||l.keyboardNav.scroll(t))},scroll:function(e){var t,a;e!==l.folder.querySelector("dt > nav")&&(a=(t=function(e){return e.offsetTop+(e.offsetParent?t(e.offsetParent):0)})(e),l.isFolderOpen()?(e=l.folder.querySelector("dd.kint-foldout")).scrollTo(0,a-e.clientHeight/2):window.scrollTo(0,a-window.innerHeight/2))},moveCursor:function(e){for(l.keyboardNav.target+=e;l.keyboardNav.target<0;)l.keyboardNav.target+=l.keyboardNav.targets.length;for(;l.keyboardNav.target>=l.keyboardNav.targets.length;)l.keyboardNav.target-=l.keyboardNav.targets.length;l.keyboardNav.sync()},setCursor:function(e){if(l.isFolderOpen()&&!l.folder.contains(e))return!1;l.keyboardNav.fetchTargets();for(var t=0;t"},openInNewWindow:function(e){var t=window.open();t&&(t.document.open(),t.document.write(l.mktag("html")+l.mktag("head")+l.mktag("title")+"Kint ("+(new Date).toISOString()+")"+l.mktag("/title")+l.mktag('meta charset="utf-8"')+l.mktag('script class="kint-rich-script" nonce="'+l.script.nonce+'"')+l.script.innerHTML+l.mktag("/script")+l.mktag('style class="kint-rich-style" nonce="'+l.style.nonce+'"')+l.style.innerHTML+l.mktag("/style")+l.mktag("/head")+l.mktag("body")+'
'+e.parentNode.outerHTML+"
"+l.mktag("/body")),t.document.close())},sortTable:function(e,a){var t=e.tBodies[0];[].slice.call(e.tBodies[0].rows).sort(function(e,t){if(e=e.cells[a].textContent.trim().toLocaleLowerCase(),t=t.cells[a].textContent.trim().toLocaleLowerCase(),isNaN(e)||isNaN(t)){if(isNaN(e)&&!isNaN(t))return 1;if(isNaN(t)&&!isNaN(e))return-1}else e=parseFloat(e),t=parseFloat(t);return eli:not(.kint-active-tab)").forEach(function(e){l.isFolderOpen()&&!l.folder.contains(e)||0===e.offsetWidth&&0===e.offsetHeight||l.keyboardNav.targets.push(e)}),e&&-1!==l.keyboardNav.targets.indexOf(e)&&(l.keyboardNav.target=l.keyboardNav.targets.indexOf(e))},sync:function(e){var t=document.querySelector(".kint-focused");t&&t.classList.remove("kint-focused"),l.keyboardNav.active&&((t=l.keyboardNav.targets[l.keyboardNav.target]).classList.add("kint-focused"),e||l.keyboardNav.scroll(t))},scroll:function(e){var t,a;e!==l.folder.querySelector("dt > nav")&&(a=(t=function(e){return e.offsetTop+(e.offsetParent?t(e.offsetParent):0)})(e),l.isFolderOpen()?(e=l.folder.querySelector("dd.kint-foldout")).scrollTo(0,a-e.clientHeight/2):window.scrollTo(0,a-window.innerHeight/2))},moveCursor:function(e){for(l.keyboardNav.target+=e;l.keyboardNav.target<0;)l.keyboardNav.target+=l.keyboardNav.targets.length;for(;l.keyboardNav.target>=l.keyboardNav.targets.length;)l.keyboardNav.target-=l.keyboardNav.targets.length;l.keyboardNav.sync()},setCursor:function(e){if(l.isFolderOpen()&&!l.folder.contains(e))return!1;l.keyboardNav.fetchTargets();for(var t=0;t Date: Sun, 28 Aug 2022 06:40:42 +0900 Subject: [PATCH 46/77] docs: add changelog --- user_guide_src/source/changelogs/v4.2.5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.2.5.rst b/user_guide_src/source/changelogs/v4.2.5.rst index 775ca5a33be3..59693fae6bb9 100644 --- a/user_guide_src/source/changelogs/v4.2.5.rst +++ b/user_guide_src/source/changelogs/v4.2.5.rst @@ -19,7 +19,7 @@ BREAKING Enhancements ************ -none. +- Kint has been updated to 4.2.0. Changes ******* From e215d66d1b82905cda814b7155cfebd9d9b15305 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 28 Aug 2022 11:11:40 +0900 Subject: [PATCH 47/77] docs: remove description on squashing commits It is not so important thing. --- contributing/pull_request.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/contributing/pull_request.md b/contributing/pull_request.md index 628d5593da5a..8a944ad1f490 100644 --- a/contributing/pull_request.md +++ b/contributing/pull_request.md @@ -249,18 +249,17 @@ The best way to contribute is to fork the CodeIgniter4 repository, and "clone" t 7. Fix existing bugs on the [Issue tracker](https://github.com/codeigniter4/CodeIgniter4/issues) after confirming that no one else is working on them. 8. [Commit](https://help.github.com/en/desktop/contributing-to-projects/committing-and-reviewing-changes-to-your-project) the changed files in your contribution branch. - `> git commit` - - Commit messages are expected to be descriptive of what you changed specifically. Commit messages like "Fixes #1234" would be asked by the reviewer to be revised. -9. If there are intermediate commits that are not meaningful to the overall PR, such as "Fixed error on style guide", "Fixed phpstan error", "Fixing mistake in code", and other related commits, it is advised to squash your commits so that we can have a clean commit history. -10. If you have touched PHP code, run static analysis. + - Commit messages are expected to be descriptive of why and what you changed specifically. Commit messages like "Fixes #1234" would be asked by the reviewer to be revised. [Atomic commit](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) is recommended. +9. If you have touched PHP code, run static analysis. - `> composer analyze` - `> vendor/bin/rector process` -11. Run unit tests on the specific file you modified. If there are no existing tests yet, please create one. +10. Run unit tests on the specific file you modified. If there are no existing tests yet, please create one. - `> vendor/bin/phpunit tests/system/path/to/file/you/modified` - Make sure the tests pass to have a higher chance of merging. -12. [Push](https://docs.github.com/en/github/using-git/pushing-commits-to-a-remote-repository) your contribution branch to your fork. +11. [Push](https://docs.github.com/en/github/using-git/pushing-commits-to-a-remote-repository) your contribution branch to your fork. - `> git push origin ` -13. Send a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork). -14. Label your pull request with the appropriate label if you can. +12. Send a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork). +13. Label your pull request with the appropriate label if you can. See [Contribution workflow](./workflow.md) for Git workflow details. From 122133bdb3db78578fbecbaa88b89340e023d075 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 28 Aug 2022 11:13:28 +0900 Subject: [PATCH 48/77] docs: add explanation on commits and commit messages --- contributing/workflow.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/contributing/workflow.md b/contributing/workflow.md index 7c7c1ca7cc21..7e4809c83817 100644 --- a/contributing/workflow.md +++ b/contributing/workflow.md @@ -139,16 +139,38 @@ Your local changes need to be *committed* to save them in your local repository. This is where [contribution signing](./signing.md) comes in. +Now we don't have detailed rules on commits and its messages. But +[atomic commit](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) is recommended. +Keep your commits atomic. One commit for one change. + +There are some references for writing good commit messages: + +- [Git Best Practices — AFTER Technique - DZone DevOps](https://dzone.com/articles/git-best-practices-after-technique-1) +- [Semantic Commit Messages](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716) +- [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) + +If there are intermediate commits that are not meaningful to the overall PR, +such as "Fix error on style guide", "Fix phpstan error", "Fix mistake in code", +and other related commits, you can squash your commits so that we can have a clean commit history. +But it is not a must. + +### Commit messages + +Commit messages are expected to be descriptive of **why** and what you changed specifically. +Commit messages like "Fixes #1234" would be asked by the reviewer to be revised. + You can have as many commits in a branch as you need to "get it right". For instance, to commit your work from a debugging session: ```console > git add . -> git commit -S -m "Find and fix the broken reference problem" +> git commit -S -m "Fix the broken reference problem" ``` Just make sure that your commits in a feature branch are all related. +### When you work on two features + If you are working on two features at a time, then you will want to switch between them to keep the contributions separate. For instance: From 55fd7f0239293b4e1201ffa6048a8a49924991ce Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 28 Aug 2022 11:14:40 +0900 Subject: [PATCH 49/77] docs: update wording We changed `git checkout` to `git switch` before. --- contributing/workflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/workflow.md b/contributing/workflow.md index 7e4809c83817..12ab737eb5f8 100644 --- a/contributing/workflow.md +++ b/contributing/workflow.md @@ -186,7 +186,7 @@ switch between them to keep the contributions separate. For instance: > git switch develop ``` -The last checkout makes sure that you end up in your *develop* branch as +The last switch makes sure that you end up in your *develop* branch as a starting point for your next session working with your repository. This is a good practice, as it is not always obvious which branch you are working in. From 2e956258170612d8d83c340a71cbd37e2525ed2b Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 28 Aug 2022 12:50:52 +0900 Subject: [PATCH 50/77] docs: move description from signing to workflow --- contributing/signing.md | 10 +--------- contributing/workflow.md | 12 +++++++++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/contributing/signing.md b/contributing/signing.md index ca84ef79a9a7..d312d3fc217f 100644 --- a/contributing/signing.md +++ b/contributing/signing.md @@ -52,12 +52,4 @@ bash shell to use the **-S** option to force the secure signing. ## Commit Messages Regardless of how you sign a commit, commit messages are important too. -They communicate the intent of a specific change, concisely. They make -it easier to review code, and to find out why a change was made if the -code history is examined later. - -The audience for your commit messages will be the codebase maintainers, -any code reviewers, and debuggers trying to figure out when a bug might -have been introduced. - -Make your commit messages meaningful. +See [Contribution Workflow](./workflow.md#commit-messages) for details. diff --git a/contributing/workflow.md b/contributing/workflow.md index 12ab737eb5f8..50673ce10166 100644 --- a/contributing/workflow.md +++ b/contributing/workflow.md @@ -154,7 +154,17 @@ such as "Fix error on style guide", "Fix phpstan error", "Fix mistake in code", and other related commits, you can squash your commits so that we can have a clean commit history. But it is not a must. -### Commit messages +### Commit Messages + +Commit messages are important. They communicate the intent of a specific change, concisely. +They make it easier to review code, and to find out why a change was made +if the code history is examined later. + +The audience for your commit messages will be the codebase maintainers, +any code reviewers, and debuggers trying to figure out when a bug might +have been introduced. + +Make your commit messages meaningful. Commit messages are expected to be descriptive of **why** and what you changed specifically. Commit messages like "Fixes #1234" would be asked by the reviewer to be revised. From c3820963f6ad75836ddcd8c3bc5c1e6591f24096 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 28 Aug 2022 12:51:18 +0900 Subject: [PATCH 51/77] docs: add link to detailed explanation --- contributing/pull_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/pull_request.md b/contributing/pull_request.md index 8a944ad1f490..ed6d0dfde9e6 100644 --- a/contributing/pull_request.md +++ b/contributing/pull_request.md @@ -249,7 +249,7 @@ The best way to contribute is to fork the CodeIgniter4 repository, and "clone" t 7. Fix existing bugs on the [Issue tracker](https://github.com/codeigniter4/CodeIgniter4/issues) after confirming that no one else is working on them. 8. [Commit](https://help.github.com/en/desktop/contributing-to-projects/committing-and-reviewing-changes-to-your-project) the changed files in your contribution branch. - `> git commit` - - Commit messages are expected to be descriptive of why and what you changed specifically. Commit messages like "Fixes #1234" would be asked by the reviewer to be revised. [Atomic commit](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) is recommended. + - Commit messages are expected to be descriptive of why and what you changed specifically. Commit messages like "Fixes #1234" would be asked by the reviewer to be revised. [Atomic commit](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) is recommended. See [Contribution Workflow](./workflow.md#commit-messages) for details. 9. If you have touched PHP code, run static analysis. - `> composer analyze` - `> vendor/bin/rector process` From 596b6293308e2c04a75aa36aa7a1a0103f4e0111 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 28 Aug 2022 19:41:04 +0900 Subject: [PATCH 52/77] chore: update kint vertion to ^4.2 --- admin/framework/composer.json | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/framework/composer.json b/admin/framework/composer.json index 78bd71deb0c0..b2a1d6d0e69b 100644 --- a/admin/framework/composer.json +++ b/admin/framework/composer.json @@ -10,7 +10,7 @@ "ext-intl": "*", "ext-json": "*", "ext-mbstring": "*", - "kint-php/kint": "^4.1.1", + "kint-php/kint": "^4.2", "laminas/laminas-escaper": "^2.9", "psr/log": "^1.1" }, diff --git a/composer.json b/composer.json index 080fc5974605..3080b701539b 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "ext-intl": "*", "ext-json": "*", "ext-mbstring": "*", - "kint-php/kint": "^4.1.1", + "kint-php/kint": "^4.2", "laminas/laminas-escaper": "^2.9", "psr/log": "^1.1" }, From 53e14836d95c1d200487ef3659a8f194e9f6fba8 Mon Sep 17 00:00:00 2001 From: naente Date: Sun, 28 Aug 2022 22:54:22 +0900 Subject: [PATCH 53/77] docs: $db->getFieldData() data explanation is not complete --- user_guide_src/source/database/metadata.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index 75459e7e053c..7276f4730b40 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -103,8 +103,8 @@ database: - name - column name - type - the type of the column - max_length - maximum length of the column -- primary_key - integer ``1`` if the column is a primary key (all integer ``1``, even if there are multiple primary keys), otherwise integer ``0`` -- nullable - boolean ``true`` if the column is nullable, otherwise boolean ``false`` +- primary_key - integer ``1`` if the column is a primary key (all integer ``1``, even if there are multiple primary keys), otherwise integer ``0`` (This field is currently only available for MySQL and SQLite3) +- nullable - boolean ``true`` if the column is nullable, otherwise boolean ``false`` (This field is currently not available in SQL Server) - default - the default value List the Indexes in a Table From b6d9f91fd8c1cee4718a5d4d964efc48b7608316 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 07:41:18 +0900 Subject: [PATCH 54/77] docs: fix incorrect links to the spark page The page spark_commands explains how to use spark, the page cli_commands explains how to create a command. --- user_guide_src/source/incoming/routing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 2656b2ea7d1e..86f53d3be3df 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -329,7 +329,7 @@ available from the command line: .. literalinclude:: routing/032.php .. note:: It is recommended to use Spark Commands for CLI scripts instead of calling controllers via CLI. - See the :doc:`../cli/spark_commands` page for detailed information. + See the :doc:`../cli/cli_commands` page for detailed information. .. warning:: If you enable :ref:`auto-routing` and place the command file in **app/Controllers**, anyone could access the command with the help of auto-routing via HTTP. @@ -717,7 +717,7 @@ In this example, if the user were to visit **example.com/products**, and a ``Pro Confirming Routes ***************** -CodeIgniter has the following :doc:`command ` to display all routes. +CodeIgniter has the following :doc:`command ` to display all routes. .. _spark-routes: From 5e7b4fe99d670471d4f0e93586d36574ae81cb1e Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 08:04:25 +0900 Subject: [PATCH 55/77] test: fix incorrect assertions --- tests/system/HTTP/CLIRequestTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system/HTTP/CLIRequestTest.php b/tests/system/HTTP/CLIRequestTest.php index e8e57dbc611b..c9c1573ec313 100644 --- a/tests/system/HTTP/CLIRequestTest.php +++ b/tests/system/HTTP/CLIRequestTest.php @@ -599,16 +599,16 @@ public function testGetGet() public function testGetPost() { - $this->assertSame([], $this->request->getGet()); + $this->assertSame([], $this->request->getPost()); } public function testGetPostGet() { - $this->assertSame([], $this->request->getGet()); + $this->assertSame([], $this->request->getPostGet()); } public function testGetGetPost() { - $this->assertSame([], $this->request->getGet()); + $this->assertSame([], $this->request->getGetPost()); } } From 6aafff0611b934091d18ef7111966dbfffcdf67c Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 08:06:25 +0900 Subject: [PATCH 56/77] fix: add missing CLIRequest::getLocale() Call to undefined method CodeIgniter\HTTP\CLIRequest::getLocale() --- system/HTTP/CLIRequest.php | 10 ++++++++++ tests/system/HTTP/CLIRequestTest.php | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 0b6c2b83a12d..e81ae9cabbd9 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -12,6 +12,7 @@ namespace CodeIgniter\HTTP; use Config\App; +use Locale; use RuntimeException; /** @@ -280,4 +281,13 @@ private function returnNullOrEmptyArray($index) { return ($index === null || is_array($index)) ? [] : null; } + + /** + * Gets the current locale, with a fallback to the default + * locale if none is set. + */ + public function getLocale(): string + { + return Locale::getDefault(); + } } diff --git a/tests/system/HTTP/CLIRequestTest.php b/tests/system/HTTP/CLIRequestTest.php index c9c1573ec313..b294d1621b2b 100644 --- a/tests/system/HTTP/CLIRequestTest.php +++ b/tests/system/HTTP/CLIRequestTest.php @@ -611,4 +611,9 @@ public function testGetGetPost() { $this->assertSame([], $this->request->getGetPost()); } + + public function testGetLocale() + { + $this->assertSame('en', $this->request->getLocale()); + } } From cd450e7ac28688225ee06b525eca472af79e189f Mon Sep 17 00:00:00 2001 From: MGatner Date: Mon, 29 Aug 2022 00:46:26 +0000 Subject: [PATCH 57/77] Prep for 4.2.5 release --- CHANGELOG.md | 26 +++++++++++++ system/CodeIgniter.php | 2 +- user_guide_src/source/changelogs/index.rst | 1 + user_guide_src/source/changelogs/v4.2.5.rst | 2 +- user_guide_src/source/changelogs/v4.2.6.rst | 37 +++++++++++++++++++ user_guide_src/source/conf.py | 2 +- .../source/installation/upgrade_425.rst | 18 +++++++++ .../source/installation/upgrading.rst | 1 + 8 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 user_guide_src/source/changelogs/v4.2.6.rst create mode 100644 user_guide_src/source/installation/upgrade_425.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index 174689cf5a2e..42709f5ec371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## [v4.2.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.5) (2022-08-28) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.4...v4.2.5) + +### Breaking Changes +* Add $cached param to BaseConnection::tableExists() by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6364 +* Fix validation custom error asterisk field by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/6378 + +### Fixed Bugs +* fix: Email class may not log an error when it fails to send by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6362 +* fix: Response::download() causes TypeError by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6361 +* fix: command usages by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6402 +* Fix: The subquery adds a prefix for the table alias. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6390 +* Fix Sqlite Table::createTable() by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6396 +* docs: add missing `@method` `groupBy()` in Model by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6433 +* fix: CLIRequest Erros in CLI by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6421 +* fix: Call to undefined method CodeIgniter\HTTP\CLIRequest::getLocale() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6442 + +### Enhancements +* chore: update Kint to 4.2.0 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6436 + +### Refactoring +* refactor: add test for DownloadResponse by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6375 +* refactor: ValidationTest by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6382 +* refactor: remove unused `_parent_name` in BaseBuilder::objectToArray() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6427 +* Remove unneeded abstract `handle()` method by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6434 + ## [v4.2.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.4) (2022-08-13) [Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.3...v4.2.4) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 157723b188d8..d4cc2944d7f7 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -47,7 +47,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.2.4'; + public const CI_VERSION = '4.2.5'; /** * App startup time. diff --git a/user_guide_src/source/changelogs/index.rst b/user_guide_src/source/changelogs/index.rst index cff08f07c9ec..0c1bf02a8bf0 100644 --- a/user_guide_src/source/changelogs/index.rst +++ b/user_guide_src/source/changelogs/index.rst @@ -12,6 +12,7 @@ See all the changes. .. toctree:: :titlesonly: + v4.2.6 v4.2.5 v4.2.4 v4.2.3 diff --git a/user_guide_src/source/changelogs/v4.2.5.rst b/user_guide_src/source/changelogs/v4.2.5.rst index 59693fae6bb9..c9ff36b05508 100644 --- a/user_guide_src/source/changelogs/v4.2.5.rst +++ b/user_guide_src/source/changelogs/v4.2.5.rst @@ -1,7 +1,7 @@ Version 4.2.5 ############# -Release Date: Unreleased +Release Date: August 28, 2022 **4.2.5 release of CodeIgniter4** diff --git a/user_guide_src/source/changelogs/v4.2.6.rst b/user_guide_src/source/changelogs/v4.2.6.rst new file mode 100644 index 000000000000..d1a467297660 --- /dev/null +++ b/user_guide_src/source/changelogs/v4.2.6.rst @@ -0,0 +1,37 @@ +Version 4.2.6 +############# + +Release Date: Unreleased + +**4.2.6 release of CodeIgniter4** + +.. contents:: + :local: + :depth: 2 + +BREAKING +******** + +none. + +Enhancements +************ + +none. + +Changes +******* + +none. + +Deprecations +************ + +none. + +Bugs Fixed +********** + +none. + +See the repo's `CHANGELOG.md `_ for a complete list of bugs fixed. diff --git a/user_guide_src/source/conf.py b/user_guide_src/source/conf.py index 0c5ce70cd366..6d2d282ddb7e 100644 --- a/user_guide_src/source/conf.py +++ b/user_guide_src/source/conf.py @@ -24,7 +24,7 @@ version = '4.2' # The full version, including alpha/beta/rc tags. -release = '4.2.4' +release = '4.2.5' # -- General configuration --------------------------------------------------- diff --git a/user_guide_src/source/installation/upgrade_425.rst b/user_guide_src/source/installation/upgrade_425.rst new file mode 100644 index 000000000000..930f188c811d --- /dev/null +++ b/user_guide_src/source/installation/upgrade_425.rst @@ -0,0 +1,18 @@ +############################# +Upgrading from 4.2.3 to 4.2.5 +############################# + +Please refer to the upgrade instructions corresponding to your installation method. + +- :ref:`Composer Installation App Starter Upgrading ` +- :ref:`Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading ` +- :ref:`Manual Installation Upgrading ` + +.. contents:: + :local: + :depth: 2 + +Project Files +************* + +Version ``4.2.5`` did not alter any project files. diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index 84695eb58ac2..a31a3164d9c2 100644 --- a/user_guide_src/source/installation/upgrading.rst +++ b/user_guide_src/source/installation/upgrading.rst @@ -16,6 +16,7 @@ See also :doc:`./backward_compatibility_notes`. backward_compatibility_notes + upgrade_425 upgrade_423 upgrade_422 upgrade_421 From 2e3dd46d42ba9c9953e1df55ed64860c3341decc Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 10:32:27 +0900 Subject: [PATCH 58/77] chore: add `rector process --dry-run` to `composer analyze` --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3080b701539b..ec04c8a0b02d 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,10 @@ "CodeIgniter\\ComposerScripts::postUpdate", "bash -c \"if [ -f admin/setup.sh ]; then bash admin/setup.sh; fi\"" ], - "analyze": "phpstan analyse", + "analyze": [ + "phpstan analyze", + "rector process --dry-run" + ], "test": "phpunit", "cs": [ "php-cs-fixer fix --ansi --verbose --dry-run --diff --config=.php-cs-fixer.user-guide.php", From cb60d9394f8a5a8ff9bce8ad25461e15f2f8dd81 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 10:32:49 +0900 Subject: [PATCH 59/77] chore: add composer script aliaes --- composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ec04c8a0b02d..01a5af11b1b6 100644 --- a/composer.json +++ b/composer.json @@ -74,6 +74,7 @@ "phpstan analyze", "rector process --dry-run" ], + "sa": "@analyze", "test": "phpunit", "cs": [ "php-cs-fixer fix --ansi --verbose --dry-run --diff --config=.php-cs-fixer.user-guide.php", @@ -84,7 +85,8 @@ "php-cs-fixer fix --ansi --verbose --diff --config=.php-cs-fixer.user-guide.php", "php-cs-fixer fix --ansi --verbose --diff --config=.php-cs-fixer.no-header.php", "php-cs-fixer fix --ansi --verbose --diff" - ] + ], + "style": "@cs-fix" }, "scripts-descriptions": { "analyze": "Run static analysis", From a589af0d6164dcee628afbff45ffff859f57c8f1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 10:40:35 +0900 Subject: [PATCH 60/77] docs: update pull_request.md --- contributing/pull_request.md | 1 - 1 file changed, 1 deletion(-) diff --git a/contributing/pull_request.md b/contributing/pull_request.md index ed6d0dfde9e6..f1e29304e305 100644 --- a/contributing/pull_request.md +++ b/contributing/pull_request.md @@ -252,7 +252,6 @@ The best way to contribute is to fork the CodeIgniter4 repository, and "clone" t - Commit messages are expected to be descriptive of why and what you changed specifically. Commit messages like "Fixes #1234" would be asked by the reviewer to be revised. [Atomic commit](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) is recommended. See [Contribution Workflow](./workflow.md#commit-messages) for details. 9. If you have touched PHP code, run static analysis. - `> composer analyze` - - `> vendor/bin/rector process` 10. Run unit tests on the specific file you modified. If there are no existing tests yet, please create one. - `> vendor/bin/phpunit tests/system/path/to/file/you/modified` - Make sure the tests pass to have a higher chance of merging. From 14bfd1f71945b48cbc355379bcdf06d6fad4989c Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 10:48:21 +0900 Subject: [PATCH 61/77] docs: add Changing a commit message --- contributing/workflow.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contributing/workflow.md b/contributing/workflow.md index 50673ce10166..0ebe3270d5cd 100644 --- a/contributing/workflow.md +++ b/contributing/workflow.md @@ -179,6 +179,10 @@ For instance, to commit your work from a debugging session: Just make sure that your commits in a feature branch are all related. +### Changing a commit message + +See . + ### When you work on two features If you are working on two features at a time, then you will want to From 71a4bed4812d4ca9ec98b1c03c56d7c3af4f17b7 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 10:50:23 +0900 Subject: [PATCH 62/77] docs: change letter cases in titles --- contributing/workflow.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contributing/workflow.md b/contributing/workflow.md index 0ebe3270d5cd..cde1f2eb5de6 100644 --- a/contributing/workflow.md +++ b/contributing/workflow.md @@ -66,7 +66,7 @@ Clone your repository, leaving a local folder for you to work with: > git clone ORIGIN_URL ``` -## Syncing your repository +## Syncing Your Repository Within your local repository, Git will have created an alias, **origin**, for the GitHub repository it is bound to. You want to create @@ -179,11 +179,11 @@ For instance, to commit your work from a debugging session: Just make sure that your commits in a feature branch are all related. -### Changing a commit message +### Changing a Commit Message See . -### When you work on two features +### When You Work on Two Features If you are working on two features at a time, then you will want to switch between them to keep the contributions separate. For instance: @@ -318,7 +318,7 @@ And finally push your local branch to your GitHub repository: > git push --force-with-lease origin fix/problem123 ``` -## If you sent to the wrong branch +## If You Sent to the Wrong Branch If you have sent a PR to the wrong branch, you need to create a new PR branch. From 536e5b1b7e1ae5ad4f53413bcace3394100130fb Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Aug 2022 21:08:32 +0900 Subject: [PATCH 63/77] docs: add about controllers namespace in routing --- user_guide_src/source/incoming/routing.rst | 21 ++++++++++++++++++- .../source/incoming/routing/063.php | 4 ++++ .../source/incoming/routing/064.php | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 user_guide_src/source/incoming/routing/063.php create mode 100644 user_guide_src/source/incoming/routing/064.php diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 86f53d3be3df..98c9191dab15 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -47,6 +47,23 @@ You can supply multiple verbs that a route should match by passing them in as an .. literalinclude:: routing/004.php +Controller's Namespace +====================== + +If a controller name is stated without beginning with ``\``, the :ref:`routing-default-namespace` will be prepended: + +.. literalinclude:: routing/063.php + +If you put ``\`` at the beginning, it is treated as a fully qualified class name: + +.. literalinclude:: routing/064.php + +You can also specify the namespace with the ``namespace`` option: + +.. literalinclude:: routing/038.php + +See :ref:`assigning-namespace` for details. + Placeholders ============ @@ -394,7 +411,7 @@ You specify an array for the filter value: Assigning Namespace ------------------- -While a default namespace will be prepended to the generated controllers (see below), you can also specify +While a :ref:`routing-default-namespace` will be prepended to the generated controllers, you can also specify a different namespace to be used in any options array, with the ``namespace`` option. The value should be the namespace you want modified: @@ -480,6 +497,8 @@ Routes Configuration Options The RoutesCollection class provides several options that affect all routes, and can be modified to meet your application's needs. These options are available at the top of **app/Config/Routes.php**. +.. _routing-default-namespace: + Default Namespace ================= diff --git a/user_guide_src/source/incoming/routing/063.php b/user_guide_src/source/incoming/routing/063.php new file mode 100644 index 000000000000..554fe1160ae8 --- /dev/null +++ b/user_guide_src/source/incoming/routing/063.php @@ -0,0 +1,4 @@ +get('users', 'Users::index'); diff --git a/user_guide_src/source/incoming/routing/064.php b/user_guide_src/source/incoming/routing/064.php new file mode 100644 index 000000000000..8d6df0548e30 --- /dev/null +++ b/user_guide_src/source/incoming/routing/064.php @@ -0,0 +1,4 @@ +get('blog', '\Acme\Blog\Controllers\Home::list'); From a8c69e87a46b33c3eaedd6cbbeade5022910617a Mon Sep 17 00:00:00 2001 From: daycry Date: Mon, 29 Aug 2022 17:26:38 +0200 Subject: [PATCH 64/77] - FIX: in command prompt at SYSTEMPATH\Validation\FileRules.php:42 Backtrace: 1 [internal function] CodeIgniter\Debug\Exceptions()->errorHandler(2, 'assert(): assert($request instanceof IncomingRequest) failed', 'C:\\laragon\\www\\test\\vendor\\codeigniter4\\framework\\system\\Validation\\FileRules.php', 42, [...]) --- system/Validation/FileRules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index aec9d9efa2a4..ea3b8030b7aa 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -39,7 +39,7 @@ public function __construct(?RequestInterface $request = null) $request = Services::request(); } - assert($request instanceof IncomingRequest); + assert($request instanceof Request); $this->request = $request; } From 8e1e695cb92f47fd16559dc87d7a34eee2f6733a Mon Sep 17 00:00:00 2001 From: daycry Date: Tue, 30 Aug 2022 09:20:38 +0200 Subject: [PATCH 65/77] Update FileRules.php --- system/Validation/FileRules.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index ea3b8030b7aa..d1e470171b7e 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Validation; use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\Request; use CodeIgniter\HTTP\RequestInterface; use Config\Mimes; @@ -39,7 +40,7 @@ public function __construct(?RequestInterface $request = null) $request = Services::request(); } - assert($request instanceof Request); + assert($request instanceof CLIRequest ?? IncomingRequest) $this->request = $request; } From 9e34a8f4492bb1a03c37a363388818b3398f6de1 Mon Sep 17 00:00:00 2001 From: daycry Date: Tue, 30 Aug 2022 09:42:03 +0200 Subject: [PATCH 66/77] Update FileRules.php --- system/Validation/FileRules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index d1e470171b7e..96c3071e151f 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -40,7 +40,7 @@ public function __construct(?RequestInterface $request = null) $request = Services::request(); } - assert($request instanceof CLIRequest ?? IncomingRequest) + assert($request instanceof IncomingRequest || $request instanceof CLIRequest); $this->request = $request; } From 426873f6a6c110dcebb0fafb96971757c13187e5 Mon Sep 17 00:00:00 2001 From: daycry Date: Tue, 30 Aug 2022 10:00:45 +0200 Subject: [PATCH 67/77] Update FileRules.php --- system/Validation/FileRules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index 96c3071e151f..ba054d134b6b 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -11,8 +11,8 @@ namespace CodeIgniter\Validation; -use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\CLIRequest; +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\Request; use CodeIgniter\HTTP\RequestInterface; use Config\Mimes; From e046890c453313c58ec402b1160d7ee2e8b297b6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 30 Aug 2022 20:56:12 +0900 Subject: [PATCH 68/77] docs: improve sample code --- user_guide_src/source/incoming/routing/063.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/incoming/routing/063.php b/user_guide_src/source/incoming/routing/063.php index 554fe1160ae8..4996345ed7ae 100644 --- a/user_guide_src/source/incoming/routing/063.php +++ b/user_guide_src/source/incoming/routing/063.php @@ -1,4 +1,4 @@ get('users', 'Users::index'); +// Routes to \App\Controllers\Api\Users::update() +$routes->post('api/users', 'Api\Users::update'); From 1aeb5b3ab4b015926588f3acfaf2e0ac9e7e4a80 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 31 Aug 2022 10:17:28 +0900 Subject: [PATCH 69/77] test: restore changed locale in tearDown() --- tests/system/I18n/TimeDifferenceTest.php | 11 +++++++++++ tests/system/I18n/TimeTest.php | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/system/I18n/TimeDifferenceTest.php b/tests/system/I18n/TimeDifferenceTest.php index 7141f71769d3..cea4d34f3344 100644 --- a/tests/system/I18n/TimeDifferenceTest.php +++ b/tests/system/I18n/TimeDifferenceTest.php @@ -19,14 +19,25 @@ */ final class TimeDifferenceTest extends CIUnitTestCase { + private string $currentLocale; + protected function setUp(): void { parent::setUp(); helper('date'); + + $this->currentLocale = Locale::getDefault(); Locale::setDefault('America/Chicago'); } + protected function tearDown(): void + { + parent::tearDown(); + + Locale::setDefault($this->currentLocale); + } + public function testDifferenceBasics() { $current = Time::parse('March 10, 2017', 'America/Chicago'); diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index 2db4fcc48c2b..e541d17c049e 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -25,14 +25,25 @@ */ final class TimeTest extends CIUnitTestCase { + private string $currentLocale; + protected function setUp(): void { parent::setUp(); helper('date'); + + $this->currentLocale = Locale::getDefault(); Locale::setDefault('en_US'); } + protected function tearDown(): void + { + parent::tearDown(); + + Locale::setDefault($this->currentLocale); + } + public function testNewTimeNow() { $formatter = new IntlDateFormatter( From 69c407b38c20e8603065e1722bb59e0176eafd62 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 31 Aug 2022 13:42:13 +0900 Subject: [PATCH 70/77] docs: update PHPDoc in Entity --- system/Entity/Entity.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php index 39adf0661215..89ddd2afaac5 100644 --- a/system/Entity/Entity.php +++ b/system/Entity/Entity.php @@ -25,6 +25,7 @@ use CodeIgniter\Entity\Cast\URICast; use CodeIgniter\Entity\Exceptions\CastException; use CodeIgniter\I18n\Time; +use DateTime; use Exception; use JsonSerializable; use ReturnTypeWillChange; @@ -147,7 +148,7 @@ public function fill(?array $data = null) * * @param bool $onlyChanged If true, only return values that have changed since object creation * @param bool $cast If true, properties will be cast. - * @param bool $recursive If true, inner entities will be casted as array as well. + * @param bool $recursive If true, inner entities will be cast as array as well. */ public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recursive = false): array { @@ -189,7 +190,7 @@ public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recu * Returns the raw values of the current attributes. * * @param bool $onlyChanged If true, only return values that have changed since object creation - * @param bool $recursive If true, inner entities will be casted as array as well. + * @param bool $recursive If true, inner entities will be cast as array as well. */ public function toRawArray(bool $onlyChanged = false, bool $recursive = false): array { @@ -247,7 +248,7 @@ public function syncOriginal() * was created. Or, without a parameter, checks if any * properties have changed. * - * @param string $key + * @param string|null $key class property */ public function hasChanged(?string $key = null): bool { @@ -308,11 +309,11 @@ protected function mapProperty(string $key) * Converts the given string|timestamp|DateTime|Time instance * into the "CodeIgniter\I18n\Time" object. * - * @param mixed $value + * @param DateTime|float|int|string|Time $value * * @throws Exception * - * @return mixed|Time + * @return Time */ protected function mutateDate($value) { @@ -324,13 +325,13 @@ protected function mutateDate($value) * Add ? at the beginning of $type (i.e. ?string) to get NULL * instead of casting $value if $value === null * - * @param mixed $value Attribute value - * @param string $attribute Attribute name - * @param string $method Allowed to "get" and "set" + * @param bool|float|int|string|null $value Attribute value + * @param string $attribute Attribute name + * @param string $method Allowed to "get" and "set" * * @throws CastException * - * @return mixed + * @return array|bool|float|int|object|string|null */ protected function castAs($value, string $attribute, string $method = 'get') { @@ -424,7 +425,7 @@ public function cast(?bool $cast = null) * $this->my_property = $p; * $this->setMyProperty() = $p; * - * @param mixed|null $value + * @param array|bool|float|int|object|string|null $value * * @throws Exception * @@ -470,8 +471,9 @@ public function __set(string $key, $value = null) * $p = $this->getMyProperty() * * @throws Exception + * @params string $key class property * - * @return mixed + * @return array|bool|float|int|object|string|null */ public function __get(string $key) { From 528eed7f8b637ea2a04e3752cd542519848ceff5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 31 Aug 2022 17:57:20 +0900 Subject: [PATCH 71/77] test: fix invalid locale value locale Is a BCP 47 compliant language tag. See https://www.php.net/manual/en/locale.setdefault.php --- tests/system/I18n/TimeDifferenceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/I18n/TimeDifferenceTest.php b/tests/system/I18n/TimeDifferenceTest.php index cea4d34f3344..4f8a02f038af 100644 --- a/tests/system/I18n/TimeDifferenceTest.php +++ b/tests/system/I18n/TimeDifferenceTest.php @@ -28,7 +28,7 @@ protected function setUp(): void helper('date'); $this->currentLocale = Locale::getDefault(); - Locale::setDefault('America/Chicago'); + Locale::setDefault('en-US'); } protected function tearDown(): void From 3b8ee4f9a49f76fd0283f78f8a42051817e6d426 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Mon, 22 Aug 2022 14:03:27 +0300 Subject: [PATCH 72/77] Fix broken caching system when array of allowed parameters configuration used Cache Include Query String in the /Config/Cache.php (line 73) suggest to the user 3 options to choose from: false = Disabled true = Enabled, take all query parameters into account. Please be aware that this may result in numerous cache files generated for the same page over and over again. array('q') = Enabled, but only take into account the specified list of query parameters. However, last option with array of important parameters just doesn't work. We were using third option, because we had one important parameter (color) on the site, and content were dynamic based on this parameter. But then we`ve seen huge amount of cache files on our server, thousands and thousands. Turned out, even when using array of parameters in Config/Cache $cacheQueryString option - CodeIgniter was creating new cache file for any requests just like it was set to 'true'. Cache fles generated for the same page over and over again, performance were slow. Since Google Ads, Bing, Yahoo and all marketing platforms always adding dynamic parameters like 'click_id' for example, 'utm_source', 'utm_medium' and other, cache file was creating for all requests from the paid traffic. It was almost impossible to catch, but we saw weird amount of caching files, and that helped us to see the problem. I`ve made testing on the clean CodeIgniter 4 installations, latest version. Happened there too. So I`ve checked the source code, and I saw there there is no difference between true/array options, theyjust work same. I believe this is very missleading, because Config/Cache showing 'array' option and suggest to use it. This fixes should help, and take into the account array option. Thank you --- system/CodeIgniter.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index d4cc2944d7f7..86c2eab81046 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -730,9 +730,12 @@ protected function generateCacheName(Cache $config): string } $uri = $this->request->getUri(); - if ($config->cacheQueryString) { - $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()); + if (is_array($config->cacheQueryString)){ + $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery(array('only' => $config->cacheQueryString))); + }else{ + $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()); + } } else { $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath()); } From 6460513ab498644a27b1879a4e660f47164db976 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Tue, 23 Aug 2022 12:24:37 +0300 Subject: [PATCH 73/77] Fix coding style --- system/CodeIgniter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 86c2eab81046..2f4db3da0407 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -731,9 +731,9 @@ protected function generateCacheName(Cache $config): string $uri = $this->request->getUri(); if ($config->cacheQueryString) { - if (is_array($config->cacheQueryString)){ - $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery(array('only' => $config->cacheQueryString))); - }else{ + if (is_array($config->cacheQueryString)) { + $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery(['only' => $config->cacheQueryString])); + } else { $name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()); } } else { From 903b78dccc46d2f24ac718847ca87a455ba29e18 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Tue, 23 Aug 2022 18:43:44 +0300 Subject: [PATCH 74/77] Created 3 tests for caching with different cacheQueryString options Three new tests, for all suggested options of $cacheQueryString in the Config/Cache.php cacheQueryString = true cacheQueryString = false cacheQueryString = array('q') --- tests/system/CodeIgniterTest.php | 199 +++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index d1b6793e4e89..9ae846550f17 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -18,6 +18,7 @@ use CodeIgniter\Test\Filters\CITestStreamFilter; use CodeIgniter\Test\Mock\MockCodeIgniter; use Config\App; +use Config\Cache; use Config\Filters; use Config\Modules; use Tests\Support\Filters\Customfilter; @@ -622,4 +623,202 @@ public function testPageCacheSendSecureHeaders() stream_filter_remove($outputStreamFilter); stream_filter_remove($errorStreamFilter); } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 + */ + public function testPageCacheWithCacheQueryStringFalse() + { + // Suppress command() output + CITestStreamFilter::$buffer = ''; + $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Create cache config with disabled cacheQueryString + $cacheConfig = new Cache(); + $cacheConfig->cacheQueryString = false; + + // Clear cache before starting the test + command('cache:clear'); + + // Calculate amount of items in the cache before the test + $cache = \Config\Services::cache(); + $cache_start_counter = count($cache->getCacheInfo()); + + // URLs for testing cache configuration. Only one cache file should be created for all 5 URL`s, since we have disabled cacheQueryString + $testing_urls = [ + 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page + 'test?important_parameter=1', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + 'test?important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration + ]; + + // Generate request to each URL from the testing array + foreach ($testing_urls as $key => $testing_url) { + $_SERVER['REQUEST_URI'] = '/' . $testing_url; + $routes = Services::routes(true); + $routes->add($testing_url, static function () { + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + $response = Services::response(); + $string = 'This is a test page, to check cache configuration'; + + return $response->setBody($string); + }); + + // Inject router + $router = Services::router($routes, Services::request(null, false)); + Services::injectMock('router', $router); + + // Cache the page output using default caching function and our own $cacheConfig + $this->codeigniter->useSafeOutput(true)->run(); + $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration + } + + // Calculate how much cached items exist in the cache after the test requests + $cache_end_counter = count($cache->getCacheInfo()); + $new_pages_cached = $cache_end_counter - $cache_start_counter; + + // Clear cache after the test + command('cache:clear'); + + // We expect only one new page in the cache, because GET parameters should be ignored with cacheQueryString = false + $this->assertSame(1, $new_pages_cached); + + // Remove stream filters + stream_filter_remove($outputStreamFilter); + stream_filter_remove($errorStreamFilter); + } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 + */ + public function testPageCacheWithCacheQueryStringTrue() + { + // Suppress command() output + CITestStreamFilter::$buffer = ''; + $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Create cache config with enabled cacheQueryString + $cacheConfig = new Cache(); + $cacheConfig->cacheQueryString = true; + + // Clear cache before starting the test + command('cache:clear'); + + // Calculate amount of items in the cache before the test + $cache = \Config\Services::cache(); + $cache_start_counter = count($cache->getCacheInfo()); + + // URLs for testing cache configuration. New 5 files should be created for all 5 URL`s, since we have enabled cacheQueryString, and all GET parameters combinations is unique + $testing_urls = [ + 'test', // Should be cached because its a unique URL and not exist in the cache + 'test?important_parameter=1', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test?important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test?important_parameter=1¬_important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + ]; + + // Generate request to each URL from the testing array + foreach ($testing_urls as $key => $testing_url) { + $_SERVER['REQUEST_URI'] = '/' . $testing_url; + $routes = Services::routes(true); + $routes->add($testing_url, static function () { + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + $response = Services::response(); + $string = 'This is a test page, to check cache configuration'; + + return $response->setBody($string); + }); + + // Inject router + $router = Services::router($routes, Services::request(null, false)); + Services::injectMock('router', $router); + + // Cache the page output using default caching function and our own $cacheConfig + $this->codeigniter->useSafeOutput(true)->run(); + $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration + } + + // Calculate how much cached items exist in the cache after the test requests + $cache_end_counter = count($cache->getCacheInfo()); + $new_pages_cached = $cache_end_counter - $cache_start_counter; + + // Clear cache after the test + command('cache:clear'); + + // We expect 5 new pages in the cache, because GET parameters should be counted as unique pages with cacheQueryString = true + $this->assertSame(5, $new_pages_cached); + + // Remove stream filters + stream_filter_remove($outputStreamFilter); + stream_filter_remove($errorStreamFilter); + } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 + */ + public function testPageCacheWithCacheQueryStringArray() + { + // Suppress command() output + CITestStreamFilter::$buffer = ''; + $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); + $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); + + // Create cache config with enabled cacheQueryString + $cacheConfig = new Cache(); + $cacheConfig->cacheQueryString = ['important_parameter']; + + // Clear cache before starting the test + command('cache:clear'); + + // Calculate amount of items in the cache before the test + $cache = \Config\Services::cache(); + $cache_start_counter = count($cache->getCacheInfo()); + + // URLs for testing cache configuration. Only 3 files should be created for this 5 URL`s, since cacheQueryString is an array of important parameters only, and all other GET parameters (not in the array) should be ignored + $testing_urls = [ + 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page + 'test?important_parameter=1', // Should be cached because important parameter exist and have unique value 1 + 'test?important_parameter=2', // Should be cached because important parameter exist and have unique value 2 + 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored + ]; + + // Generate request to each URL from the testing array + foreach ($testing_urls as $key => $testing_url) { + $_SERVER['REQUEST_URI'] = '/' . $testing_url; + $routes = Services::routes(true); + $routes->add($testing_url, static function () { + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + $response = Services::response(); + $string = 'This is a test page, to check cache configuration'; + + return $response->setBody($string); + }); + + // Inject router + $router = Services::router($routes, Services::request(null, false)); + Services::injectMock('router', $router); + + // Cache the page output using default caching function and our own $cacheConfig + $this->codeigniter->useSafeOutput(true)->run(); + $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration + } + + // Calculate how much cached items exist in the cache after the test requests + $cache_end_counter = count($cache->getCacheInfo()); + $new_pages_cached = $cache_end_counter - $cache_start_counter; + + // Clear cache after the test + command('cache:clear'); + + // We expect 3 new pages in the cache, because only important GET parameters from array should be counted as unique pages with cacheQueryString = ['important_parameter'] + $this->assertSame(3, $new_pages_cached); + + // Remove stream filters + stream_filter_remove($outputStreamFilter); + stream_filter_remove($errorStreamFilter); + } } From 2a4a99005963f6891ae8e21d8d05720bcfe984b9 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Tue, 23 Aug 2022 20:47:51 +0300 Subject: [PATCH 75/77] Refactoring syntax edits to pass Rector 7.4 and 8.0 check --- tests/system/CodeIgniterTest.php | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 9ae846550f17..4ee789f43f2b 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -642,11 +642,11 @@ public function testPageCacheWithCacheQueryStringFalse() command('cache:clear'); // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cache_start_counter = count($cache->getCacheInfo()); + $cache = \Config\Services::cache(); + $cacheStartCounter = count($cache->getCacheInfo()); // URLs for testing cache configuration. Only one cache file should be created for all 5 URL`s, since we have disabled cacheQueryString - $testing_urls = [ + $testingUrls = [ 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page 'test?important_parameter=1', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration 'test?important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration @@ -655,10 +655,10 @@ public function testPageCacheWithCacheQueryStringFalse() ]; // Generate request to each URL from the testing array - foreach ($testing_urls as $key => $testing_url) { - $_SERVER['REQUEST_URI'] = '/' . $testing_url; + foreach ($testingUrls as $testingUrl) { + $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); - $routes->add($testing_url, static function () { + $routes->add($testingUrl, static function () { CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -676,14 +676,14 @@ public function testPageCacheWithCacheQueryStringFalse() } // Calculate how much cached items exist in the cache after the test requests - $cache_end_counter = count($cache->getCacheInfo()); - $new_pages_cached = $cache_end_counter - $cache_start_counter; + $cacheEndCounter = count($cache->getCacheInfo()); + $newPagesCached = $cacheEndCounter - $cacheStartCounter; // Clear cache after the test command('cache:clear'); // We expect only one new page in the cache, because GET parameters should be ignored with cacheQueryString = false - $this->assertSame(1, $new_pages_cached); + $this->assertSame(1, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); @@ -708,11 +708,11 @@ public function testPageCacheWithCacheQueryStringTrue() command('cache:clear'); // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cache_start_counter = count($cache->getCacheInfo()); + $cache = \Config\Services::cache(); + $cacheStartCounter = count($cache->getCacheInfo()); // URLs for testing cache configuration. New 5 files should be created for all 5 URL`s, since we have enabled cacheQueryString, and all GET parameters combinations is unique - $testing_urls = [ + $testingUrls = [ 'test', // Should be cached because its a unique URL and not exist in the cache 'test?important_parameter=1', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache 'test?important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache @@ -721,10 +721,10 @@ public function testPageCacheWithCacheQueryStringTrue() ]; // Generate request to each URL from the testing array - foreach ($testing_urls as $key => $testing_url) { - $_SERVER['REQUEST_URI'] = '/' . $testing_url; + foreach ($testingUrls as $testingUrl) { + $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); - $routes->add($testing_url, static function () { + $routes->add($testingUrl, static function () { CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -742,14 +742,14 @@ public function testPageCacheWithCacheQueryStringTrue() } // Calculate how much cached items exist in the cache after the test requests - $cache_end_counter = count($cache->getCacheInfo()); - $new_pages_cached = $cache_end_counter - $cache_start_counter; + $cacheEndCounter = count($cache->getCacheInfo()); + $newPagesCached = $cacheEndCounter - $cacheStartCounter; // Clear cache after the test command('cache:clear'); // We expect 5 new pages in the cache, because GET parameters should be counted as unique pages with cacheQueryString = true - $this->assertSame(5, $new_pages_cached); + $this->assertSame(5, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); @@ -774,11 +774,11 @@ public function testPageCacheWithCacheQueryStringArray() command('cache:clear'); // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cache_start_counter = count($cache->getCacheInfo()); + $cache = \Config\Services::cache(); + $cacheStartCounter = count($cache->getCacheInfo()); // URLs for testing cache configuration. Only 3 files should be created for this 5 URL`s, since cacheQueryString is an array of important parameters only, and all other GET parameters (not in the array) should be ignored - $testing_urls = [ + $testingUrls = [ 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page 'test?important_parameter=1', // Should be cached because important parameter exist and have unique value 1 'test?important_parameter=2', // Should be cached because important parameter exist and have unique value 2 @@ -787,10 +787,10 @@ public function testPageCacheWithCacheQueryStringArray() ]; // Generate request to each URL from the testing array - foreach ($testing_urls as $key => $testing_url) { - $_SERVER['REQUEST_URI'] = '/' . $testing_url; + foreach ($testingUrls as $testingUrl) { + $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); - $routes->add($testing_url, static function () { + $routes->add($testingUrl, static function () { CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -808,14 +808,14 @@ public function testPageCacheWithCacheQueryStringArray() } // Calculate how much cached items exist in the cache after the test requests - $cache_end_counter = count($cache->getCacheInfo()); - $new_pages_cached = $cache_end_counter - $cache_start_counter; + $cacheEndCounter = count($cache->getCacheInfo()); + $newPagesCached = $cacheEndCounter - $cacheStartCounter; // Clear cache after the test command('cache:clear'); // We expect 3 new pages in the cache, because only important GET parameters from array should be counted as unique pages with cacheQueryString = ['important_parameter'] - $this->assertSame(3, $new_pages_cached); + $this->assertSame(3, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); From 5237e30e5ea7c21eb07f2931a7d99ddf5f845ac4 Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Wed, 24 Aug 2022 00:59:55 +0300 Subject: [PATCH 76/77] Updating the Cache test to use data provider Using data provider for 1 cache test instead of 3 different tests. https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html#writing-tests-for-phpunit-data-providers --- tests/system/CodeIgniterTest.php | 163 +++++-------------------------- 1 file changed, 22 insertions(+), 141 deletions(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 4ee789f43f2b..d14173a4ad35 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -625,18 +625,23 @@ public function testPageCacheSendSecureHeaders() } /** + * @param mixed $cacheQueryStringValue + * @param int $expectedPagesInCache + * @param array $testingUrls + * @dataProvider cacheQueryStringProvider + * * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 */ - public function testPageCacheWithCacheQueryStringFalse() + public function testPageCacheWithCacheQueryString($cacheQueryStringValue, $expectedPagesInCache, $testingUrls) { // Suppress command() output CITestStreamFilter::$buffer = ''; $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); - // Create cache config with disabled cacheQueryString + // Create cache config with cacheQueryString value from the dataProvider $cacheConfig = new Cache(); - $cacheConfig->cacheQueryString = false; + $cacheConfig->cacheQueryString = $cacheQueryStringValue; // Clear cache before starting the test command('cache:clear'); @@ -645,21 +650,12 @@ public function testPageCacheWithCacheQueryStringFalse() $cache = \Config\Services::cache(); $cacheStartCounter = count($cache->getCacheInfo()); - // URLs for testing cache configuration. Only one cache file should be created for all 5 URL`s, since we have disabled cacheQueryString - $testingUrls = [ - 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page - 'test?important_parameter=1', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - 'test?important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached, because we expect that GET parameters are ignored and URL without GET parameters was cached at first itteration - ]; - // Generate request to each URL from the testing array foreach ($testingUrls as $testingUrl) { $_SERVER['REQUEST_URI'] = '/' . $testingUrl; $routes = Services::routes(true); $routes->add($testingUrl, static function () { - CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test + CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings from the dataProvider $response = Services::response(); $string = 'This is a test page, to check cache configuration'; @@ -670,7 +666,7 @@ public function testPageCacheWithCacheQueryStringFalse() $router = Services::router($routes, Services::request(null, false)); Services::injectMock('router', $router); - // Cache the page output using default caching function and our own $cacheConfig + // Cache the page output using default caching function and $cacheConfig with value from the data provider $this->codeigniter->useSafeOutput(true)->run(); $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration } @@ -682,143 +678,28 @@ public function testPageCacheWithCacheQueryStringFalse() // Clear cache after the test command('cache:clear'); - // We expect only one new page in the cache, because GET parameters should be ignored with cacheQueryString = false - $this->assertSame(1, $newPagesCached); + // Check that amount of new items created in the cache matching expected value from the data provider + $this->assertSame($expectedPagesInCache, $newPagesCached); // Remove stream filters stream_filter_remove($outputStreamFilter); stream_filter_remove($errorStreamFilter); } - /** - * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 - */ - public function testPageCacheWithCacheQueryStringTrue() + public function cacheQueryStringProvider(): array { - // Suppress command() output - CITestStreamFilter::$buffer = ''; - $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); - $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); - - // Create cache config with enabled cacheQueryString - $cacheConfig = new Cache(); - $cacheConfig->cacheQueryString = true; - - // Clear cache before starting the test - command('cache:clear'); - - // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cacheStartCounter = count($cache->getCacheInfo()); - - // URLs for testing cache configuration. New 5 files should be created for all 5 URL`s, since we have enabled cacheQueryString, and all GET parameters combinations is unique $testingUrls = [ - 'test', // Should be cached because its a unique URL and not exist in the cache - 'test?important_parameter=1', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache - 'test?important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache - 'test?important_parameter=1¬_important_parameter=2', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache - 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should be cached because its a unique URL (with GET parameters) and its not exist in the cache + 'test', // URL #1 + 'test?important_parameter=1', // URL #2 + 'test?important_parameter=2', // URL #3 + 'test?important_parameter=1¬_important_parameter=2', // URL #4 + 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // URL #5 ]; - // Generate request to each URL from the testing array - foreach ($testingUrls as $testingUrl) { - $_SERVER['REQUEST_URI'] = '/' . $testingUrl; - $routes = Services::routes(true); - $routes->add($testingUrl, static function () { - CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test - $response = Services::response(); - $string = 'This is a test page, to check cache configuration'; - - return $response->setBody($string); - }); - - // Inject router - $router = Services::router($routes, Services::request(null, false)); - Services::injectMock('router', $router); - - // Cache the page output using default caching function and our own $cacheConfig - $this->codeigniter->useSafeOutput(true)->run(); - $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration - } - - // Calculate how much cached items exist in the cache after the test requests - $cacheEndCounter = count($cache->getCacheInfo()); - $newPagesCached = $cacheEndCounter - $cacheStartCounter; - - // Clear cache after the test - command('cache:clear'); - - // We expect 5 new pages in the cache, because GET parameters should be counted as unique pages with cacheQueryString = true - $this->assertSame(5, $newPagesCached); - - // Remove stream filters - stream_filter_remove($outputStreamFilter); - stream_filter_remove($errorStreamFilter); - } - - /** - * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 - */ - public function testPageCacheWithCacheQueryStringArray() - { - // Suppress command() output - CITestStreamFilter::$buffer = ''; - $outputStreamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter'); - $errorStreamFilter = stream_filter_append(STDERR, 'CITestStreamFilter'); - - // Create cache config with enabled cacheQueryString - $cacheConfig = new Cache(); - $cacheConfig->cacheQueryString = ['important_parameter']; - - // Clear cache before starting the test - command('cache:clear'); - - // Calculate amount of items in the cache before the test - $cache = \Config\Services::cache(); - $cacheStartCounter = count($cache->getCacheInfo()); - - // URLs for testing cache configuration. Only 3 files should be created for this 5 URL`s, since cacheQueryString is an array of important parameters only, and all other GET parameters (not in the array) should be ignored - $testingUrls = [ - 'test', // Should be cached because its first request after clearing cache and there is no cached version of the page - 'test?important_parameter=1', // Should be cached because important parameter exist and have unique value 1 - 'test?important_parameter=2', // Should be cached because important parameter exist and have unique value 2 - 'test?important_parameter=1¬_important_parameter=2', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored - 'test?important_parameter=1¬_important_parameter=2&another_not_important_parameter=3', // Should NOT be cached because important parameter have not unique value 1 (was already cashed at previous iteration) and other not important parameters should be ignored + return [ + '$cacheQueryString=false' => [false, 1, $testingUrls], // We expect only 1 page in the cache, because when cacheQueryString is set to false, all GET parameter should be ignored, and page URI will be absolutely same "/test" string for all 5 requests + '$cacheQueryString=true' => [true, 5, $testingUrls], // We expect all 5 pages in the cache, because when cacheQueryString is set to true, all GET parameter should be processed as unique requests + '$cacheQueryString=array' => [['important_parameter'], 3, $testingUrls], // We expect only 3 pages in the cache, because when cacheQueryString is set to array with important parameters, we should ignore all parameters thats not in the array. Only URL #1, URL #2 and URL #3 should be cached. URL #4 and URL #5 is duplication of URL #2 (with value ?important_parameter=1), so they should not be processed as new unique requests and application should return already cached page for URL #2 ]; - - // Generate request to each URL from the testing array - foreach ($testingUrls as $testingUrl) { - $_SERVER['REQUEST_URI'] = '/' . $testingUrl; - $routes = Services::routes(true); - $routes->add($testingUrl, static function () { - CodeIgniter::cache(0); // Dont cache the page in the run() function because CodeIgniter class will create default $cacheConfig and overwrite settings in the test - $response = Services::response(); - $string = 'This is a test page, to check cache configuration'; - - return $response->setBody($string); - }); - - // Inject router - $router = Services::router($routes, Services::request(null, false)); - Services::injectMock('router', $router); - - // Cache the page output using default caching function and our own $cacheConfig - $this->codeigniter->useSafeOutput(true)->run(); - $this->codeigniter->cachePage($cacheConfig); // Cache the page using our own $cacheConfig confugration - } - - // Calculate how much cached items exist in the cache after the test requests - $cacheEndCounter = count($cache->getCacheInfo()); - $newPagesCached = $cacheEndCounter - $cacheStartCounter; - - // Clear cache after the test - command('cache:clear'); - - // We expect 3 new pages in the cache, because only important GET parameters from array should be counted as unique pages with cacheQueryString = ['important_parameter'] - $this->assertSame(3, $newPagesCached); - - // Remove stream filters - stream_filter_remove($outputStreamFilter); - stream_filter_remove($errorStreamFilter); } } From 48d2dc66353426faa3c3a946ed501452bbb1113e Mon Sep 17 00:00:00 2001 From: Kulakov Ivan Date: Wed, 24 Aug 2022 14:51:28 +0300 Subject: [PATCH 77/77] Use parameter type declaration instead of doc comment | Change mixed type to strict type for the --- tests/system/CodeIgniterTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index d14173a4ad35..11b7542c3dff 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -625,14 +625,12 @@ public function testPageCacheSendSecureHeaders() } /** - * @param mixed $cacheQueryStringValue - * @param int $expectedPagesInCache - * @param array $testingUrls + * @param array|bool $cacheQueryStringValue * @dataProvider cacheQueryStringProvider * * @see https://github.com/codeigniter4/CodeIgniter4/pull/6410 */ - public function testPageCacheWithCacheQueryString($cacheQueryStringValue, $expectedPagesInCache, $testingUrls) + public function testPageCacheWithCacheQueryString($cacheQueryStringValue, int $expectedPagesInCache, array $testingUrls) { // Suppress command() output CITestStreamFilter::$buffer = '';