From 5b528af01f1a2b478454ff10c528c1aaecd54252 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 6 Mar 2022 14:38:42 +0900 Subject: [PATCH 0001/8282] fix: now initEnvValue() converts type when the default property value is int and float --- system/Config/BaseConfig.php | 22 ++++++++++++++----- tests/system/Config/BaseConfigTest.php | 19 ++++++++++++++++ tests/system/Config/fixtures/.env | 5 +++++ tests/system/Config/fixtures/SimpleConfig.php | 3 +++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index 9b71a4963bab..359fcc1488ba 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -88,7 +88,7 @@ public function __construct() * * @param mixed $property * - * @return mixed + * @return void */ protected function initEnvValue(&$property, string $name, string $prefix, string $shortPrefix) { @@ -102,16 +102,28 @@ protected function initEnvValue(&$property, string $name, string $prefix, string } elseif ($value === 'true') { $value = true; } - $property = is_bool($value) ? $value : trim($value, '\'"'); - } + if (is_bool($value)) { + $property = $value; + + return; + } + + $value = trim($value, '\'"'); - return $property; + if (is_int($property)) { + $value = (int) $value; + } elseif (is_float($property)) { + $value = (float) $value; + } + + $property = $value; + } } /** * Retrieve an environment-specific configuration setting * - * @return mixed + * @return string|null */ protected function getEnvValue(string $property, string $prefix, string $shortPrefix) { diff --git a/tests/system/Config/BaseConfigTest.php b/tests/system/Config/BaseConfigTest.php index 4c2d4ba20ccb..667a877287fb 100644 --- a/tests/system/Config/BaseConfigTest.php +++ b/tests/system/Config/BaseConfigTest.php @@ -55,6 +55,25 @@ public function testBasicValues() $this->assertSame(18, $config->golf); } + public function testUseDefaultValueTypeIntAndFloatValues() + { + $dotenv = new DotEnv($this->fixturesFolder, '.env'); + $dotenv->load(); + $config = new SimpleConfig(); + + $this->assertSame(0.0, $config->float); + $this->assertSame(999, $config->int); + } + + public function testUseDefaultValueTypeStringValue() + { + $dotenv = new DotEnv($this->fixturesFolder, '.env'); + $dotenv->load(); + $config = new SimpleConfig(); + + $this->assertSame('123456', $config->password); + } + /** * @runInSeparateProcess * @preserveGlobalState disabled diff --git a/tests/system/Config/fixtures/.env b/tests/system/Config/fixtures/.env index 2f859cc9faca..38ca0bed6f0b 100644 --- a/tests/system/Config/fixtures/.env +++ b/tests/system/Config/fixtures/.env @@ -32,3 +32,8 @@ SimpleConfig.crew.captain = Malcolm SimpleConfig.crew.pilot = Wash SimpleConfig.crew.comms = true SimpleConfig.crew.doctor = false + +SimpleConfig.float = '0.0' +SimpleConfig.int = '999' + +SimpleConfig.password = 123456 diff --git a/tests/system/Config/fixtures/SimpleConfig.php b/tests/system/Config/fixtures/SimpleConfig.php index 69f5590e90f1..d64aa1a0d741 100644 --- a/tests/system/Config/fixtures/SimpleConfig.php +++ b/tests/system/Config/fixtures/SimpleConfig.php @@ -48,4 +48,7 @@ class SimpleConfig extends \CodeIgniter\Config\BaseConfig public $one_deep = [ 'under_deep' => null, ]; + public $float = 12.34; + public $int = 1234; + public $password = 'secret'; } From 8524bb963bd79bd3be676fa21ab82b8c98b6a412 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 18 Mar 2022 16:08:22 +0900 Subject: [PATCH 0002/8282] test: add test for nullable int property --- tests/system/Config/BaseConfigTest.php | 3 +++ tests/system/Config/fixtures/.env | 2 ++ tests/system/Config/fixtures/SimpleConfig.php | 7 ++++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/system/Config/BaseConfigTest.php b/tests/system/Config/BaseConfigTest.php index 667a877287fb..20d2f77c8421 100644 --- a/tests/system/Config/BaseConfigTest.php +++ b/tests/system/Config/BaseConfigTest.php @@ -117,6 +117,9 @@ public function testEnvironmentOverrides() $this->assertSame('bar', $config->onedeep_value); // array property name with underscore and key with underscore $this->assertSame('foo', $config->one_deep['under_deep']); + + // The default property value is null but has type + $this->assertSame(20, $config->size); } public function testPrefixedValues() diff --git a/tests/system/Config/fixtures/.env b/tests/system/Config/fixtures/.env index 38ca0bed6f0b..36f5651cf532 100644 --- a/tests/system/Config/fixtures/.env +++ b/tests/system/Config/fixtures/.env @@ -37,3 +37,5 @@ SimpleConfig.float = '0.0' SimpleConfig.int = '999' SimpleConfig.password = 123456 + +SimpleConfig.size=20 diff --git a/tests/system/Config/fixtures/SimpleConfig.php b/tests/system/Config/fixtures/SimpleConfig.php index d64aa1a0d741..9870c72b0ac2 100644 --- a/tests/system/Config/fixtures/SimpleConfig.php +++ b/tests/system/Config/fixtures/SimpleConfig.php @@ -48,7 +48,8 @@ class SimpleConfig extends \CodeIgniter\Config\BaseConfig public $one_deep = [ 'under_deep' => null, ]; - public $float = 12.34; - public $int = 1234; - public $password = 'secret'; + public $float = 12.34; + public $int = 1234; + public $password = 'secret'; + public ?int $size = null; } From d61b3c7487f2168f38b6f01ea2398b409d7c2523 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 19 Mar 2022 10:11:18 +0900 Subject: [PATCH 0003/8282] test: change .env entry Do not recommend to use `'` for int/float value. --- tests/system/Config/fixtures/.env | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/system/Config/fixtures/.env b/tests/system/Config/fixtures/.env index 36f5651cf532..17d9e8e9116e 100644 --- a/tests/system/Config/fixtures/.env +++ b/tests/system/Config/fixtures/.env @@ -33,9 +33,12 @@ SimpleConfig.crew.pilot = Wash SimpleConfig.crew.comms = true SimpleConfig.crew.doctor = false -SimpleConfig.float = '0.0' -SimpleConfig.int = '999' +# The default value's type in the Config class is float, so it will be converted to float +SimpleConfig.float = 0.0 +# The default value's type in the Config class is int, so it will be converted to int +SimpleConfig.int = 999 SimpleConfig.password = 123456 +# The property type in the Config class is ?int, so it will be converted to int by PHP SimpleConfig.size=20 From e4fcba2749b3e7e5dd3af55b5cdd057820343fe0 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 20 Mar 2022 16:14:40 +0900 Subject: [PATCH 0004/8282] fix: failover's DBPrefix not working Fixes #5812 --- system/Database/BaseConnection.php | 7 +++++++ tests/system/Database/Live/ConnectTest.php | 12 ++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 4f54c1980830..9c803d0c1372 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -342,6 +342,13 @@ public function __construct(array $params) if (class_exists($queryClass)) { $this->queryClass = $queryClass; } + + if ($this->failover !== []) { + // If there is a failover database, connect now to do failover. + // Otherwise, Query Builder creates SQL statement with the main database config + // (DBPrefix) even when the main database is down. + $this->initialize(); + } } /** diff --git a/tests/system/Database/Live/ConnectTest.php b/tests/system/Database/Live/ConnectTest.php index 743b5450cca3..1757f91cb676 100644 --- a/tests/system/Database/Live/ConnectTest.php +++ b/tests/system/Database/Live/ConnectTest.php @@ -95,14 +95,22 @@ public function testConnectWorksWithGroupName() public function testConnectWithFailover() { $this->tests['failover'][] = $this->tests; - unset($this->tests['failover'][0]['failover']); + // Change main's DBPrefix + $this->tests['DBPrefix'] = 'main_'; + + if ($this->tests['DBDriver'] === 'SQLite3') { + // Change main's database path to fail to connect + $this->tests['database'] = '/does/not/exists/test.db'; + } + $this->tests['username'] = 'wrong'; $db1 = Database::connect($this->tests); - $this->assertSame($this->tests['failover'][0]['DBDriver'], $this->getPrivateProperty($db1, 'DBDriver')); + $this->assertSame($this->tests['failover'][0]['DBPrefix'], $this->getPrivateProperty($db1, 'DBPrefix')); + $this->assertGreaterThanOrEqual(0, count($db1->listTables())); } } From 5a456125d8a8503945568564b91a2ad256d2d184 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 20 Mar 2022 16:40:08 +0900 Subject: [PATCH 0005/8282] test: fix failed test --- tests/system/Database/BaseConnectionTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/system/Database/BaseConnectionTest.php b/tests/system/Database/BaseConnectionTest.php index 9bb8ddee1499..781588dd78e8 100644 --- a/tests/system/Database/BaseConnectionTest.php +++ b/tests/system/Database/BaseConnectionTest.php @@ -105,8 +105,11 @@ public function testCanConnectToFailoverWhenNoConnectionAvailable() $options = $this->options; $options['failover'] = [$this->failoverOptions]; - $db = new MockConnection($options); - $db->shouldReturn('connect', [false, 345])->initialize(); + $db = new class ($options) extends MockConnection { + protected $returnValues = [ + 'connect' => [false, 345], + ]; + }; $this->assertSame(345, $db->getConnection()); $this->assertSame('failover', $db->username); From 57e0594a8d2a12b32de852ecd52a1ab679fa4705 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 23 Mar 2022 09:13:11 +0900 Subject: [PATCH 0006/8282] chore: remove App\ and Config\ in autoload.psr-4 in app starter composer.json Revert #3423 Problems: - Cannot change `app` folder name. Because the composer's path overwrite the config in Config\Autoload. - Causes error when defining new namespace under app/. See #5818 --- admin/starter/composer.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/admin/starter/composer.json b/admin/starter/composer.json index f2959dd49b51..026329ca9a24 100644 --- a/admin/starter/composer.json +++ b/admin/starter/composer.json @@ -17,10 +17,6 @@ "ext-fileinfo": "Improves mime type detection for files" }, "autoload": { - "psr-4": { - "App\\": "app", - "Config\\": "app/Config" - }, "exclude-from-classmap": [ "**/Database/Migrations/**" ] From 135222d40724d3b6bc470fd88bf9c85b1fff9b95 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 23 Mar 2022 09:23:43 +0900 Subject: [PATCH 0007/8282] docs: add @TODO --- system/CLI/Commands.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/CLI/Commands.php b/system/CLI/Commands.php index 3f84f33d918f..d153b3aa857a 100644 --- a/system/CLI/Commands.php +++ b/system/CLI/Commands.php @@ -76,6 +76,10 @@ public function getCommands() /** * Discovers all commands in the framework and within user code, * and collects instances of them to work with. + * + * @TODO this approach (find qualified classname from path) causes error, + * when using Composer autoloader. + * See https://github.com/codeigniter4/CodeIgniter4/issues/5818 */ public function discoverCommands() { From c5eb48011648c5f9dfa0f2390f69297a13d7e651 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 27 Mar 2022 00:11:10 +0800 Subject: [PATCH 0008/8282] Rename `Abstact` to `Abstract` --- .../{AbstactHandlerTestCase.php => AbstractHandlerTestCase.php} | 2 +- tests/system/Session/Handlers/Database/MySQLiHandlerTest.php | 2 +- tests/system/Session/Handlers/Database/PostgreHandlerTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/system/Session/Handlers/Database/{AbstactHandlerTestCase.php => AbstractHandlerTestCase.php} (98%) diff --git a/tests/system/Session/Handlers/Database/AbstactHandlerTestCase.php b/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php similarity index 98% rename from tests/system/Session/Handlers/Database/AbstactHandlerTestCase.php rename to tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php index 7434301f40cc..f252fc24fb43 100644 --- a/tests/system/Session/Handlers/Database/AbstactHandlerTestCase.php +++ b/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php @@ -19,7 +19,7 @@ /** * @internal */ -abstract class AbstactHandlerTestCase extends CIUnitTestCase +abstract class AbstractHandlerTestCase extends CIUnitTestCase { use DatabaseTestTrait; use ReflectionHelper; diff --git a/tests/system/Session/Handlers/Database/MySQLiHandlerTest.php b/tests/system/Session/Handlers/Database/MySQLiHandlerTest.php index 469b0c8e1f7e..5fba8bb779d0 100644 --- a/tests/system/Session/Handlers/Database/MySQLiHandlerTest.php +++ b/tests/system/Session/Handlers/Database/MySQLiHandlerTest.php @@ -18,7 +18,7 @@ /** * @internal */ -final class MySQLiHandlerTest extends AbstactHandlerTestCase +final class MySQLiHandlerTest extends AbstractHandlerTestCase { protected function setUp(): void { diff --git a/tests/system/Session/Handlers/Database/PostgreHandlerTest.php b/tests/system/Session/Handlers/Database/PostgreHandlerTest.php index 9575b68e499f..d28d1fab7134 100644 --- a/tests/system/Session/Handlers/Database/PostgreHandlerTest.php +++ b/tests/system/Session/Handlers/Database/PostgreHandlerTest.php @@ -18,7 +18,7 @@ /** * @internal */ -final class PostgreHandlerTest extends AbstactHandlerTestCase +final class PostgreHandlerTest extends AbstractHandlerTestCase { protected function setUp(): void { From 7612cdb09129ab2ce3e736fa3c131f834b52caa9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 28 Mar 2022 12:46:08 +0900 Subject: [PATCH 0009/8282] docs: add index.php/spark changes in changelog --- user_guide_src/source/changelogs/v4.2.0.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index 9751dd06fee0..f12a13bcb416 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -13,6 +13,9 @@ BREAKING ******** - The method signature of ``Validation::setRule()`` has been changed. The ``string`` typehint on the ``$rules`` parameter was removed. Extending classes should likewise remove the parameter so as not to break LSP. +- The ``CodeIgniter\CodeIgniter`` class has a new property ``$context`` and it must have the correct context at runtime. So the following files have been changed: + - ``public/index.php`` + - ``spark`` Enhancements ************ From 2f8157dc34d328ba450788ab473fcc076f3a3772 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 28 Mar 2022 13:02:28 +0900 Subject: [PATCH 0010/8282] chore: fix ImportError ImportError: cannot import name 'environmentfilter' from 'jinja2' (/usr/local/lib/python3.10/site-packages/jinja2/__init__.py) See https://github.com/sphinx-doc/sphinx/issues/10291 --- user_guide_src/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/requirements.txt b/user_guide_src/requirements.txt index 19f5064b9815..5d11554964d1 100644 --- a/user_guide_src/requirements.txt +++ b/user_guide_src/requirements.txt @@ -2,3 +2,4 @@ sphinx>=2.4.4,<3 sphinxcontrib-phpdomain>=0.7.1 docutils>=0.16 sphinx-rtd-theme>=0.5.0 +jinja2<3.1 From 4138c04808f95699699a9140671a9fe605e5c2eb Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 29 Mar 2022 21:16:43 +0900 Subject: [PATCH 0011/8282] config: add mime type for webp --- app/Config/Mimes.php | 1 + user_guide_src/source/changelogs/v4.2.0.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php index 786bc6a1e5c7..a1bb458a2804 100644 --- a/app/Config/Mimes.php +++ b/app/Config/Mimes.php @@ -260,6 +260,7 @@ class Mimes 'image/png', 'image/x-png', ], + 'webp' => 'image/webp', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'css' => [ diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index f12a13bcb416..a0494ebd47c0 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -34,6 +34,7 @@ Enhancements - The ``spark routes`` command now shows closure routes, auto routes, and filters. See :ref:`URI Routing `. - Exception information logged through ``log_message()`` has now improved. It now includes the file and line where the exception originated. It also does not truncate the message anymore. - The log format has also changed. If users are depending on the log format in their apps, the new log format is "<1-based count> (): " +- Added support for webp files to **app/Config/Mimes.php**. Changes ******* From 250b0cadc1a453573a86d721ad9acbf31e8dfc0b Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 30 Mar 2022 11:27:59 +0900 Subject: [PATCH 0012/8282] docs: fix comment --- system/View/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/View/Parser.php b/system/View/Parser.php index 6834c003e500..dbb0395fbd0b 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -289,7 +289,7 @@ protected function parsePair(string $variable, array $data, string $template): a */ foreach ($matches as $match) { // Loop over each piece of $data, replacing - // it's contents so that we know what to replace in parse() + // its contents so that we know what to replace in parse() $str = ''; // holds the new contents for this tag pair. foreach ($data as $row) { From 9a85788f51b056c24c1d7c4477a4108e52bec9f1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 30 Mar 2022 11:28:59 +0900 Subject: [PATCH 0013/8282] fix: view parser fails with ({variable}) in loop --- system/View/Parser.php | 11 +---------- tests/system/View/ParserTest.php | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/system/View/Parser.php b/system/View/Parser.php index dbb0395fbd0b..6f62ca4fb333 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -338,8 +338,7 @@ protected function parsePair(string $variable, array $data, string $template): a $str .= $out; } - // Escape | character from filters as it's handled as OR in regex - $escapedMatch = preg_replace('/(?assertSame("Super Heroes\nTom Dick Henry ", $this->parser->renderString($template)); } + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/5825 + */ + public function testParseLoopVariableWithParentheses() + { + $data = [ + 'title' => 'Super Heroes', + 'powers' => [ + ['name' => 'Tom'], + ['name' => 'Dick'], + ['name' => 'Henry'], + ], + ]; + + $template = "{title}\n{powers}({name}) {/powers}"; + + $this->parser->setData($data); + $this->assertSame("Super Heroes\n(Tom) (Dick) (Henry) ", $this->parser->renderString($template)); + } + public function testParseLoopObjectProperties() { $obj1 = new stdClass(); From 78d42a58f57eb4cc6a8a6e9f74be876b93b4051c Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 28 Mar 2022 11:40:45 +0900 Subject: [PATCH 0014/8282] fix: spark can't use options in PHP 7.4 Cannot unpack array with string keys at SYSTEMPATH/CodeIgniter.php:893 --- system/CLI/CommandRunner.php | 4 ++-- system/CodeIgniter.php | 10 +++++---- tests/system/CLI/CommandRunnerTest.php | 2 +- tests/system/SparkTest.php | 29 ++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 tests/system/SparkTest.php diff --git a/system/CLI/CommandRunner.php b/system/CLI/CommandRunner.php index a6985d0db931..06d06863de2f 100644 --- a/system/CLI/CommandRunner.php +++ b/system/CLI/CommandRunner.php @@ -40,13 +40,13 @@ public function __construct() * so we have the chance to look for a Command first. * * @param string $method - * @param array ...$params + * @param array $params * * @throws ReflectionException * * @return mixed */ - public function _remap($method, ...$params) + public function _remap($method, $params) { // The first param is usually empty, so scrap it. if (empty($params[0])) { diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 86e70546dd41..093687254c77 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -884,14 +884,16 @@ protected function runController($class) /** @var CLIRequest $request */ $request = $this->request; $params = $request->getArgs(); + + $output = $class->_remap($this->method, $params); } else { // This is a Web request or PHP CLI request $params = $this->router->params(); - } - $output = method_exists($class, '_remap') - ? $class->_remap($this->method, ...$params) - : $class->{$this->method}(...$params); + $output = method_exists($class, '_remap') + ? $class->_remap($this->method, ...$params) + : $class->{$this->method}(...$params); + } $this->benchmark->stop('controller'); diff --git a/tests/system/CLI/CommandRunnerTest.php b/tests/system/CLI/CommandRunnerTest.php index 32ca71357e75..c9e3245b932c 100644 --- a/tests/system/CLI/CommandRunnerTest.php +++ b/tests/system/CLI/CommandRunnerTest.php @@ -123,7 +123,7 @@ public function testBadCommand() */ public function testRemapEmptyFirstParams() { - self::$runner->_remap('anyvalue', null, 'list'); + self::$runner->_remap('anyvalue', [null, 'list']); $result = CITestStreamFilter::$buffer; // make sure the result looks like a command list diff --git a/tests/system/SparkTest.php b/tests/system/SparkTest.php new file mode 100644 index 000000000000..0cd76453c02a --- /dev/null +++ b/tests/system/SparkTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter; + +use CodeIgniter\Test\CIUnitTestCase; + +/** + * @internal + */ +final class SparkTest extends CIUnitTestCase +{ + public function testCanUseOption() + { + ob_start(); + passthru('php spark list --simple'); + $output = ob_get_clean(); + + $this->assertStringContainsString('cache:clear', $output); + } +} From 9cade91cf68f5121428c507135aba69a7dc182c4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 28 Mar 2022 11:44:41 +0900 Subject: [PATCH 0015/8282] refactor: remove unneeded logic It seems out of dated logic. --- system/CLI/CommandRunner.php | 5 ----- tests/system/CLI/CommandRunnerTest.php | 12 ------------ 2 files changed, 17 deletions(-) diff --git a/system/CLI/CommandRunner.php b/system/CLI/CommandRunner.php index 06d06863de2f..ef4ed057b606 100644 --- a/system/CLI/CommandRunner.php +++ b/system/CLI/CommandRunner.php @@ -48,11 +48,6 @@ public function __construct() */ public function _remap($method, $params) { - // The first param is usually empty, so scrap it. - if (empty($params[0])) { - array_shift($params); - } - return $this->index($params); } diff --git a/tests/system/CLI/CommandRunnerTest.php b/tests/system/CLI/CommandRunnerTest.php index c9e3245b932c..892b7b5ff516 100644 --- a/tests/system/CLI/CommandRunnerTest.php +++ b/tests/system/CLI/CommandRunnerTest.php @@ -117,16 +117,4 @@ public function testBadCommand() // make sure the result looks like a command list $this->assertStringContainsString('Command "bogus" not found', CITestStreamFilter::$buffer); } - - /** - * @TODO When the first param is empty? Use case? - */ - public function testRemapEmptyFirstParams() - { - self::$runner->_remap('anyvalue', [null, 'list']); - $result = CITestStreamFilter::$buffer; - - // make sure the result looks like a command list - $this->assertStringContainsString('Lists the available commands.', $result); - } } From d0b323d9fbf7a7227551b7190f80ba0dbbf265ef Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 30 Mar 2022 19:16:35 +0900 Subject: [PATCH 0016/8282] docs: add CommandRunner::_remap() change in changelogs --- user_guide_src/source/changelogs/v4.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index f12a13bcb416..34daeff2f474 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -16,6 +16,7 @@ BREAKING - The ``CodeIgniter\CodeIgniter`` class has a new property ``$context`` and it must have the correct context at runtime. So the following files have been changed: - ``public/index.php`` - ``spark`` +- The method signature of ``CodeIgniter\CLI\CommandRunner::_remap()`` has been changed to fix a bug. Enhancements ************ From 60ee4f3b946b7984ce7465d4770b4cd97bd3d2fe Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 1 Mar 2022 11:09:11 +0900 Subject: [PATCH 0017/8282] config: disable auto-routing by default --- app/Config/Routes.php | 2 +- system/Router/RouteCollection.php | 2 +- tests/system/CodeIgniterTest.php | 14 ++++++++++++++ tests/system/Commands/RoutesTest.php | 1 + .../Utilities/Routes/FilterCollectorTest.php | 2 ++ tests/system/Router/RouterTest.php | 4 ++++ user_guide_src/source/changelogs/v4.2.0.rst | 1 + 7 files changed, 24 insertions(+), 2 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 0060a6f67dff..2b35d0dda6f8 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -21,7 +21,7 @@ $routes->setDefaultMethod('index'); $routes->setTranslateURIDashes(false); $routes->set404Override(); -$routes->setAutoRoute(true); +$routes->setAutoRoute(false); /* * -------------------------------------------------------------------- diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 65bf4f4985af..a1dd7b1a8fe7 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -76,7 +76,7 @@ class RouteCollection implements RouteCollectionInterface * * @var bool */ - protected $autoRoute = true; + protected $autoRoute = false; /** * A callable that will be shown diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 42706e39d110..a29879522e37 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -47,6 +47,8 @@ protected function tearDown(): void if (count(ob_list_handlers()) > 1) { ob_end_clean(); } + + $this->resetServices(); } public function testRunEmptyDefaultRoute() @@ -469,6 +471,12 @@ public function testSpoofRequestMethodCanUsePUT() $_POST['_method'] = 'PUT'; + $routes = \Config\Services::routes(); + $routes->setDefaultNamespace('App\Controllers'); + $routes->resetRoutes(); + $routes->post('/', 'Home::index'); + $routes->put('/', 'Home::index'); + ob_start(); $this->codeigniter->useSafeOutput(true)->run(); ob_get_clean(); @@ -487,6 +495,12 @@ public function testSpoofRequestMethodCannotUseGET() $_POST['_method'] = 'GET'; + $routes = \Config\Services::routes(); + $routes->setDefaultNamespace('App\Controllers'); + $routes->resetRoutes(); + $routes->post('/', 'Home::index'); + $routes->get('/', 'Home::index'); + ob_start(); $this->codeigniter->useSafeOutput(true)->run(); ob_get_clean(); diff --git a/tests/system/Commands/RoutesTest.php b/tests/system/Commands/RoutesTest.php index dd5e6b3280a8..1787e8bef3fa 100644 --- a/tests/system/Commands/RoutesTest.php +++ b/tests/system/Commands/RoutesTest.php @@ -67,6 +67,7 @@ public function testRoutesCommandRouteFilterAndAutoRoute() $routes->setDefaultNamespace('App\Controllers'); $routes->resetRoutes(); $routes->get('/', 'Home::index', ['filter' => 'csrf']); + $routes->setAutoRoute(true); command('routes'); diff --git a/tests/system/Commands/Utilities/Routes/FilterCollectorTest.php b/tests/system/Commands/Utilities/Routes/FilterCollectorTest.php index 6002323d3393..5037c148b704 100644 --- a/tests/system/Commands/Utilities/Routes/FilterCollectorTest.php +++ b/tests/system/Commands/Utilities/Routes/FilterCollectorTest.php @@ -23,6 +23,8 @@ public function testGet() { $routes = Services::routes(); $routes->resetRoutes(); + $routes->setDefaultNamespace('App\Controllers'); + $routes->get('/', 'Home::index'); $collector = new FilterCollector(); diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index b2c476f5add6..d3ca04422c41 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -728,7 +728,10 @@ public function testAutoRouteMatchesZeroParams() public function testAutoRouteMethodEmpty() { $router = new Router($this->collection, $this->request); + $this->collection->setAutoRoute(true); + $router->handle('Home/'); + $this->assertSame('Home', $router->controllerName()); $this->assertSame('index', $router->methodName()); } @@ -775,6 +778,7 @@ public function testRegularExpressionPlaceholderWithUnicode() public function testRouterPriorDirectory() { $router = new Router($this->collection, $this->request); + $this->collection->setAutoRoute(true); $router->setDirectory('foo/bar/baz', false, true); $router->handle('Some_controller/some_method/param1/param2/param3'); diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index f12a13bcb416..55f7986375aa 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -43,6 +43,7 @@ Changes - The process of sending cookies has been moved to the ``Response`` class. Now the ``Session`` class doesn't send cookies, set them to the Response. - Validation. Changed generation of errors when using fields with a wildcard (*). Now the error key contains the full path. See :ref:`validation-getting-all-errors`. - ``Validation::getError()`` when using a wildcard will return all found errors matching the mask as a string. +- To make the default configuration more secure, auto-routing has been changed to disabled by default. Deprecations ************ From d9a2389bf4437bf811368568cd536ba216bc5fc1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 1 Mar 2022 11:33:42 +0900 Subject: [PATCH 0018/8282] docs: move "Default Controller" in the "Auto Routing" --- user_guide_src/source/incoming/routing.rst | 50 +++++++++++----------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index eb7d57370015..3a761ed1d5a0 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -465,31 +465,6 @@ then you can change this value to save typing: .. literalinclude:: routing/046.php -Default Controller -================== - -When a user visits the root of your site (i.e., example.com) the controller to use is determined by the value set by -the ``setDefaultController()`` method, unless a route exists for it explicitly. The default value for this is ``Home`` -which matches the controller at **app/Controllers/Home.php**: - -.. literalinclude:: routing/047.php - -The default controller is also used when no matching route has been found, and the URI would point to a directory -in the controllers directory. For example, if the user visits **example.com/admin**, if a controller was found at -**app/Controllers/Admin/Home.php**, it would be used. - -Default Method -============== - -This works similar to the default controller setting, but is used to determine the default method that is used -when a controller is found that matches the URI, but no segment exists for the method. The default value is -``index``. - -In this example, if the user were to visit **example.com/products**, and a ``Products`` controller existed, the -``Products::listAll()`` method would be executed: - -.. literalinclude:: routing/048.php - Translate URI Dashes ==================== @@ -566,8 +541,33 @@ and executes the corresponding controller method. The auto-routing is enabled by .. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. +Default Controller +================== + +When a user visits the root of your site (i.e., example.com) the controller to use is determined by the value set by +the ``setDefaultController()`` method, unless a route exists for it explicitly. The default value for this is ``Home`` +which matches the controller at **app/Controllers/Home.php**: + +.. literalinclude:: routing/047.php + +The default controller is also used when no matching route has been found, and the URI would point to a directory +in the controllers directory. For example, if the user visits **example.com/admin**, if a controller was found at +**app/Controllers/Admin/Home.php**, it would be used. + See :ref:`Auto Routing in Controllers ` for more info. +Default Method +============== + +This works similar to the default controller setting, but is used to determine the default method that is used +when a controller is found that matches the URI, but no segment exists for the method. The default value is +``index``. + +In this example, if the user were to visit **example.com/products**, and a ``Products`` controller existed, the +``Products::listAll()`` method would be executed: + +.. literalinclude:: routing/048.php + Confirming Routes ***************** From aa69f381eae378e867d940c38363efc548ec047f Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 1 Mar 2022 11:40:19 +0900 Subject: [PATCH 0019/8282] docs: move "Default Method" in "Auto Routing" --- user_guide_src/source/incoming/routing.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 3a761ed1d5a0..d4b4c2f34cf1 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -539,10 +539,13 @@ and executes the corresponding controller method. The auto-routing is enabled by .. note:: To prevent misconfiguration and miscoding, we recommend that you disable the auto-routing feature. See :ref:`use-defined-routes-only`. +Configuration Options +===================== + .. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. Default Controller -================== +------------------ When a user visits the root of your site (i.e., example.com) the controller to use is determined by the value set by the ``setDefaultController()`` method, unless a route exists for it explicitly. The default value for this is ``Home`` @@ -557,7 +560,7 @@ in the controllers directory. For example, if the user visits **example.com/admi See :ref:`Auto Routing in Controllers ` for more info. Default Method -============== +-------------- This works similar to the default controller setting, but is used to determine the default method that is used when a controller is found that matches the URI, but no segment exists for the method. The default value is From fabcf1c4ab76efdc41faf24553d094bde2cee385 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 1 Mar 2022 11:53:07 +0900 Subject: [PATCH 0020/8282] docs: add "Enable Auto Routing" --- user_guide_src/source/incoming/routing.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index d4b4c2f34cf1..04beafd9f2dc 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -515,6 +515,15 @@ It is recommended that all routes are defined in the **app/Config/Routes.php** f However, CodeIgniter can also automatically route HTTP requests based on conventions and execute the corresponding controller methods. +Enable Auto Routing +=================== + +Since v4.2.0, the auto-routing is disabled by default. + +To use it, you need to change the setting ``setAutoRoute()`` option to true in **app/Config/Routes.php**:: + + $routes->setAutoRoute(true); + URI Segments ============ From a514043270fa56efe301f37e5a4955d140a72527 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 1 Mar 2022 11:53:54 +0900 Subject: [PATCH 0021/8282] docs: update existing description for disabling auto-routing by default --- user_guide_src/source/incoming/controllers.rst | 6 +++--- user_guide_src/source/incoming/routing.rst | 14 +++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index 037710255100..287d6ae28153 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -114,10 +114,10 @@ Auto Routing This section describes the functionality of the auto-routing. It automatically routes an HTTP request, and executes the corresponding controller method -without route definitions. The auto-routing is enabled by default. +without route definitions. The auto-routing is disabled by default. .. note:: To prevent misconfiguration and miscoding, we recommend that you disable - the auto-routing feature. See :ref:`use-defined-routes-only`. + the auto-routing feature. .. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. @@ -220,7 +220,7 @@ Your method will be passed URI segments 3 and 4 (``'sandals'`` and ``'123'``): .. literalinclude:: controllers/014.php .. important:: If you are using the :doc:`URI Routing ` - feature, the segments passed to your method will be the re-routed + feature, the segments passed to your method will be the defined ones. Defining a Default Controller diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 04beafd9f2dc..77d68533fba2 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -312,7 +312,7 @@ available from the command line: .. literalinclude:: routing/032.php -.. warning:: If you don't disable auto-routing and place the command file in **app/Controllers**, +.. warning:: If you enable auto-routing and place the command file in **app/Controllers**, anyone could access the command with the help of auto-routing via HTTP. Global Options @@ -515,6 +515,11 @@ It is recommended that all routes are defined in the **app/Config/Routes.php** f However, CodeIgniter can also automatically route HTTP requests based on conventions and execute the corresponding controller methods. +.. warning:: To prevent misconfiguration and miscoding, we recommend that you disable + the auto-routing feature. + +.. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. + Enable Auto Routing =================== @@ -543,15 +548,14 @@ In the above example, CodeIgniter would attempt to find a controller named **Hel and executes ``index()`` method with passing ``'1'`` as the first argument. We call this "**Auto Routes**". CodeIgniter automatically routes an HTTP request, -and executes the corresponding controller method. The auto-routing is enabled by default. +and executes the corresponding controller method. The auto-routing is disabled by default. -.. note:: To prevent misconfiguration and miscoding, we recommend that you disable - the auto-routing feature. See :ref:`use-defined-routes-only`. +See :ref:`Auto Routing in Controllers ` for more info. Configuration Options ===================== -.. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. +These options are available at the top of **app/Config/Routes.php**. Default Controller ------------------ From 1e6a48625fd77546390107b71823a095f9b13428 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 1 Mar 2022 12:51:06 +0900 Subject: [PATCH 0022/8282] docs: replace remap with map --- user_guide_src/source/incoming/controllers.rst | 2 +- user_guide_src/source/incoming/routing.rst | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index 287d6ae28153..a4c635a6532c 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -279,7 +279,7 @@ called if the URL contains *only* the sub-directory. Simply put a controller in there that matches the name of your default controller as specified in your **app/Config/Routes.php** file. -CodeIgniter also permits you to remap your URIs using its :doc:`URI Routing ` feature. +CodeIgniter also permits you to map your URIs using its :doc:`URI Routing ` feature. Remapping Method Calls ********************** diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 77d68533fba2..41741fb93ebf 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -76,22 +76,22 @@ Examples Here are a few basic routing examples. -A URL containing the word **journals** in the first segment will be remapped to the ``\App\Controllers\Blogs`` class, +A URL containing the word **journals** in the first segment will be mapped to the ``\App\Controllers\Blogs`` class, and the default method, which is usually ``index()``: .. literalinclude:: routing/006.php -A URL containing the segments **blog/joe** will be remapped to the ``\App\Controllers\Blogs`` class and the ``users`` method. +A URL containing the segments **blog/joe** will be mapped to the ``\App\Controllers\Blogs`` class and the ``users`` method. The ID will be set to ``34``: .. literalinclude:: routing/007.php -A URL with **product** as the first segment, and anything in the second will be remapped to the ``\App\Controllers\Catalog`` class +A URL with **product** as the first segment, and anything in the second will be mapped to the ``\App\Controllers\Catalog`` class and the ``productLookup`` method: .. literalinclude:: routing/008.php -A URL with **product** as the first segment, and a number in the second will be remapped to the ``\App\Controllers\Catalog`` class +A URL with **product** as the first segment, and a number in the second will be mapped to the ``\App\Controllers\Catalog`` class and the ``productLookupByID`` method passing in the match as a variable to the method: .. literalinclude:: routing/009.php From 08ecf84170635df1235ae757c6d9ff4b4ee1fe92 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 1 Mar 2022 12:55:26 +0900 Subject: [PATCH 0023/8282] docs: add `()` at the end of method names --- user_guide_src/source/incoming/routing.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 41741fb93ebf..4103abb6e428 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -81,18 +81,18 @@ and the default method, which is usually ``index()``: .. literalinclude:: routing/006.php -A URL containing the segments **blog/joe** will be mapped to the ``\App\Controllers\Blogs`` class and the ``users`` method. +A URL containing the segments **blog/joe** will be mapped to the ``\App\Controllers\Blogs`` class and the ``users()`` method. The ID will be set to ``34``: .. literalinclude:: routing/007.php A URL with **product** as the first segment, and anything in the second will be mapped to the ``\App\Controllers\Catalog`` class -and the ``productLookup`` method: +and the ``productLookup()`` method: .. literalinclude:: routing/008.php A URL with **product** as the first segment, and a number in the second will be mapped to the ``\App\Controllers\Catalog`` class -and the ``productLookupByID`` method passing in the match as a variable to the method: +and the ``productLookupByID()`` method passing in the match as a variable to the method: .. literalinclude:: routing/009.php From ab1d3d862c2774b51e432279412124daa5344e04 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 2 Mar 2022 09:32:49 +0900 Subject: [PATCH 0024/8282] docs: update Tutorial --- .../source/tutorial/create_news_items.rst | 2 +- .../source/tutorial/create_news_items/004.php | 5 + .../source/tutorial/news_section.rst | 3 +- .../source/tutorial/news_section/008.php | 5 + .../source/tutorial/static_pages.rst | 120 +++++++----------- .../source/tutorial/static_pages/003.php | 6 + .../source/tutorial/static_pages/004.php | 1 + 7 files changed, 68 insertions(+), 74 deletions(-) diff --git a/user_guide_src/source/tutorial/create_news_items.rst b/user_guide_src/source/tutorial/create_news_items.rst index 32bc4b703e18..49ebc36c2bc3 100644 --- a/user_guide_src/source/tutorial/create_news_items.rst +++ b/user_guide_src/source/tutorial/create_news_items.rst @@ -119,7 +119,7 @@ Routing Before you can start adding news items into your CodeIgniter application you have to add an extra rule to **app/Config/Routes.php** file. Make sure your -file contains the following. This makes sure CodeIgniter sees ``create`` +file contains the following. This makes sure CodeIgniter sees ``create()`` as a method instead of a news item's slug. You can read more about different routing types :doc:`here `. diff --git a/user_guide_src/source/tutorial/create_news_items/004.php b/user_guide_src/source/tutorial/create_news_items/004.php index c8c66824fd82..8a0905770c82 100644 --- a/user_guide_src/source/tutorial/create_news_items/004.php +++ b/user_guide_src/source/tutorial/create_news_items/004.php @@ -1,6 +1,11 @@ match(['get', 'post'], 'news/create', 'News::create'); $routes->get('news/(:segment)', 'News::view/$1'); $routes->get('news', 'News::index'); +$routes->get('pages', 'Pages::index'); $routes->get('(:any)', 'Pages::view/$1'); + +// ... diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst index 2797c8a5cfaa..a27902819265 100644 --- a/user_guide_src/source/tutorial/news_section.rst +++ b/user_guide_src/source/tutorial/news_section.rst @@ -171,8 +171,7 @@ The only thing left to do is create the corresponding view at Routing ******* -Because of the wildcard routing rule created earlier, you need an extra -route to view the controller that you just made. Modify your routing file +Modify your routing file (**app/Config/Routes.php**) so it looks as follows. This makes sure the requests reach the ``News`` controller instead of going directly to the ``Pages`` controller. The first line routes URI's diff --git a/user_guide_src/source/tutorial/news_section/008.php b/user_guide_src/source/tutorial/news_section/008.php index 8943925258bd..4ba41638ef7f 100644 --- a/user_guide_src/source/tutorial/news_section/008.php +++ b/user_guide_src/source/tutorial/news_section/008.php @@ -1,5 +1,10 @@ get('news/(:segment)', 'News::view/$1'); $routes->get('news', 'News::index'); +$routes->get('pages', 'Pages::index'); $routes->get('(:any)', 'Pages::view/$1'); + +// ... diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst index 5128d6665e2c..6c39f82b5f80 100644 --- a/user_guide_src/source/tutorial/static_pages.rst +++ b/user_guide_src/source/tutorial/static_pages.rst @@ -9,20 +9,6 @@ The first thing you're going to do is set up a **controller** to handle static pages. A controller is simply a class that helps delegate work. It is the glue of your web application. -For example, when a call is made to:: - - http://example.com/news/latest/10 - -We might imagine that there is a controller named "news". The method -being called on news would be "latest". The news method's job could be to -grab 10 news items, and render them on the page. Very often in MVC, -you'll see URL patterns that match:: - - http://example.com/[controller-class]/[controller-method]/[arguments] - -As URL schemes become more complex, this may change. But for now, this -is all we will need to know. - Let's Make our First Controller ******************************* @@ -126,59 +112,11 @@ view. throw errors on case-sensitive platforms. You can read more about it :doc:`here `. -Running the App -*************** - -Ready to test? You cannot run the app using PHP's built-in server, -since it will not properly process the ``.htaccess`` rules that are provided in -``public``, and which eliminate the need to specify "index.php/" -as part of a URL. CodeIgniter has its own command that you can use though. - -From the command line, at the root of your project:: - - > php spark serve - -will start a web server, accessible on port 8080. If you set the location field -in your browser to ``localhost:8080``, you should see the CodeIgniter welcome page. - -You can now try several URLs in the browser location field, to see what the ``Pages`` -controller you made above produces... - -.. table:: - :widths: 20 80 - - +---------------------------------+-----------------------------------------------------------------+ - | URL | Will show | - +=================================+=================================================================+ - | localhost:8080/pages | the results from the `index` method inside our `Pages` | - | | controller, which is to display the CodeIgniter "welcome" page, | - | | because "index" is the default controller method | - +---------------------------------+-----------------------------------------------------------------+ - | localhost:8080/pages/index | the CodeIgniter "welcome" page, because we explicitly asked for | - | | the "index" method | - +---------------------------------+-----------------------------------------------------------------+ - | localhost:8080/pages/view | the "home" page that you made above, because it is the default | - | | "page" parameter to the ``view()`` method. | - +---------------------------------+-----------------------------------------------------------------+ - | localhost:8080/pages/view/home | show the "home" page that you made above, because we explicitly | - | | asked for it | - +---------------------------------+-----------------------------------------------------------------+ - | localhost:8080/pages/view/about | the "about" page that you made above, because we explicitly | - | | asked for it | - +---------------------------------+-----------------------------------------------------------------+ - | localhost:8080/pages/view/shop | a "404 - File Not Found" error page, because there is no | - | | `app/Views/pages/shop.php` | - +---------------------------------+-----------------------------------------------------------------+ - Routing ******* -The controller is now functioning! - -Using custom routing rules, you have the power to map any URI to any -controller and method, and break free from the normal convention:: - - http://example.com/[controller-class]/[controller-method]/[arguments] +We have made the controller. The next thing is to set routing rules. +Routing associates a URI with a controller's method. Let's do that. Open the routing file located at **app/Config/Routes.php** and look for the "Route Definitions" @@ -191,9 +129,10 @@ The only uncommented line there to start with should be: This directive says that any incoming request without any content specified should be handled by the ``index()`` method inside the ``Home`` controller. -Add the following line, **after** the route directive for '/'. +Add the following lines, **after** the route directive for '/'. .. literalinclude:: static_pages/004.php + :lines: 2- CodeIgniter reads its routing rules from top to bottom and routes the request to the first matching rule. Each rule is a regular expression @@ -205,18 +144,57 @@ arguments. More information about routing can be found in the URI Routing :doc:`documentation `. -Here, the second rule in the ``$routes`` object matches **any** request -using the wildcard string ``(:any)``. and passes the parameter to the +Here, the second rule in the ``$routes`` object matches GET request +to the URI path ``/pages`` maps the ``index()`` method of the ``Pages`` class. + +The third rule in the ``$routes`` object matches GET request to **any** URI path +using the wildcard string ``(:any)``, and passes the parameter to the ``view()`` method of the ``Pages`` class. +Running the App +*************** + +Ready to test? You cannot run the app using PHP's built-in server, +since it will not properly process the ``.htaccess`` rules that are provided in +``public``, and which eliminate the need to specify "index.php/" +as part of a URL. CodeIgniter has its own command that you can use though. + +From the command line, at the root of your project:: + + > php spark serve + +will start a web server, accessible on port 8080. If you set the location field +in your browser to ``localhost:8080``, you should see the CodeIgniter welcome page. + Now visit ``localhost:8080/home``. Did it get routed correctly to the ``view()`` -method in the pages controller? Awesome! +method in the ``Pages`` controller? Awesome! You should see something like the following: .. image:: ../images/tutorial1.png :align: center -.. note:: When manually specifying routes, it is recommended to disable - auto-routing by setting ``$routes->setAutoRoute(false);`` in the **Routes.php** file. - This ensures that only routes you define can be accessed. +You can now try several URLs in the browser location field, to see what the ``Pages`` +controller you made above produces... + +.. table:: + :widths: 20 80 + + +---------------------------------+-----------------------------------------------------------------+ + | URL | Will show | + +=================================+=================================================================+ + | localhost:8080/pages | the results from the ``index()`` method inside our ``Pages`` | + | | controller, which is to display the CodeIgniter "welcome" page. | + +---------------------------------+-----------------------------------------------------------------+ + | localhost:8080/pages/view | the "home" page that you made above, because it is the default | + | | "page" parameter to the ``view()`` method. | + +---------------------------------+-----------------------------------------------------------------+ + | localhost:8080/pages/view/home | show the "home" page that you made above, because we explicitly | + | | asked for it. | + +---------------------------------+-----------------------------------------------------------------+ + | localhost:8080/pages/view/about | the "about" page that you made above, because we explicitly | + | | asked for it. | + +---------------------------------+-----------------------------------------------------------------+ + | localhost:8080/pages/view/shop | a "404 - File Not Found" error page, because there is no | + | | **app/Views/pages/shop.php**. | + +---------------------------------+-----------------------------------------------------------------+ diff --git a/user_guide_src/source/tutorial/static_pages/003.php b/user_guide_src/source/tutorial/static_pages/003.php index d799d1cf0fb5..956c097d390f 100644 --- a/user_guide_src/source/tutorial/static_pages/003.php +++ b/user_guide_src/source/tutorial/static_pages/003.php @@ -1,3 +1,9 @@ get('/', 'Home::index'); + +// ... diff --git a/user_guide_src/source/tutorial/static_pages/004.php b/user_guide_src/source/tutorial/static_pages/004.php index f998b89762fd..a48a8caf18cb 100644 --- a/user_guide_src/source/tutorial/static_pages/004.php +++ b/user_guide_src/source/tutorial/static_pages/004.php @@ -1,3 +1,4 @@ get('pages', 'Pages::index'); $routes->get('(:any)', 'Pages::view/$1'); From 4027b1f9247ea3d6cf5b18c26a3d21179ccd7a67 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 2 Mar 2022 10:00:10 +0900 Subject: [PATCH 0025/8282] docs: change "note" to "warning" --- user_guide_src/source/incoming/controllers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index a4c635a6532c..928a16fff9b3 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -116,7 +116,7 @@ This section describes the functionality of the auto-routing. It automatically routes an HTTP request, and executes the corresponding controller method without route definitions. The auto-routing is disabled by default. -.. note:: To prevent misconfiguration and miscoding, we recommend that you disable +.. warning:: To prevent misconfiguration and miscoding, we recommend that you disable the auto-routing feature. .. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. From c7edb7c4c26ce82044b29607177de761e84b5311 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 2 Mar 2022 12:26:02 +0900 Subject: [PATCH 0026/8282] docs: make warning more detailed --- user_guide_src/source/incoming/controllers.rst | 5 +++-- user_guide_src/source/incoming/routing.rst | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index 928a16fff9b3..99c22b5a356d 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -116,8 +116,9 @@ This section describes the functionality of the auto-routing. It automatically routes an HTTP request, and executes the corresponding controller method without route definitions. The auto-routing is disabled by default. -.. warning:: To prevent misconfiguration and miscoding, we recommend that you disable - the auto-routing feature. +.. warning:: To prevent misconfiguration and miscoding, we recommend that you do not use + the auto-routing feature. It is easy to create vulnerable apps where controller filters + or CSRF protection are bypassed. .. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 4103abb6e428..504e555dd76c 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -515,8 +515,9 @@ It is recommended that all routes are defined in the **app/Config/Routes.php** f However, CodeIgniter can also automatically route HTTP requests based on conventions and execute the corresponding controller methods. -.. warning:: To prevent misconfiguration and miscoding, we recommend that you disable - the auto-routing feature. +.. warning:: To prevent misconfiguration and miscoding, we recommend that you do not use + the auto-routing feature. It is easy to create vulnerable apps where controller filters + or CSRF protection are bypassed. .. important:: The auto-routing routes a HTTP request with **any** HTTP method to a controller method. From d811d5b37ad4ede6d04afb4dd51ff510481e9205 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 2 Mar 2022 12:27:06 +0900 Subject: [PATCH 0027/8282] docs: update description --- user_guide_src/source/cli/cli.rst | 2 +- user_guide_src/source/incoming/routing.rst | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/cli/cli.rst b/user_guide_src/source/cli/cli.rst index f3ffcc9285f6..bd57d4f608fd 100644 --- a/user_guide_src/source/cli/cli.rst +++ b/user_guide_src/source/cli/cli.rst @@ -88,7 +88,7 @@ works exactly like a normal route definition: For more information, see the :ref:`Routes ` page. -.. warning:: If you don't disable auto-routing and place the command file in **app/Controllers**, +.. warning:: If you enable auto-routing and place the command file in **app/Controllers**, anyone could access the command with the help of auto-routing via HTTP. The CLI Library diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 504e555dd76c..4989f0891e0b 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -479,8 +479,12 @@ dash isn't a valid class or method name character and would cause a fatal error Use Defined Routes Only ======================= +Since v4.2.0, the auto-routing is disabled by default. + When no defined route is found that matches the URI, the system will attempt to match that URI against the -controllers and methods as described in :ref:`auto-routing`. You can disable this automatic matching, and restrict routes +controllers and methods when :ref:`auto-routing` is enabled. + +You can disable this automatic matching, and restrict routes to only those defined by you, by setting the ``setAutoRoute()`` option to false: .. literalinclude:: routing/050.php From 1fe49bafc27d3483f9d0c863c4dbc4c29fb853fb Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 2 Mar 2022 12:33:35 +0900 Subject: [PATCH 0028/8282] docs: add warning as a comment --- app/Config/Routes.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 2b35d0dda6f8..15371af7de5e 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -21,7 +21,10 @@ $routes->setDefaultMethod('index'); $routes->setTranslateURIDashes(false); $routes->set404Override(); -$routes->setAutoRoute(false); +// The auto-routing is very dangerous. It is easy to create vulnerable apps +// where controller filters or CSRF protection are bypassed. +// It is recommended that you do not set it to `true`. +//$routes->setAutoRoute(false); /* * -------------------------------------------------------------------- From a65bc3ab2f5302c11bff26e545b12ec7b75e4e8f Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 3 Mar 2022 09:21:21 +0900 Subject: [PATCH 0029/8282] docs: add about the config change of auto-routing in Upgrade Note --- user_guide_src/source/installation/upgrade_420.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/user_guide_src/source/installation/upgrade_420.rst b/user_guide_src/source/installation/upgrade_420.rst index a302f113ae61..daf89b39290f 100644 --- a/user_guide_src/source/installation/upgrade_420.rst +++ b/user_guide_src/source/installation/upgrade_420.rst @@ -49,7 +49,8 @@ Content Changes The following files received significant changes (including deprecations or visual adjustments) and it is recommended that you merge the updated versions with your application: -* +* ``app/Config/Routes.php`` + * To make the default configuration more secure, auto-routing has been changed to disabled by default. All Changes =========== From dafce4979cba1aed3b8a39705b919014fa6cd340 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 31 Mar 2022 10:34:00 +0900 Subject: [PATCH 0030/8282] feat: add options to change delimitors for conditionals To avoid accidentally changing JavaScript code. --- system/View/Parser.php | 45 +++++++++++++++++++++++++++-- tests/system/View/ParserTest.php | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/system/View/Parser.php b/system/View/Parser.php index 6834c003e500..665347d8f978 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -37,6 +37,16 @@ class Parser extends View */ public $rightDelimiter = '}'; + /** + * Left delimiter characters for conditionals + */ + protected string $leftConditionalDelimiter = '{'; + + /** + * Right delimiter characters for conditionals + */ + protected string $rightConditionalDelimiter = '}'; + /** * Stores extracted noparse blocks. * @@ -405,7 +415,14 @@ public function insertNoparse(string $template): string */ protected function parseConditionals(string $template): string { - $pattern = '/\{\s*(if|elseif)\s*((?:\()?(.*?)(?:\))?)\s*\}/ms'; + $leftDelimiter = preg_quote($this->leftConditionalDelimiter, '/'); + $rightDelimiter = preg_quote($this->rightConditionalDelimiter, '/'); + + $pattern = '/' + . $leftDelimiter + . '\s*(if|elseif)\s*((?:\()?(.*?)(?:\))?)\s*' + . $rightDelimiter + . '/ms'; /* * For each match: @@ -424,8 +441,16 @@ protected function parseConditionals(string $template): string $template = str_replace($match[0], $statement, $template); } - $template = preg_replace('/\{\s*else\s*\}/ms', '', $template); - $template = preg_replace('/\{\s*endif\s*\}/ms', '', $template); + $template = preg_replace( + '/' . $leftDelimiter . '\s*else\s*' . $rightDelimiter . '/ms', + '', + $template + ); + $template = preg_replace( + '/' . $leftDelimiter . '\s*endif\s*' . $rightDelimiter . '/ms', + '', + $template + ); // Parse the PHP itself, or insert an error so they can debug ob_start(); @@ -461,6 +486,20 @@ public function setDelimiters($leftDelimiter = '{', $rightDelimiter = '}'): Rend return $this; } + /** + * Over-ride the substitution conditional delimiters. + * + * @param string $leftDelimiter + * @param string $rightDelimiter + */ + public function setConditionalDelimiters($leftDelimiter = '{', $rightDelimiter = '}'): RendererInterface + { + $this->leftConditionalDelimiter = $leftDelimiter; + $this->rightConditionalDelimiter = $rightDelimiter; + + return $this; + } + /** * Handles replacing a pseudo-variable with the actual content. Will double-check * for escaping brackets. diff --git a/tests/system/View/ParserTest.php b/tests/system/View/ParserTest.php index 3462b0cc2a84..bf307a419f61 100644 --- a/tests/system/View/ParserTest.php +++ b/tests/system/View/ParserTest.php @@ -938,4 +938,53 @@ public function testRenderFindsOtherView() $expected = '

Hello World

'; $this->assertSame($expected, $this->parser->render('Simpler.html')); } + + public function testChangedConditionalDelimitersTrue() + { + $this->parser->setConditionalDelimiters('{%', '%}'); + + $data = [ + 'doit' => true, + 'dontdoit' => false, + ]; + $this->parser->setData($data); + + $template = '{% if $doit %}Howdy{% endif %}{% if $dontdoit === false %}Welcome{% endif %}'; + $output = $this->parser->renderString($template); + + $this->assertSame('HowdyWelcome', $output); + } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/5831 + */ + public function testChangeConditionalDelimitersWorkWithJavaScriptCode() + { + $this->parser->setConditionalDelimiters('{%', '%}'); + + $data = [ + 'message' => 'Warning!', + ]; + $this->parser->setData($data); + + $template = <<<'EOL' + + EOL; + $expected = <<<'EOL' + + EOL; + $this->assertSame($expected, $this->parser->renderString($template)); + } } From 2d90f0cc6ca2bca0927bd73413d04f60d77fd9a2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 31 Mar 2022 10:55:53 +0900 Subject: [PATCH 0031/8282] docs: add setConditionalDelimiters() --- .../source/outgoing/view_parser.rst | 25 +++++++++++++++++++ .../source/outgoing/view_parser/027.php | 3 +++ 2 files changed, 28 insertions(+) create mode 100644 user_guide_src/source/outgoing/view_parser/027.php diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index 2e615e52bdb9..752dcc489590 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -290,6 +290,31 @@ of the comparison operators you would normally, like ``==``, ``===``, ``!==``, ` .. warning:: In the background, conditionals are parsed using an ``eval()``, so you must ensure that you take care with the user data that is used within conditionals, or you could open your application up to security risks. +Changing the Conditional Delimiters +----------------------------------- + +If you have JavaScript code like the following in your templates, the Parser raises a syntax error because there are strings that can be interpreted as a conditional:: + + + +In that case, you can change the delimiters for conditionals with the ``setConditionalDelimiters()`` method to avoid misinterpretations: + +.. literalinclude:: view_parser/027.php + +In this case, you will write code in your template:: + + {% if $role=='admin' %} +

Welcome, Admin

+ {% else %} +

Welcome, User

+ {% endif %} + Escaping Data ============= diff --git a/user_guide_src/source/outgoing/view_parser/027.php b/user_guide_src/source/outgoing/view_parser/027.php new file mode 100644 index 000000000000..84730679ced5 --- /dev/null +++ b/user_guide_src/source/outgoing/view_parser/027.php @@ -0,0 +1,3 @@ +setConditionalDelimiters('{%', '%}'); From e521f66fdabee7d0d109761caf084809132ccebc Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 31 Mar 2022 11:40:15 +0900 Subject: [PATCH 0032/8282] docs: replace variables with properties They are different. --- user_guide_src/source/tutorial/static_pages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst index 6c39f82b5f80..668683995231 100644 --- a/user_guide_src/source/tutorial/static_pages.rst +++ b/user_guide_src/source/tutorial/static_pages.rst @@ -30,7 +30,7 @@ displays the CodeIgniter welcome page. The ``Pages`` class is extending the ``BaseController`` class that extends the ``CodeIgniter\Controller`` class. This means that the new Pages class can access the -methods and variables defined in the ``CodeIgniter\Controller`` class +methods and properties defined in the ``CodeIgniter\Controller`` class (**system/Controller.php**). The **controller is what will become the center of every request** to From 5dacb15c7ccd93ecb74b99d9788397941dd642cb Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 31 Mar 2022 11:59:08 +0900 Subject: [PATCH 0033/8282] fix: remove session _ci_validation_errors before running validation --- system/Validation/Validation.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index 155efc223492..f330c91799f6 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -108,6 +108,12 @@ public function __construct($config, RendererInterface $view) */ public function run(?array $data = null, ?string $group = null, ?string $dbGroup = null): bool { + // If there are still validation errors for redirect_with_input request, remove them. + // See `getErrors()` method. + if (isset($_SESSION, $_SESSION['_ci_validation_errors'])) { + unset($_SESSION['_ci_validation_errors']); + } + $data ??= $this->data; // i.e. is_unique From 35c2947d1a1988f37a422275e5ba037f32f11aa3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 31 Mar 2022 11:40:53 +0900 Subject: [PATCH 0034/8282] docs: remove `echo` in the controller and use `return` echo() has side effects, so return is better. --- .../source/tutorial/create_news_items/002.php | 10 +++++----- user_guide_src/source/tutorial/news_section/004.php | 6 +++--- user_guide_src/source/tutorial/news_section/006.php | 6 +++--- user_guide_src/source/tutorial/static_pages/002.php | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/user_guide_src/source/tutorial/create_news_items/002.php b/user_guide_src/source/tutorial/create_news_items/002.php index cdebb0a71e6a..66ff5e26776b 100644 --- a/user_guide_src/source/tutorial/create_news_items/002.php +++ b/user_guide_src/source/tutorial/create_news_items/002.php @@ -18,11 +18,11 @@ public function create() 'body' => $this->request->getPost('body'), ]); - echo view('news/success'); - } else { - echo view('templates/header', ['title' => 'Create a news item']); - echo view('news/create'); - echo view('templates/footer'); + return view('news/success'); } + + return view('templates/header', ['title' => 'Create a news item']) + . view('news/create') + . view('templates/footer'); } } diff --git a/user_guide_src/source/tutorial/news_section/004.php b/user_guide_src/source/tutorial/news_section/004.php index 475b86fa2f45..b62562bfe351 100644 --- a/user_guide_src/source/tutorial/news_section/004.php +++ b/user_guide_src/source/tutorial/news_section/004.php @@ -15,8 +15,8 @@ public function index() 'title' => 'News archive', ]; - echo view('templates/header', $data); - echo view('news/overview', $data); - echo view('templates/footer', $data); + return view('templates/header', $data) + . view('news/overview', $data) + . view('templates/footer', $data); } } diff --git a/user_guide_src/source/tutorial/news_section/006.php b/user_guide_src/source/tutorial/news_section/006.php index 36faacb39a50..8bfc123e43f7 100644 --- a/user_guide_src/source/tutorial/news_section/006.php +++ b/user_guide_src/source/tutorial/news_section/006.php @@ -18,8 +18,8 @@ public function view($slug = null) $data['title'] = $data['news']['title']; - echo view('templates/header', $data); - echo view('news/view', $data); - echo view('templates/footer', $data); + return view('templates/header', $data) + . view('news/view', $data) + . view('templates/footer', $data); } } diff --git a/user_guide_src/source/tutorial/static_pages/002.php b/user_guide_src/source/tutorial/static_pages/002.php index 970e663558d5..9f80c4556a58 100644 --- a/user_guide_src/source/tutorial/static_pages/002.php +++ b/user_guide_src/source/tutorial/static_pages/002.php @@ -13,8 +13,8 @@ public function view($page = 'home') $data['title'] = ucfirst($page); // Capitalize the first letter - echo view('templates/header', $data); - echo view('pages/' . $page, $data); - echo view('templates/footer', $data); + return view('templates/header', $data) + . view('pages/' . $page, $data) + . view('templates/footer', $data); } } From 59260c1cceda48673214e82b4607ba8567f135c4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 1 Apr 2022 11:27:15 +0900 Subject: [PATCH 0035/8282] docs: remove unneeded & in sample code --- user_guide_src/source/models/model/055.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/models/model/055.php b/user_guide_src/source/models/model/055.php index 1d62c54ba058..537199753d49 100644 --- a/user_guide_src/source/models/model/055.php +++ b/user_guide_src/source/models/model/055.php @@ -8,8 +8,8 @@ class UserModel { protected $db; - public function __construct(ConnectionInterface &$db) + public function __construct(ConnectionInterface $db) { - $this->db = &$db; + $this->db = $db; } } From d84440415a86cfd5deb32a88d74bbf4236ef9e75 Mon Sep 17 00:00:00 2001 From: Toto Date: Fri, 1 Apr 2022 21:26:11 +0700 Subject: [PATCH 0036/8282] update link https://codeigniter.com/en/contribute not found --- app/Views/welcome_message.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php index 9ee2e427c308..b982f58aa158 100644 --- a/app/Views/welcome_message.php +++ b/app/Views/welcome_message.php @@ -279,7 +279,7 @@

CodeIgniter is a community driven project and accepts contributions of code and documentation from the community. Why not - + join us ?

From 88a7f8b88a9bbd29daaea1f94deeb95a618ccd76 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Fri, 1 Apr 2022 12:28:28 -0500 Subject: [PATCH 0037/8282] Added contributors to readme --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a8a3566e395d..d8525f299037 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,19 @@ to optional packages, with their own repository. ## Contributing -We **are** accepting contributions from the community! +We **are** accepting contributions from the community! It doesn't matter whether you can code, write documentation, or help find bugs, +all contributions are welcome. Please read the [*Contributing to CodeIgniter*](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/README.md). +CodeIgniter has had thousands on contributions from people since its creation. This project would not be what it is without them. + + + + + +Made with [contrib.rocks](https://contrib.rocks). + ## Server Requirements PHP version 7.4 or higher is required, with the following extensions installed: From 27a74023bb25aefd752beb8f473393e84ac4fbfd Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 12:50:56 +0900 Subject: [PATCH 0038/8282] fix: FileLocator::listFiles() returns directories We don't need directories. --- system/Autoloader/FileLocator.php | 3 +++ tests/system/Autoloader/FileLocatorTest.php | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index f9734828b692..0b7b5d1d4345 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -309,6 +309,9 @@ public function listFiles(string $path): array $tempFiles = get_filenames($fullPath, true); + // Remove directories + $tempFiles = array_filter($tempFiles, static fn ($path) => strtolower(substr($path, -4)) === '.php'); + if (! empty($tempFiles)) { $files = array_merge($files, $tempFiles); } diff --git a/tests/system/Autoloader/FileLocatorTest.php b/tests/system/Autoloader/FileLocatorTest.php index 54a471d74453..3b86cfb7e535 100644 --- a/tests/system/Autoloader/FileLocatorTest.php +++ b/tests/system/Autoloader/FileLocatorTest.php @@ -211,6 +211,18 @@ public function testListFilesSimple() $this->assertTrue(in_array($expectedWin, $files, true) || in_array($expectedLin, $files, true)); } + public function testListFilesDoesNotContainDirectories() + { + $files = $this->locator->listFiles('Config/'); + + $directory = str_replace( + '/', + DIRECTORY_SEPARATOR, + APPPATH . 'Config/Boot' + ); + $this->assertNotContains($directory, $files); + } + public function testListFilesWithFileAsInput() { $files = $this->locator->listFiles('Config/App.php'); From 37e99bf13f1db654c546c2187d30dc9ec3c8b6f1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 12:53:10 +0900 Subject: [PATCH 0039/8282] docs: add @return --- system/Autoloader/FileLocator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 0b7b5d1d4345..cfe12b79c2fa 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -289,6 +289,8 @@ public function findQualifiedNameFromPath(string $path) /** * Scans the defined namespaces, returning a list of all files * that are contained within the subpath specified by $path. + * + * @return string[] List of file paths */ public function listFiles(string $path): array { @@ -323,6 +325,8 @@ public function listFiles(string $path): array /** * Scans the provided namespace, returning a list of all files * that are contained within the sub path specified by $path. + * + * @return string[] List of file paths */ public function listNamespaceFiles(string $prefix, string $path): array { From 7aba08ab2f93a2e42a452a6fd995ad9b5c308318 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 12:53:51 +0900 Subject: [PATCH 0040/8282] fix: discoverCommands() loads incorrect classname --- system/CLI/Commands.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/system/CLI/Commands.php b/system/CLI/Commands.php index d153b3aa857a..2714db30bcf9 100644 --- a/system/CLI/Commands.php +++ b/system/CLI/Commands.php @@ -76,10 +76,6 @@ public function getCommands() /** * Discovers all commands in the framework and within user code, * and collects instances of them to work with. - * - * @TODO this approach (find qualified classname from path) causes error, - * when using Composer autoloader. - * See https://github.com/codeigniter4/CodeIgniter4/issues/5818 */ public function discoverCommands() { @@ -100,9 +96,9 @@ public function discoverCommands() // Loop over each file checking to see if a command with that // alias exists in the class. foreach ($files as $file) { - $className = $locator->findQualifiedNameFromPath($file); + $className = $locator->getClassname($file); - if (empty($className) || ! class_exists($className)) { + if ($className === '' || ! class_exists($className)) { continue; } From 4c1dc38a75847a52e9efe2dab952cdcdbbc46ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tuna=20=C3=87a=C4=9Flar=20G=C3=BCm=C3=BC=C5=9F?= Date: Sat, 2 Apr 2022 13:58:12 +0300 Subject: [PATCH 0041/8282] wrong function name. Needs to be orWhere(). There is no or_where in CI 4. --- user_guide_src/source/database/query_builder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index d6e5b27aac36..5e05f0ce6b0b 100755 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -804,7 +804,7 @@ Generates a **DELETE** SQL string and runs the query. .. literalinclude:: query_builder/093.php The first parameter is the where clause. -You can also use the ``where()`` or ``or_where()`` methods instead of passing +You can also use the ``where()`` or ``orWhere()`` methods instead of passing the data to the first parameter of the method: .. literalinclude:: query_builder/094.php From de7c19c456bf944e61c9e32835097727cd5d79ef Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 14:02:14 +0900 Subject: [PATCH 0042/8282] test: fix assertions If we use Compoesr autoloader, the path would be like `/vendor/composer/../../app/Controllers/Home.php`. --- tests/system/Autoloader/AutoloaderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Autoloader/AutoloaderTest.php b/tests/system/Autoloader/AutoloaderTest.php index f9a56cae82db..82e0307affad 100644 --- a/tests/system/Autoloader/AutoloaderTest.php +++ b/tests/system/Autoloader/AutoloaderTest.php @@ -88,7 +88,7 @@ public function testServiceAutoLoaderFromShareInstances() // look for Home controller, as that should be in base repo $actual = $autoloader->loadClass('App\Controllers\Home'); $expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php'; - $this->assertSame($expected, $actual); + $this->assertSame($expected, realpath($actual) ?: $actual); } public function testServiceAutoLoader() @@ -99,7 +99,7 @@ public function testServiceAutoLoader() // look for Home controller, as that should be in base repo $actual = $autoloader->loadClass('App\Controllers\Home'); $expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php'; - $this->assertSame($expected, $actual); + $this->assertSame($expected, realpath($actual) ?: $actual); } public function testExistingFile() From 46b3de8aef4d74600235693a09a9052c476df731 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 14:07:33 +0900 Subject: [PATCH 0043/8282] refactor: extract method --- system/Autoloader/Autoloader.php | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index bfd535f856ca..7423fc235586 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -106,7 +106,8 @@ public function initialize(Autoload $config, Modules $modules) // Should we load through Composer's namespaces, also? if ($modules->discoverInComposer) { - $this->discoverComposerNamespaces(); + $this->loadComposerClassmap(); + $this->loadComposerNamespaces(); } return $this; @@ -296,10 +297,7 @@ public function sanitizeFilename(string $filename): string return trim($filename, '.-_'); } - /** - * Locates autoload information from Composer, if available. - */ - protected function discoverComposerNamespaces() + protected function loadComposerNamespaces() { if (! is_file(COMPOSER_PATH)) { return; @@ -310,7 +308,6 @@ protected function discoverComposerNamespaces() */ $composer = include COMPOSER_PATH; $paths = $composer->getPrefixesPsr4(); - $classes = $composer->getClassMap(); unset($composer); @@ -327,6 +324,23 @@ protected function discoverComposerNamespaces() } $this->prefixes = array_merge($this->prefixes, $newPaths); + } + + protected function loadComposerClassmap(): void + { + if (! is_file(COMPOSER_PATH)) { + return; + } + + /** + * @var ClassLoader $composer + */ + $composer = include COMPOSER_PATH; + + $classes = $composer->getClassMap(); + + unset($composer); + $this->classmap = array_merge($this->classmap, $classes); } } From ceb9418bd5ea051fd0a71fb916cb90a399bb0bda Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 14:17:41 +0900 Subject: [PATCH 0044/8282] refactor: move duplicated logic up --- system/Autoloader/Autoloader.php | 44 ++++++++++++-------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 7423fc235586..88f3dd743470 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -104,10 +104,19 @@ public function initialize(Autoload $config, Modules $modules) $this->files = $config->files; } - // Should we load through Composer's namespaces, also? - if ($modules->discoverInComposer) { - $this->loadComposerClassmap(); - $this->loadComposerNamespaces(); + if (is_file(COMPOSER_PATH)) { + /** + * @var ClassLoader $composer + */ + $composer = include COMPOSER_PATH; + + // Should we load through Composer's namespaces, also? + if ($modules->discoverInComposer) { + $this->loadComposerClassmap($composer); + $this->loadComposerNamespaces($composer); + } + + unset($composer); } return $this; @@ -297,19 +306,9 @@ public function sanitizeFilename(string $filename): string return trim($filename, '.-_'); } - protected function loadComposerNamespaces() + protected function loadComposerNamespaces(ClassLoader $composer): void { - if (! is_file(COMPOSER_PATH)) { - return; - } - - /** - * @var ClassLoader $composer - */ - $composer = include COMPOSER_PATH; - $paths = $composer->getPrefixesPsr4(); - - unset($composer); + $paths = $composer->getPrefixesPsr4(); // Get rid of CodeIgniter so we don't have duplicates if (isset($paths['CodeIgniter\\'])) { @@ -326,21 +325,10 @@ protected function loadComposerNamespaces() $this->prefixes = array_merge($this->prefixes, $newPaths); } - protected function loadComposerClassmap(): void + protected function loadComposerClassmap(ClassLoader $composer): void { - if (! is_file(COMPOSER_PATH)) { - return; - } - - /** - * @var ClassLoader $composer - */ - $composer = include COMPOSER_PATH; - $classes = $composer->getClassMap(); - unset($composer); - $this->classmap = array_merge($this->classmap, $classes); } } From 954b647ddd7ac3df8f455a44b4cbfa13f6f68194 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 14:18:22 +0900 Subject: [PATCH 0045/8282] fix: don't load Composer classmap if $discoverInComposer is false Now it always loads and uses Composer classmap if it is available. --- system/Autoloader/Autoloader.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 88f3dd743470..f4c37b4d250e 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -110,9 +110,10 @@ public function initialize(Autoload $config, Modules $modules) */ $composer = include COMPOSER_PATH; + $this->loadComposerClassmap($composer); + // Should we load through Composer's namespaces, also? if ($modules->discoverInComposer) { - $this->loadComposerClassmap($composer); $this->loadComposerNamespaces($composer); } From 783835efddd6c383de74c42402380c95903f88f5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 2 Apr 2022 14:20:09 +0900 Subject: [PATCH 0046/8282] refactor: extract method --- system/Autoloader/Autoloader.php | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index f4c37b4d250e..cca4a7d28f1e 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -105,22 +105,27 @@ public function initialize(Autoload $config, Modules $modules) } if (is_file(COMPOSER_PATH)) { - /** - * @var ClassLoader $composer - */ - $composer = include COMPOSER_PATH; + $this->loadComposerInfo($modules); + } - $this->loadComposerClassmap($composer); + return $this; + } - // Should we load through Composer's namespaces, also? - if ($modules->discoverInComposer) { - $this->loadComposerNamespaces($composer); - } + protected function loadComposerInfo(Modules $modules): void + { + /** + * @var ClassLoader $composer + */ + $composer = include COMPOSER_PATH; + + $this->loadComposerClassmap($composer); - unset($composer); + // Should we load through Composer's namespaces, also? + if ($modules->discoverInComposer) { + $this->loadComposerNamespaces($composer); } - return $this; + unset($composer); } /** From c179ed044558faa93d7ad05869b1e4c9b22069ca Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 08:01:39 +0900 Subject: [PATCH 0047/8282] refactor: restore discoverComposerNamespaces() and deprecate --- system/Autoloader/Autoloader.php | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index cca4a7d28f1e..7152f160c652 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -337,4 +337,40 @@ protected function loadComposerClassmap(ClassLoader $composer): void $this->classmap = array_merge($this->classmap, $classes); } + + /** + * Locates autoload information from Composer, if available. + * + * @deprecated No longer used. + */ + protected function discoverComposerNamespaces() + { + if (! is_file(COMPOSER_PATH)) { + return; + } + + /** + * @var ClassLoader $composer + */ + $composer = include COMPOSER_PATH; + $paths = $composer->getPrefixesPsr4(); + $classes = $composer->getClassMap(); + + unset($composer); + + // Get rid of CodeIgniter so we don't have duplicates + if (isset($paths['CodeIgniter\\'])) { + unset($paths['CodeIgniter\\']); + } + + $newPaths = []; + + foreach ($paths as $key => $value) { + // Composer stores namespaces with trailing slash. We don't. + $newPaths[rtrim($key, '\\ ')] = $value; + } + + $this->prefixes = array_merge($this->prefixes, $newPaths); + $this->classmap = array_merge($this->classmap, $classes); + } } From 577eefe25c4061e0a286b51969fc96ad177282b3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 08:03:15 +0900 Subject: [PATCH 0048/8282] refactor: make new methods private --- system/Autoloader/Autoloader.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 7152f160c652..520e669be685 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -111,7 +111,7 @@ public function initialize(Autoload $config, Modules $modules) return $this; } - protected function loadComposerInfo(Modules $modules): void + private function loadComposerInfo(Modules $modules): void { /** * @var ClassLoader $composer @@ -312,7 +312,7 @@ public function sanitizeFilename(string $filename): string return trim($filename, '.-_'); } - protected function loadComposerNamespaces(ClassLoader $composer): void + private function loadComposerNamespaces(ClassLoader $composer): void { $paths = $composer->getPrefixesPsr4(); @@ -331,7 +331,7 @@ protected function loadComposerNamespaces(ClassLoader $composer): void $this->prefixes = array_merge($this->prefixes, $newPaths); } - protected function loadComposerClassmap(ClassLoader $composer): void + private function loadComposerClassmap(ClassLoader $composer): void { $classes = $composer->getClassMap(); From c7e659fc367eefd76cb0680214880fb14eaa4585 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 08:13:44 +0900 Subject: [PATCH 0049/8282] docs: add changelog --- user_guide_src/source/changelogs/v4.2.0.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index 465054262b41..99f980d6b35e 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -17,6 +17,7 @@ BREAKING - ``public/index.php`` - ``spark`` - The method signature of ``CodeIgniter\CLI\CommandRunner::_remap()`` has been changed to fix a bug. +- The ``CodeIgniter\Autoloader\Autoloader::initialize()`` has changed the behavior to fix a bug. It used to use Composer classmap only when ``$modules->discoverInComposer`` is true. Now it always uses the Composer classmap if Composer is available. Enhancements ************ @@ -55,6 +56,7 @@ Deprecations - ``CodeIgniter\Log\Logger::cleanFilenames()`` and ``CodeIgniter\Test\TestLogger::cleanup()`` are both deprecated. Use the ``clean_path()`` function instead. - ``CodeIgniter\Router\Router::setDefaultController()`` is deprecated. - The constant ``SPARKED`` in **spark** is deprecated. Use the ``$context`` property in ``CodeIgniter\CodeIgniter`` instead. +- ``CodeIgniter\Autoloader\Autoloader::discoverComposerNamespaces()`` is deprecated, and no longer used. Bugs Fixed ********** From c03df2ea180af20c82ad94a8a96e55b2bab35b15 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:10:11 +0900 Subject: [PATCH 0050/8282] docs: add missing `+` --- user_guide_src/source/outgoing/view_parser.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index 752dcc489590..b431b9979602 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -458,7 +458,7 @@ While plugins will often consist of tag pairs, like shown above, they can also b Opening tags can also contain parameters that can customize how the plugin works. The parameters are represented as key/value pairs:: - {+ foo bar=2 baz="x y" } + {+ foo bar=2 baz="x y" +} Parameters can also be single values:: From 53fb7ac06fd959077323bec40d9c4d596d21d853 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:10:40 +0900 Subject: [PATCH 0051/8282] docs: remove unnecessary line breaks in sample code --- user_guide_src/source/installation/upgrade_view_parser/001.php | 3 +-- user_guide_src/source/outgoing/view_parser.rst | 3 +-- user_guide_src/source/outgoing/view_parser/003.php | 3 +-- user_guide_src/source/outgoing/view_parser/006.php | 3 +-- user_guide_src/source/outgoing/view_parser/007.php | 3 +-- user_guide_src/source/outgoing/view_parser/008.php | 3 +-- user_guide_src/source/outgoing/view_parser/018.php | 3 +-- user_guide_src/source/outgoing/view_parser/019.php | 3 +-- user_guide_src/source/outgoing/view_parser/020.php | 3 +-- user_guide_src/source/outgoing/view_parser/021.php | 3 +-- 10 files changed, 10 insertions(+), 20 deletions(-) diff --git a/user_guide_src/source/installation/upgrade_view_parser/001.php b/user_guide_src/source/installation/upgrade_view_parser/001.php index 6bef47ef69ec..27dc410b1708 100644 --- a/user_guide_src/source/installation/upgrade_view_parser/001.php +++ b/user_guide_src/source/installation/upgrade_view_parser/001.php @@ -7,5 +7,4 @@ 'blog_heading' => 'My Blog Heading', ]; -echo $parser->setData($data) - ->render('blog_template'); +echo $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index b431b9979602..6c6c2601b451 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -550,8 +550,7 @@ An example with the iteration controlled in the view:: ['title' => 'Second Link', 'link' => '/second'], ] ]; - echo $parser->setData($data) - ->renderString($template); + echo $parser->setData($data)->renderString($template); Result:: diff --git a/user_guide_src/source/outgoing/view_parser/003.php b/user_guide_src/source/outgoing/view_parser/003.php index 1ee48b997ae6..da4ef088f6bd 100644 --- a/user_guide_src/source/outgoing/view_parser/003.php +++ b/user_guide_src/source/outgoing/view_parser/003.php @@ -5,5 +5,4 @@ 'blog_heading' => 'My Blog Heading', ]; -echo $parser->setData($data) - ->render('blog_template'); +echo $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/006.php b/user_guide_src/source/outgoing/view_parser/006.php index 6b1409c46c1d..dfde61eec728 100644 --- a/user_guide_src/source/outgoing/view_parser/006.php +++ b/user_guide_src/source/outgoing/view_parser/006.php @@ -12,5 +12,4 @@ ], ]; -echo $parser->setData($data) - ->render('blog_template'); +echo $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/007.php b/user_guide_src/source/outgoing/view_parser/007.php index 032f9d55f268..b3ca937f711f 100644 --- a/user_guide_src/source/outgoing/view_parser/007.php +++ b/user_guide_src/source/outgoing/view_parser/007.php @@ -8,5 +8,4 @@ 'blog_entries' => $query->getResultArray(), ]; -echo $parser->setData($data) - ->render('blog_template'); +echo $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/008.php b/user_guide_src/source/outgoing/view_parser/008.php index 62be126a407d..4b0a6215830e 100644 --- a/user_guide_src/source/outgoing/view_parser/008.php +++ b/user_guide_src/source/outgoing/view_parser/008.php @@ -9,5 +9,4 @@ ], ]; -echo $parser->setData($data) - ->render('blog_template'); +echo $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/018.php b/user_guide_src/source/outgoing/view_parser/018.php index 4e039f02a08c..0bb60828d691 100644 --- a/user_guide_src/source/outgoing/view_parser/018.php +++ b/user_guide_src/source/outgoing/view_parser/018.php @@ -6,6 +6,5 @@ 'firstname' => 'John', 'lastname' => 'Doe', ]; -echo $parser->setData($data) - ->renderString($template); +echo $parser->setData($data)->renderString($template); // Result: Hello, John Doe diff --git a/user_guide_src/source/outgoing/view_parser/019.php b/user_guide_src/source/outgoing/view_parser/019.php index 16621ef2f190..a3cb80300299 100644 --- a/user_guide_src/source/outgoing/view_parser/019.php +++ b/user_guide_src/source/outgoing/view_parser/019.php @@ -6,6 +6,5 @@ 'firstname' => 'John', 'lastname' => 'Doe', ]; -echo $parser->setData($data) - ->renderString($template); +echo $parser->setData($data)->renderString($template); // Result: Hello, John {initials} Doe diff --git a/user_guide_src/source/outgoing/view_parser/020.php b/user_guide_src/source/outgoing/view_parser/020.php index 9226793f280d..62256192019d 100644 --- a/user_guide_src/source/outgoing/view_parser/020.php +++ b/user_guide_src/source/outgoing/view_parser/020.php @@ -10,6 +10,5 @@ ['degree' => 'PhD'], ], ]; -echo $parser->setData($data) - ->renderString($template); +echo $parser->setData($data)->renderString($template); // Result: Hello, John Doe (Mr{degree} {/degrees}) diff --git a/user_guide_src/source/outgoing/view_parser/021.php b/user_guide_src/source/outgoing/view_parser/021.php index 3c9982437a19..977b8b958140 100644 --- a/user_guide_src/source/outgoing/view_parser/021.php +++ b/user_guide_src/source/outgoing/view_parser/021.php @@ -15,5 +15,4 @@ $data = [ 'menuitems' => $temp, ]; -echo $parser->setData($data) - ->renderString($template2); +echo $parser->setData($data)->renderString($template2); From 71a7682d1eba5bad20109362e292479cf952f5b4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:13:15 +0900 Subject: [PATCH 0052/8282] docs: fix text decoration for variable --- user_guide_src/source/outgoing/view_parser.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index 6c6c2601b451..e3908ec0a391 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -491,7 +491,7 @@ Registering a Plugin -------------------- At its simplest, all you need to do to register a new plugin and make it ready for use is to add it to the -**app/Config/View.php**, under the **$plugins** array. The key is the name of the plugin that is +**app/Config/View.php**, under the ``$plugins`` array. The key is the name of the plugin that is used within the template file. The value is any valid PHP callable, including static class methods: .. literalinclude:: view_parser/014.php From 931c6f43a7724446b9fcf8dbd13b782e0dd352c6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:20:31 +0900 Subject: [PATCH 0053/8282] docs: remove unsupported options `leftDelimiter` and `rightDelimiter` --- user_guide_src/source/outgoing/view_parser.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index e3908ec0a391..67ee0b7680eb 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -595,8 +595,6 @@ Class Reference - ``cache_name`` - the ID used to save/retrieve a cached view result; defaults to the viewpath - ``cascadeData`` - true if the data pairs in effect when a nested or loop substitution occurs should be propagated - ``saveData`` - true if the view data parameter should be retained for subsequent calls - - ``leftDelimiter`` - the left delimiter to use in pseudo-variable syntax - - ``rightDelimiter`` - the right delimiter to use in pseudo-variable syntax Any conditional substitutions are performed first, then remaining substitutions are performed for each data pair. From 20194690313be9d7e7f60165808376baf49a12f9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:24:59 +0900 Subject: [PATCH 0054/8282] docs: rename variable name in sample code Make the same name as other sample code. --- user_guide_src/source/outgoing/view_parser/024.php | 2 +- user_guide_src/source/outgoing/view_parser/025.php | 2 +- user_guide_src/source/outgoing/view_parser/026.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/outgoing/view_parser/024.php b/user_guide_src/source/outgoing/view_parser/024.php index 42ddfa949331..17a0a477e756 100644 --- a/user_guide_src/source/outgoing/view_parser/024.php +++ b/user_guide_src/source/outgoing/view_parser/024.php @@ -1,3 +1,3 @@ setData(['name' => 'George', 'position' => 'Boss']); +$parser->setData(['name' => 'George', 'position' => 'Boss']); diff --git a/user_guide_src/source/outgoing/view_parser/025.php b/user_guide_src/source/outgoing/view_parser/025.php index 8fa85f4faa98..fe67fe17aeb4 100644 --- a/user_guide_src/source/outgoing/view_parser/025.php +++ b/user_guide_src/source/outgoing/view_parser/025.php @@ -1,3 +1,3 @@ setVar('name', 'Joe', 'html'); +$parser->setVar('name', 'Joe', 'html'); diff --git a/user_guide_src/source/outgoing/view_parser/026.php b/user_guide_src/source/outgoing/view_parser/026.php index 46cfd9afd818..a7efd14837f2 100644 --- a/user_guide_src/source/outgoing/view_parser/026.php +++ b/user_guide_src/source/outgoing/view_parser/026.php @@ -1,3 +1,3 @@ setDelimiters('[', ']'); +$parser->setDelimiters('[', ']'); From 08d4dbecccf14847f098804edfa2987120668efe Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:29:10 +0900 Subject: [PATCH 0055/8282] docs: add setConditionalDelimiters() in Class Reference --- user_guide_src/source/outgoing/view_parser.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index 67ee0b7680eb..c369e7a5a78b 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -652,3 +652,14 @@ Class Reference Override the substitution field delimiters: .. literalinclude:: view_parser/026.php + + .. php:method:: setConditionalDelimiters($leftDelimiter = '{', $rightDelimiter = '}') + + :param string $leftDelimiter: Left delimiter for conditionals + :param string $rightDelimiter: right delimiter for conditionals + :returns: The Renderer, for method chaining + :rtype: CodeIgniter\\View\\RendererInterface. + + Override the conditional delimiters: + + .. literalinclude:: view_parser/027.php From bbea1665c368b235086a7b3cbbc5f462431e26f3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:32:27 +0900 Subject: [PATCH 0056/8282] docs: remove parameters of methods in text No parameters make them easier to read. --- user_guide_src/source/outgoing/view_parser.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index c369e7a5a78b..f4239b320207 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -53,9 +53,9 @@ can instantiate it directly: .. literalinclude:: view_parser/002.php Then you can use any of the three standard rendering methods that it provides: -``render(viewpath, options, save)``, ``setVar(name, value, context)`` and -``setData(data, context)``. You will also be able to specify delimiters directly, -through the ``setDelimiters(left, right)`` method. +``render()``, ``setVar()`` and +``setData()``. You will also be able to specify delimiters directly, +through the ``setDelimiters()`` method. Using the ``Parser``, your view templates are processed only by the Parser itself, and not like a conventional view PHP script. PHP code in such a script From faf39c2003bbb17c04957facf03d9300b0969424 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:36:29 +0900 Subject: [PATCH 0057/8282] docs: make text as a important note --- user_guide_src/source/outgoing/view_parser.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index f4239b320207..b72388486eb6 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -57,11 +57,11 @@ Then you can use any of the three standard rendering methods that it provides: ``setData()``. You will also be able to specify delimiters directly, through the ``setDelimiters()`` method. -Using the ``Parser``, your view templates are processed only by the Parser -itself, and not like a conventional view PHP script. PHP code in such a script -is ignored by the parser, and only substitutions are performed. +.. important:: Using the ``Parser``, your view templates are processed only by the Parser + itself, and not like a conventional view PHP script. PHP code in such a script + is ignored by the parser, and only substitutions are performed. -This is purposeful: view files with no PHP. + This is purposeful: view files with no PHP. What It Does ============ From d368c511299d7ca063e9cfa70f56761924129b7c Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:44:45 +0900 Subject: [PATCH 0058/8282] docs: fix text decoration for filter --- user_guide_src/source/outgoing/view_parser.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index b72388486eb6..50b4b4376707 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -336,7 +336,7 @@ Filters Any single variable substitution can have one or more filters applied to it to modify the way it is presented. These are not intended to drastically change the output, but provide ways to reuse the same variable data but with different -presentations. The **esc** filter discussed above is one example. Dates are another common use case, where you might +presentations. The ``esc`` filter discussed above is one example. Dates are another common use case, where you might need to format the same data differently in several sections on the same page. Filters are commands that come after the pseudo-variable name, and are separated by the pipe symbol, ``|``:: From b5423cc15a7f8471cc14d32ea7da22bc3ca821d9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 09:45:05 +0900 Subject: [PATCH 0059/8282] docs: add space between tags --- user_guide_src/source/outgoing/view_parser.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index 50b4b4376707..961d8ea96c31 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -250,7 +250,7 @@ This example gives different results, depending on cascading: Preventing Parsing ================== -You can specify portions of the page to not be parsed with the ``{noparse}{/noparse}`` tag pair. Anything in this +You can specify portions of the page to not be parsed with the ``{noparse}`` ``{/noparse}`` tag pair. Anything in this section will stay exactly as it is, with no variable substitution, looping, etc, happening to the markup between the brackets. :: From cf4d83d1e5d7e144a44d49e19892ac819d908c6c Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 10:25:57 +0900 Subject: [PATCH 0060/8282] docs: add links to reference --- user_guide_src/source/tutorial/conclusion.rst | 5 +++-- user_guide_src/source/tutorial/news_section.rst | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/tutorial/conclusion.rst b/user_guide_src/source/tutorial/conclusion.rst index afb38465904b..2a4f66264784 100644 --- a/user_guide_src/source/tutorial/conclusion.rst +++ b/user_guide_src/source/tutorial/conclusion.rst @@ -10,8 +10,9 @@ design patterns, which you can expand upon. Now that you've completed this tutorial, we recommend you check out the rest of the documentation. CodeIgniter is often praised because of its comprehensive documentation. Use this to your advantage and read the -"Overview" and "General Topics" sections thoroughly. You should read -the class and helper references when needed. +:doc:`Overview ` and :doc:`/general/index` +sections thoroughly. You should read +the :doc:`Library ` and :doc:`/helpers/index` references when needed. Every intermediate PHP programmer should be able to get the hang of CodeIgniter within a few days. diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst index a27902819265..aca19a25cad3 100644 --- a/user_guide_src/source/tutorial/news_section.rst +++ b/user_guide_src/source/tutorial/news_section.rst @@ -80,7 +80,7 @@ library. This will make the database class available through the Now that the database and a model have been set up, you'll need a method to get all of our posts from our database. To do this, the database abstraction layer that is included with CodeIgniter - -:doc:`Query Builder <../database/query_builder>` - is used. This makes it +:doc:`Query Builder <../database/query_builder>` - is used in the ``CodeIgnite\Model``. This makes it possible to write your 'queries' once and make them work on :doc:`all supported database systems <../intro/requirements>`. The Model class also allows you to easily work with the Query Builder and provides From 3f480d453479b17a6d75cc0abadf3ded39c65e34 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 10:26:28 +0900 Subject: [PATCH 0061/8282] docs: update forum url and add slack url --- user_guide_src/source/tutorial/conclusion.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/user_guide_src/source/tutorial/conclusion.rst b/user_guide_src/source/tutorial/conclusion.rst index 2a4f66264784..94b8ba5e3f2d 100644 --- a/user_guide_src/source/tutorial/conclusion.rst +++ b/user_guide_src/source/tutorial/conclusion.rst @@ -20,4 +20,5 @@ CodeIgniter within a few days. If you still have questions about the framework or your own CodeIgniter code, you can: -- Check out our `forums `_ +- Check out our `Forum `_ +- Check out our `Slack `_ From f58bac1954d5c189bf635309eb2a1ec6e8d5ed84 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 10:26:58 +0900 Subject: [PATCH 0062/8282] docs: remove unneeded `` --- user_guide_src/source/tutorial/create_news_items.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/tutorial/create_news_items.rst b/user_guide_src/source/tutorial/create_news_items.rst index 49ebc36c2bc3..a59124515a59 100644 --- a/user_guide_src/source/tutorial/create_news_items.rst +++ b/user_guide_src/source/tutorial/create_news_items.rst @@ -50,7 +50,7 @@ the slug from our title in the model. Create a new view at There are probably only three things here that look unfamiliar. -The ``getFlashdata('error') ?>`` function is used to report +The ``session()->getFlashdata('error')`` function is used to report errors related to CSRF protection. The ``service('validation')->listErrors()`` function is used to report From 54d80bcd0727c9f10c5f7ebf2f98548131732402 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 10:27:26 +0900 Subject: [PATCH 0063/8282] docs: fix link to validation page --- user_guide_src/source/incoming/controllers.rst | 2 ++ user_guide_src/source/tutorial/create_news_items.rst | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index 99c22b5a356d..c5a963c81fdd 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -61,6 +61,8 @@ modify this by passing the duration (in seconds) as the first parameter: .. note:: A number of :doc:`time-based constants ` are always available for you to use, including ``YEAR``, ``MONTH``, and more. +.. _controller-validate: + Validating data *************** diff --git a/user_guide_src/source/tutorial/create_news_items.rst b/user_guide_src/source/tutorial/create_news_items.rst index a59124515a59..99d28abf3cdb 100644 --- a/user_guide_src/source/tutorial/create_news_items.rst +++ b/user_guide_src/source/tutorial/create_news_items.rst @@ -60,8 +60,8 @@ The ``csrf_field()`` function creates a hidden input with a CSRF token that help Go back to your ``News`` controller. You're going to do two things here, check whether the form was submitted and whether the submitted data -passed the validation rules. You'll use the :doc:`form -validation <../libraries/validation>` library to do this. +passed the validation rules. +You'll use the :ref:`validation method in Controller ` to do this. .. literalinclude:: create_news_items/002.php From 149555ebd23d404ecf8684e52cb363365a7470ef Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 13:47:18 +0900 Subject: [PATCH 0064/8282] docs: replace echo with return --- user_guide_src/source/tutorial/static_pages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst index 668683995231..6fde0a671da7 100644 --- a/user_guide_src/source/tutorial/static_pages.rst +++ b/user_guide_src/source/tutorial/static_pages.rst @@ -24,7 +24,7 @@ displays the CodeIgniter welcome page. .. note:: There are two ``view()`` functions referred to in this tutorial. One is the class method created with ``public function view($page = 'home')`` - and ``echo view('welcome_message')`` for displaying a view. + and ``return view('welcome_message')`` for displaying a view. Both are *technically* a function. But when you create a function in a class, it's called a method. From 977eb2561db4108b6ed3dfe97541de150e9b6429 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 08:35:49 +0900 Subject: [PATCH 0065/8282] docs: remove unneeded $data for view() $saveData is true by default, so we don't need to pass $data more than once. --- user_guide_src/source/tutorial/news_section/004.php | 4 ++-- user_guide_src/source/tutorial/news_section/006.php | 4 ++-- user_guide_src/source/tutorial/static_pages/002.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/user_guide_src/source/tutorial/news_section/004.php b/user_guide_src/source/tutorial/news_section/004.php index b62562bfe351..9e823b6f7589 100644 --- a/user_guide_src/source/tutorial/news_section/004.php +++ b/user_guide_src/source/tutorial/news_section/004.php @@ -16,7 +16,7 @@ public function index() ]; return view('templates/header', $data) - . view('news/overview', $data) - . view('templates/footer', $data); + . view('news/overview') + . view('templates/footer'); } } diff --git a/user_guide_src/source/tutorial/news_section/006.php b/user_guide_src/source/tutorial/news_section/006.php index 8bfc123e43f7..94548ac9307f 100644 --- a/user_guide_src/source/tutorial/news_section/006.php +++ b/user_guide_src/source/tutorial/news_section/006.php @@ -19,7 +19,7 @@ public function view($slug = null) $data['title'] = $data['news']['title']; return view('templates/header', $data) - . view('news/view', $data) - . view('templates/footer', $data); + . view('news/view') + . view('templates/footer'); } } diff --git a/user_guide_src/source/tutorial/static_pages/002.php b/user_guide_src/source/tutorial/static_pages/002.php index 9f80c4556a58..5c8ec102a5a1 100644 --- a/user_guide_src/source/tutorial/static_pages/002.php +++ b/user_guide_src/source/tutorial/static_pages/002.php @@ -14,7 +14,7 @@ public function view($page = 'home') $data['title'] = ucfirst($page); // Capitalize the first letter return view('templates/header', $data) - . view('pages/' . $page, $data) - . view('templates/footer', $data); + . view('pages/' . $page) + . view('templates/footer'); } } From e0569f673f0c119b620f37981bf90dd19d8390a1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Apr 2022 14:19:58 +0900 Subject: [PATCH 0066/8282] docs: replace echo with return echo() has side effects, so return is better. --- user_guide_src/source/cli/cli/001.php | 2 +- user_guide_src/source/incoming/controllers/008.php | 2 +- user_guide_src/source/incoming/controllers/013.php | 4 ++-- user_guide_src/source/incoming/controllers/014.php | 4 ++-- .../source/installation/upgrade_controllers/001.php | 2 +- .../source/installation/upgrade_file_upload/001.php | 11 +++++------ .../source/installation/upgrade_pagination/001.php | 2 +- .../source/installation/upgrade_view_parser.rst | 2 +- .../source/installation/upgrade_view_parser/001.php | 2 +- user_guide_src/source/installation/upgrade_views.rst | 4 ++-- user_guide_src/source/libraries/pagination/002.php | 2 +- user_guide_src/source/libraries/validation/001.php | 6 +++--- user_guide_src/source/outgoing/view_layouts/001.php | 2 +- user_guide_src/source/outgoing/view_parser.rst | 3 ++- user_guide_src/source/outgoing/view_parser/003.php | 2 +- user_guide_src/source/outgoing/view_parser/004.php | 2 +- user_guide_src/source/outgoing/view_parser/005.php | 2 +- user_guide_src/source/outgoing/view_parser/006.php | 2 +- user_guide_src/source/outgoing/view_parser/007.php | 2 +- user_guide_src/source/outgoing/view_parser/008.php | 2 +- user_guide_src/source/outgoing/view_parser/009.php | 2 +- user_guide_src/source/outgoing/view_parser/010.php | 6 ++++-- user_guide_src/source/outgoing/view_parser/018.php | 3 ++- user_guide_src/source/outgoing/view_parser/019.php | 3 ++- user_guide_src/source/outgoing/view_parser/020.php | 3 ++- user_guide_src/source/outgoing/view_parser/021.php | 3 ++- user_guide_src/source/outgoing/view_parser/022.php | 2 +- user_guide_src/source/outgoing/view_parser/023.php | 2 +- user_guide_src/source/outgoing/views.rst | 3 --- user_guide_src/source/outgoing/views/001.php | 2 +- user_guide_src/source/outgoing/views/002.php | 2 +- user_guide_src/source/outgoing/views/003.php | 8 ++++---- user_guide_src/source/outgoing/views/004.php | 2 +- user_guide_src/source/outgoing/views/005.php | 2 +- user_guide_src/source/outgoing/views/006.php | 2 +- user_guide_src/source/outgoing/views/007.php | 2 +- user_guide_src/source/outgoing/views/008.php | 2 +- user_guide_src/source/outgoing/views/009.php | 2 +- user_guide_src/source/outgoing/views/010.php | 2 +- user_guide_src/source/outgoing/views/011.php | 2 +- 40 files changed, 59 insertions(+), 56 deletions(-) diff --git a/user_guide_src/source/cli/cli/001.php b/user_guide_src/source/cli/cli/001.php index b407ee50b145..999a5482e4d6 100644 --- a/user_guide_src/source/cli/cli/001.php +++ b/user_guide_src/source/cli/cli/001.php @@ -8,6 +8,6 @@ class Tools extends Controller { public function message($to = 'World') { - echo "Hello {$to}!" . PHP_EOL; + return "Hello {$to}!" . PHP_EOL; } } diff --git a/user_guide_src/source/incoming/controllers/008.php b/user_guide_src/source/incoming/controllers/008.php index 926de1e9fbdf..3aa859567c44 100644 --- a/user_guide_src/source/incoming/controllers/008.php +++ b/user_guide_src/source/incoming/controllers/008.php @@ -6,6 +6,6 @@ class Helloworld extends BaseController { public function index() { - echo 'Hello World!'; + return 'Hello World!'; } } diff --git a/user_guide_src/source/incoming/controllers/013.php b/user_guide_src/source/incoming/controllers/013.php index e43c3e4f2b34..68d4450d4e3a 100644 --- a/user_guide_src/source/incoming/controllers/013.php +++ b/user_guide_src/source/incoming/controllers/013.php @@ -6,11 +6,11 @@ class Helloworld extends BaseController { public function index() { - echo 'Hello World!'; + return 'Hello World!'; } public function comment() { - echo 'I am not flat!'; + return 'I am not flat!'; } } diff --git a/user_guide_src/source/incoming/controllers/014.php b/user_guide_src/source/incoming/controllers/014.php index 319fd8471a7b..da295eb61836 100644 --- a/user_guide_src/source/incoming/controllers/014.php +++ b/user_guide_src/source/incoming/controllers/014.php @@ -6,7 +6,7 @@ class Products extends BaseController { public function shoes($sandals, $id) { - echo $sandals; - echo $id; + return $sandals + . $id; } } diff --git a/user_guide_src/source/installation/upgrade_controllers/001.php b/user_guide_src/source/installation/upgrade_controllers/001.php index 5f292b54d1b8..b90fe63c538f 100644 --- a/user_guide_src/source/installation/upgrade_controllers/001.php +++ b/user_guide_src/source/installation/upgrade_controllers/001.php @@ -6,6 +6,6 @@ class Helloworld extends BaseController { public function index($name) { - echo 'Hello ' . esc($name) . '!'; + return 'Hello ' . esc($name) . '!'; } } diff --git a/user_guide_src/source/installation/upgrade_file_upload/001.php b/user_guide_src/source/installation/upgrade_file_upload/001.php index 6a4e6c88e250..a8b727c29745 100644 --- a/user_guide_src/source/installation/upgrade_file_upload/001.php +++ b/user_guide_src/source/installation/upgrade_file_upload/001.php @@ -6,7 +6,7 @@ class Upload extends BaseController { public function index() { - echo view('upload_form', ['error' => ' ']); + return view('upload_form', ['error' => ' ']); } public function do_upload() @@ -20,11 +20,10 @@ public function do_upload() $file = $this->request->getFile('userfile'); if (! $path = $file->store()) { - echo view('upload_form', ['error' => 'upload failed']); - } else { - $data = ['upload_file_path' => $path]; - - echo view('upload_success', $data); + return view('upload_form', ['error' => 'upload failed']); } + $data = ['upload_file_path' => $path]; + + return view('upload_success', $data); } } diff --git a/user_guide_src/source/installation/upgrade_pagination/001.php b/user_guide_src/source/installation/upgrade_pagination/001.php index fa1b373a5fc6..62710360fca9 100644 --- a/user_guide_src/source/installation/upgrade_pagination/001.php +++ b/user_guide_src/source/installation/upgrade_pagination/001.php @@ -7,4 +7,4 @@ 'pager' => $model->pager, ]; -echo view('users/index', $data); +return view('users/index', $data); diff --git a/user_guide_src/source/installation/upgrade_view_parser.rst b/user_guide_src/source/installation/upgrade_view_parser.rst index 55e86fb357a4..c58aeb8dd491 100644 --- a/user_guide_src/source/installation/upgrade_view_parser.rst +++ b/user_guide_src/source/installation/upgrade_view_parser.rst @@ -19,7 +19,7 @@ What has been changed Upgrade Guide ============= 1. Wherever you use the View Parser Library replace ``$this->load->library('parser');`` with ``$parser = service('parser');``. -2. You have to change the render part in your controller from ``$this->parser->parse('blog_template', $data);`` to ``echo $parser->setData($data)->render('blog_template');``. +2. You have to change the render part in your controller from ``$this->parser->parse('blog_template', $data);`` to ``return $parser->setData($data)->render('blog_template');``. Code Example ============ diff --git a/user_guide_src/source/installation/upgrade_view_parser/001.php b/user_guide_src/source/installation/upgrade_view_parser/001.php index 27dc410b1708..9acf5e9449d8 100644 --- a/user_guide_src/source/installation/upgrade_view_parser/001.php +++ b/user_guide_src/source/installation/upgrade_view_parser/001.php @@ -7,4 +7,4 @@ 'blog_heading' => 'My Blog Heading', ]; -echo $parser->setData($data)->render('blog_template'); +return $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/installation/upgrade_views.rst b/user_guide_src/source/installation/upgrade_views.rst index 8509705c76dc..29dcace6ee62 100644 --- a/user_guide_src/source/installation/upgrade_views.rst +++ b/user_guide_src/source/installation/upgrade_views.rst @@ -15,7 +15,7 @@ What has been changed ===================== - Your views look much like before, but they are invoked differently ... instead of CI3's - ``$this->load->view(x);``, you can use ``echo view(x);``. + ``$this->load->view(x);``, you can use ``return view(x);``. - CI4 supports *View Cells* to build your response in pieces, and *View Layouts* for page layout. - The template parser is still there, and substantially enhanced. @@ -24,7 +24,7 @@ Upgrade Guide 1. First, move all views to the folder **app/Views** 2. Change the loading syntax of views in every script where you load views: - - from ``$this->load->view('directory_name/file_name')`` to ``echo view('directory_name/file_name');`` + - from ``$this->load->view('directory_name/file_name')`` to ``return view('directory_name/file_name');`` - from ``$content = $this->load->view('file', $data, TRUE);`` to ``$content = view('file', $data);`` 3. (optional) You can change the echo syntax in views from ```` to ```` diff --git a/user_guide_src/source/libraries/pagination/002.php b/user_guide_src/source/libraries/pagination/002.php index baaa8471111f..c8cd31ebec5c 100644 --- a/user_guide_src/source/libraries/pagination/002.php +++ b/user_guide_src/source/libraries/pagination/002.php @@ -15,6 +15,6 @@ public function index() 'pager' => $model->pager, ]; - echo view('users/index', $data); + return view('users/index', $data); } } diff --git a/user_guide_src/source/libraries/validation/001.php b/user_guide_src/source/libraries/validation/001.php index e52362c46cea..41d51f1b8a25 100644 --- a/user_guide_src/source/libraries/validation/001.php +++ b/user_guide_src/source/libraries/validation/001.php @@ -11,11 +11,11 @@ public function index() helper(['form', 'url']); if (! $this->validate([])) { - echo view('Signup', [ + return view('Signup', [ 'validation' => $this->validator, ]); - } else { - echo view('Success'); } + + return view('Success'); } } diff --git a/user_guide_src/source/outgoing/view_layouts/001.php b/user_guide_src/source/outgoing/view_layouts/001.php index 2eed4c4f53dc..f6105365fa47 100644 --- a/user_guide_src/source/outgoing/view_layouts/001.php +++ b/user_guide_src/source/outgoing/view_layouts/001.php @@ -6,6 +6,6 @@ class MyController extends BaseController { public function index() { - echo view('some_view'); + return view('some_view'); } } diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index 961d8ea96c31..c0705e7fa257 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -550,7 +550,8 @@ An example with the iteration controlled in the view:: ['title' => 'Second Link', 'link' => '/second'], ] ]; - echo $parser->setData($data)->renderString($template); + + return $parser->setData($data)->renderString($template); Result:: diff --git a/user_guide_src/source/outgoing/view_parser/003.php b/user_guide_src/source/outgoing/view_parser/003.php index da4ef088f6bd..90f3c88273fc 100644 --- a/user_guide_src/source/outgoing/view_parser/003.php +++ b/user_guide_src/source/outgoing/view_parser/003.php @@ -5,4 +5,4 @@ 'blog_heading' => 'My Blog Heading', ]; -echo $parser->setData($data)->render('blog_template'); +return $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/004.php b/user_guide_src/source/outgoing/view_parser/004.php index 70862b585adc..643f541a5b3e 100644 --- a/user_guide_src/source/outgoing/view_parser/004.php +++ b/user_guide_src/source/outgoing/view_parser/004.php @@ -1,6 +1,6 @@ render('blog_template', [ +return $parser->render('blog_template', [ 'cache' => HOUR, 'cache_name' => 'something_unique', ]); diff --git a/user_guide_src/source/outgoing/view_parser/005.php b/user_guide_src/source/outgoing/view_parser/005.php index e77c54268648..e85e49db45a2 100644 --- a/user_guide_src/source/outgoing/view_parser/005.php +++ b/user_guide_src/source/outgoing/view_parser/005.php @@ -3,5 +3,5 @@ $template = '{blog_title}'; $data = ['blog_title' => 'My ramblings']; -echo $parser->setData($data)->renderString($template); +return $parser->setData($data)->renderString($template); // Result: My ramblings diff --git a/user_guide_src/source/outgoing/view_parser/006.php b/user_guide_src/source/outgoing/view_parser/006.php index dfde61eec728..40d379aec2ea 100644 --- a/user_guide_src/source/outgoing/view_parser/006.php +++ b/user_guide_src/source/outgoing/view_parser/006.php @@ -12,4 +12,4 @@ ], ]; -echo $parser->setData($data)->render('blog_template'); +return $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/007.php b/user_guide_src/source/outgoing/view_parser/007.php index b3ca937f711f..5128ed3cbe4a 100644 --- a/user_guide_src/source/outgoing/view_parser/007.php +++ b/user_guide_src/source/outgoing/view_parser/007.php @@ -8,4 +8,4 @@ 'blog_entries' => $query->getResultArray(), ]; -echo $parser->setData($data)->render('blog_template'); +return $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/008.php b/user_guide_src/source/outgoing/view_parser/008.php index 4b0a6215830e..8cac9bbbaa9d 100644 --- a/user_guide_src/source/outgoing/view_parser/008.php +++ b/user_guide_src/source/outgoing/view_parser/008.php @@ -9,4 +9,4 @@ ], ]; -echo $parser->setData($data)->render('blog_template'); +return $parser->setData($data)->render('blog_template'); diff --git a/user_guide_src/source/outgoing/view_parser/009.php b/user_guide_src/source/outgoing/view_parser/009.php index 6b112fdd6829..6f4b96e82274 100644 --- a/user_guide_src/source/outgoing/view_parser/009.php +++ b/user_guide_src/source/outgoing/view_parser/009.php @@ -7,5 +7,5 @@ 'location' => ['city' => 'Red City', 'planet' => 'Mars'], ]; -echo $parser->setData($data)->renderString($template); +return $parser->setData($data)->renderString($template); // Result: George lives in Red City on Mars. diff --git a/user_guide_src/source/outgoing/view_parser/010.php b/user_guide_src/source/outgoing/view_parser/010.php index 7fcd5bafbfec..4eb11fd6d908 100644 --- a/user_guide_src/source/outgoing/view_parser/010.php +++ b/user_guide_src/source/outgoing/view_parser/010.php @@ -7,8 +7,10 @@ 'location' => ['city' => 'Red City', 'planet' => 'Mars'], ]; -echo $parser->setData($data)->renderString($template, ['cascadeData' => false]); +return $parser->setData($data)->renderString($template, ['cascadeData' => false]); // Result: {name} lives in Red City on Mars. -echo $parser->setData($data)->renderString($template, ['cascadeData' => true]); +// or + +return $parser->setData($data)->renderString($template, ['cascadeData' => true]); // Result: George lives in Red City on Mars. diff --git a/user_guide_src/source/outgoing/view_parser/018.php b/user_guide_src/source/outgoing/view_parser/018.php index 0bb60828d691..5a88dd651c25 100644 --- a/user_guide_src/source/outgoing/view_parser/018.php +++ b/user_guide_src/source/outgoing/view_parser/018.php @@ -6,5 +6,6 @@ 'firstname' => 'John', 'lastname' => 'Doe', ]; -echo $parser->setData($data)->renderString($template); + +return $parser->setData($data)->renderString($template); // Result: Hello, John Doe diff --git a/user_guide_src/source/outgoing/view_parser/019.php b/user_guide_src/source/outgoing/view_parser/019.php index a3cb80300299..5f465be2296b 100644 --- a/user_guide_src/source/outgoing/view_parser/019.php +++ b/user_guide_src/source/outgoing/view_parser/019.php @@ -6,5 +6,6 @@ 'firstname' => 'John', 'lastname' => 'Doe', ]; -echo $parser->setData($data)->renderString($template); + +return $parser->setData($data)->renderString($template); // Result: Hello, John {initials} Doe diff --git a/user_guide_src/source/outgoing/view_parser/020.php b/user_guide_src/source/outgoing/view_parser/020.php index 62256192019d..fd072cbc0e5c 100644 --- a/user_guide_src/source/outgoing/view_parser/020.php +++ b/user_guide_src/source/outgoing/view_parser/020.php @@ -10,5 +10,6 @@ ['degree' => 'PhD'], ], ]; -echo $parser->setData($data)->renderString($template); + +return $parser->setData($data)->renderString($template); // Result: Hello, John Doe (Mr{degree} {/degrees}) diff --git a/user_guide_src/source/outgoing/view_parser/021.php b/user_guide_src/source/outgoing/view_parser/021.php index 977b8b958140..60fbb625da79 100644 --- a/user_guide_src/source/outgoing/view_parser/021.php +++ b/user_guide_src/source/outgoing/view_parser/021.php @@ -15,4 +15,5 @@ $data = [ 'menuitems' => $temp, ]; -echo $parser->setData($data)->renderString($template2); + +return $parser->setData($data)->renderString($template2); diff --git a/user_guide_src/source/outgoing/view_parser/022.php b/user_guide_src/source/outgoing/view_parser/022.php index 022450bb6fd6..1bdb256d92f1 100644 --- a/user_guide_src/source/outgoing/view_parser/022.php +++ b/user_guide_src/source/outgoing/view_parser/022.php @@ -1,3 +1,3 @@ render('myview'); +return $parser->render('myview'); diff --git a/user_guide_src/source/outgoing/view_parser/023.php b/user_guide_src/source/outgoing/view_parser/023.php index 022450bb6fd6..1bdb256d92f1 100644 --- a/user_guide_src/source/outgoing/view_parser/023.php +++ b/user_guide_src/source/outgoing/view_parser/023.php @@ -1,3 +1,3 @@ render('myview'); +return $parser->render('myview'); diff --git a/user_guide_src/source/outgoing/views.rst b/user_guide_src/source/outgoing/views.rst index 55da9ed98a22..56f736f23545 100644 --- a/user_guide_src/source/outgoing/views.rst +++ b/user_guide_src/source/outgoing/views.rst @@ -51,9 +51,6 @@ If you visit your site using the URL you did earlier you should see your new vie example.com/index.php/blog/ -.. note:: While all of the examples show echo the view directly, you can also return the output from the view, instead, - and it will be appended to any captured output. - Loading Multiple Views ====================== diff --git a/user_guide_src/source/outgoing/views/001.php b/user_guide_src/source/outgoing/views/001.php index 90c817235bf5..c1b1ff23a6dd 100644 --- a/user_guide_src/source/outgoing/views/001.php +++ b/user_guide_src/source/outgoing/views/001.php @@ -1,3 +1,3 @@ 'Your title', ]; - echo view('header'); - echo view('menu'); - echo view('content', $data); - echo view('footer'); + return view('header') + . view('menu') + . view('content', $data) + . view('footer'); } } diff --git a/user_guide_src/source/outgoing/views/004.php b/user_guide_src/source/outgoing/views/004.php index d77e328657f3..7c665c263391 100644 --- a/user_guide_src/source/outgoing/views/004.php +++ b/user_guide_src/source/outgoing/views/004.php @@ -1,3 +1,3 @@ 60]); +return view('file_name', $data, ['cache' => 60]); diff --git a/user_guide_src/source/outgoing/views/007.php b/user_guide_src/source/outgoing/views/007.php index b1d22eb55260..1b020abbc5a5 100644 --- a/user_guide_src/source/outgoing/views/007.php +++ b/user_guide_src/source/outgoing/views/007.php @@ -1,4 +1,4 @@ 60, 'cache_name' => 'my_cached_view']); +return view('file_name', $data, ['cache' => 60, 'cache_name' => 'my_cached_view']); diff --git a/user_guide_src/source/outgoing/views/008.php b/user_guide_src/source/outgoing/views/008.php index c9b2ab2f5bbd..8f3218c7029b 100644 --- a/user_guide_src/source/outgoing/views/008.php +++ b/user_guide_src/source/outgoing/views/008.php @@ -6,4 +6,4 @@ 'message' => 'My Message', ]; -echo view('blog_view', $data); +return view('blog_view', $data); diff --git a/user_guide_src/source/outgoing/views/009.php b/user_guide_src/source/outgoing/views/009.php index 61963a40bbb2..008d1cf85d26 100644 --- a/user_guide_src/source/outgoing/views/009.php +++ b/user_guide_src/source/outgoing/views/009.php @@ -11,6 +11,6 @@ public function index() $data['title'] = 'My Real Title'; $data['heading'] = 'My Real Heading'; - echo view('blog_view', $data); + return view('blog_view', $data); } } diff --git a/user_guide_src/source/outgoing/views/010.php b/user_guide_src/source/outgoing/views/010.php index c7dda333df20..189b5dc9f190 100644 --- a/user_guide_src/source/outgoing/views/010.php +++ b/user_guide_src/source/outgoing/views/010.php @@ -6,4 +6,4 @@ 'message' => 'My Message', ]; -echo view('blog_view', $data, ['saveData' => true]); +return view('blog_view', $data, ['saveData' => true]); diff --git a/user_guide_src/source/outgoing/views/011.php b/user_guide_src/source/outgoing/views/011.php index e3ddaeba2075..bb1a5fc21725 100644 --- a/user_guide_src/source/outgoing/views/011.php +++ b/user_guide_src/source/outgoing/views/011.php @@ -14,6 +14,6 @@ public function index() 'heading' => 'My Real Heading', ]; - echo view('blog_view', $data); + return view('blog_view', $data); } } From 76beb9d37b5f78ebcdc000ba00c9444a09cea72b Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 09:55:59 +0900 Subject: [PATCH 0067/8282] docs: fix header level --- user_guide_src/source/concepts/services.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index 84860e65f5c3..ea9b9712fd39 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -40,7 +40,7 @@ error-resistant. .. note:: It is recommended to only create services within controllers. Other files, like models and libraries should have the dependencies either passed into the constructor or through a setter method. Convenience Functions ---------------------- +===================== Two functions have been provided for getting a service. These functions are always available. @@ -110,7 +110,7 @@ within the class, and, if not, creates a new one. All of the factory methods pro .. literalinclude:: services/010.php Service Discovery ------------------ +================= CodeIgniter can automatically discover any Config\\Services.php files you may have created within any defined namespaces. This allows simple use of any module Services files. In order for custom Services files to be discovered, they must From 1944a69dad76ca7bfc925640ae664fcef396834f Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 09:56:57 +0900 Subject: [PATCH 0068/8282] docs: use full classname in sample code Relative classname might not work. --- user_guide_src/source/concepts/services/012.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/services/012.php b/user_guide_src/source/concepts/services/012.php index cc4b63b4e20c..bd3c89f971ec 100644 --- a/user_guide_src/source/concepts/services/012.php +++ b/user_guide_src/source/concepts/services/012.php @@ -1,3 +1,3 @@ Date: Mon, 4 Apr 2022 09:58:24 +0900 Subject: [PATCH 0069/8282] docs: add / at the end of the path The default value ends with /. --- user_guide_src/source/concepts/services/009.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/services/009.php b/user_guide_src/source/concepts/services/009.php index 1bdf73954e16..78e4d7c74caf 100644 --- a/user_guide_src/source/concepts/services/009.php +++ b/user_guide_src/source/concepts/services/009.php @@ -1,3 +1,3 @@ Date: Mon, 4 Apr 2022 10:00:13 +0900 Subject: [PATCH 0070/8282] docs: fix text decoration, etc. --- user_guide_src/source/concepts/services.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index ea9b9712fd39..b5678c024bc8 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -75,7 +75,7 @@ create a new class that implements the ``RouterCollectionInterface``: .. literalinclude:: services/006.php -Finally, modify **/app/Config/Services.php** to create a new instance of ``MyRouter`` +Finally, modify **app/Config/Services.php** to create a new instance of ``MyRouter`` instead of ``CodeIgniter\Router\RouterCollection``: .. literalinclude:: services/007.php @@ -87,7 +87,7 @@ In some instances, you will want the option to pass a setting to the class durin Since the services file is a very simple class, it is easy to make this work. A good example is the ``renderer`` service. By default, we want this class to be able -to find the views at ``APPPATH.views/``. We want the developer to have the option of +to find the views at ``APPPATH . views/``. We want the developer to have the option of changing that path, though, if their needs require it. So the class accepts the ``$viewPath`` as a constructor parameter. The service method looks like this: @@ -112,19 +112,19 @@ within the class, and, if not, creates a new one. All of the factory methods pro Service Discovery ================= -CodeIgniter can automatically discover any Config\\Services.php files you may have created within any defined namespaces. +CodeIgniter can automatically discover any **Config/Services.php** files you may have created within any defined namespaces. This allows simple use of any module Services files. In order for custom Services files to be discovered, they must meet these requirements: -- Its namespace must be defined in ``Config\Autoload.php`` -- Inside the namespace, the file must be found at ``Config\Services.php`` +- Its namespace must be defined in **app/Config/Autoload.php** +- Inside the namespace, the file must be found at **Config/Services.php** - It must extend ``CodeIgniter\Config\BaseService`` A small example should clarify this. -Imagine that you've created a new directory, ``Blog`` in your root directory. This will hold a **blog module** with controllers, +Imagine that you've created a new directory, **Blog** in your project root directory. This will hold a **Blog module** with controllers, models, etc, and you'd like to make some of the classes available as a service. The first step is to create a new file: -``Blog\Config\Services.php``. The skeleton of the file should be: +**Blog/Config/Services.php**. The skeleton of the file should be: .. literalinclude:: services/011.php From 31720766b8049ae7dee5471911334338fc81f7d5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 10:43:28 +0900 Subject: [PATCH 0071/8282] docs: add headings --- user_guide_src/source/concepts/services.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index b5678c024bc8..3539c16c7420 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -44,6 +44,9 @@ Convenience Functions Two functions have been provided for getting a service. These functions are always available. +service() +--------- + The first is ``service()`` which returns a new instance of the requested service. The only required parameter is the service name. This is the same as the method name within the Services file always returns a SHARED instance of the class, so calling the function multiple times should @@ -55,6 +58,9 @@ If the creation method requires additional parameters, they can be passed after .. literalinclude:: services/004.php +single_service() +---------------- + The second function, ``single_service()`` works just like ``service()`` but returns a new instance of the class: From f00e952e346b47d827ecab149b70daf4b3e03e45 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 10:52:07 +0900 Subject: [PATCH 0072/8282] docs: add sample code --- user_guide_src/source/concepts/services/003.php | 3 +++ user_guide_src/source/concepts/services/004.php | 3 +++ user_guide_src/source/concepts/services/005.php | 3 +++ 3 files changed, 9 insertions(+) diff --git a/user_guide_src/source/concepts/services/003.php b/user_guide_src/source/concepts/services/003.php index ebbd1d89c30d..a13e8ebf1696 100644 --- a/user_guide_src/source/concepts/services/003.php +++ b/user_guide_src/source/concepts/services/003.php @@ -1,3 +1,6 @@ Date: Mon, 4 Apr 2022 11:08:03 +0900 Subject: [PATCH 0073/8282] docs: add What is Services? and Why use Services? --- user_guide_src/source/concepts/services.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index 3539c16c7420..b5747e4a913a 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -9,10 +9,19 @@ Services Introduction ============ +What is Services? +----------------- + +The **Services** in CodeIgniter 4 provide the functionality to create and share new class instances. +It is implemented as the ``Config\Services`` class. + All of the core classes within CodeIgniter are provided as "services". This simply means that, instead of hard-coding a class name to load, the classes to call are defined within a very simple configuration file. This file acts as a type of factory to create new instances of the required class. +Why use Services? +----------------- + A quick example will probably make things clearer, so imagine that you need to pull in an instance of the Timer class. The simplest method would simply be to create a new instance of that class: From 11e97ad12b8cd3c0181c90d1d9682d8b35f87b25 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 11:10:44 +0900 Subject: [PATCH 0074/8282] docs: fix heading marks --- user_guide_src/source/concepts/services.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index b5747e4a913a..2ede82c668ac 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -7,10 +7,10 @@ Services :depth: 2 Introduction -============ +************ What is Services? ------------------ +================= The **Services** in CodeIgniter 4 provide the functionality to create and share new class instances. It is implemented as the ``Config\Services`` class. @@ -20,7 +20,7 @@ of hard-coding a class name to load, the classes to call are defined within a ve configuration file. This file acts as a type of factory to create new instances of the required class. Why use Services? ------------------ +================= A quick example will probably make things clearer, so imagine that you need to pull in an instance of the Timer class. The simplest method would simply be to create a new instance of that class: @@ -49,12 +49,12 @@ error-resistant. .. note:: It is recommended to only create services within controllers. Other files, like models and libraries should have the dependencies either passed into the constructor or through a setter method. Convenience Functions -===================== +********************* Two functions have been provided for getting a service. These functions are always available. service() ---------- +========= The first is ``service()`` which returns a new instance of the requested service. The only required parameter is the service name. This is the same as the method name within the Services @@ -68,7 +68,7 @@ If the creation method requires additional parameters, they can be passed after .. literalinclude:: services/004.php single_service() ----------------- +================ The second function, ``single_service()`` works just like ``service()`` but returns a new instance of the class: @@ -76,7 +76,7 @@ the class: .. literalinclude:: services/005.php Defining Services -================= +***************** To make services work well, you have to be able to rely on each class having a constant API, or `interface `_, to use. Almost all of @@ -96,7 +96,7 @@ instead of ``CodeIgniter\Router\RouterCollection``: .. literalinclude:: services/007.php Allowing Parameters -------------------- +=================== In some instances, you will want the option to pass a setting to the class during instantiation. Since the services file is a very simple class, it is easy to make this work. @@ -114,7 +114,7 @@ the path it uses: .. literalinclude:: services/009.php Shared Classes ------------------ +============== There are occasions where you need to require that only a single instance of a service is created. This is easily handled with the ``getSharedInstance()`` method that is called from within the @@ -125,7 +125,7 @@ within the class, and, if not, creates a new one. All of the factory methods pro .. literalinclude:: services/010.php Service Discovery -================= +***************** CodeIgniter can automatically discover any **Config/Services.php** files you may have created within any defined namespaces. This allows simple use of any module Services files. In order for custom Services files to be discovered, they must From 5fef49fe2ed5cf444e8d2c9dcb6ee0b2391a1db4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 11:26:26 +0900 Subject: [PATCH 0075/8282] docs: add How to Get a Service --- user_guide_src/source/concepts/services.rst | 19 ++++++++++++++++--- .../source/concepts/services/013.php | 3 +++ .../source/concepts/services/014.php | 3 +++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 user_guide_src/source/concepts/services/013.php create mode 100644 user_guide_src/source/concepts/services/014.php diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index 2ede82c668ac..b69a4e4c4f85 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -48,13 +48,26 @@ error-resistant. .. note:: It is recommended to only create services within controllers. Other files, like models and libraries should have the dependencies either passed into the constructor or through a setter method. +How to Get a Service +******************** + +As many CodeIgniter classes are provided as services, you can get them like the following: + +.. literalinclude:: services/013.php + +The ``$typography`` is an instance of the Typography class, and if you call ``\Config\Services::typography()`` again, you will get the exactly same 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 + Convenience Functions -********************* +===================== Two functions have been provided for getting a service. These functions are always available. service() -========= +--------- The first is ``service()`` which returns a new instance of the requested service. The only required parameter is the service name. This is the same as the method name within the Services @@ -68,7 +81,7 @@ If the creation method requires additional parameters, they can be passed after .. literalinclude:: services/004.php single_service() -================ +---------------- The second function, ``single_service()`` works just like ``service()`` but returns a new instance of the class: diff --git a/user_guide_src/source/concepts/services/013.php b/user_guide_src/source/concepts/services/013.php new file mode 100644 index 000000000000..0c82ed0e95fa --- /dev/null +++ b/user_guide_src/source/concepts/services/013.php @@ -0,0 +1,3 @@ + Date: Mon, 4 Apr 2022 12:36:06 +0900 Subject: [PATCH 0076/8282] docs: add Convenience Functions --- user_guide_src/source/concepts/factories.rst | 19 +++++++++++++++++++ .../source/concepts/factories/008.php | 6 ++++++ .../source/concepts/factories/009.php | 6 ++++++ 3 files changed, 31 insertions(+) create mode 100644 user_guide_src/source/concepts/factories/008.php create mode 100644 user_guide_src/source/concepts/factories/009.php diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 2ddab73f2216..05dd7559da03 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -35,6 +35,25 @@ you get back the instance as before: .. literalinclude:: factories/003.php +Convenience Functions +===================== + +Two functions for short cut of the Factories have been provided. These functions are always available. + +config() +-------- + +The first is ``config()`` which returns a new instance of a Config class. The only required parameter is the class name: + +.. literalinclude:: factories/008.php + +model() +-------- + +The second function, ``model()`` returns a new instance of a Model class. The only required parameter is the class name: + +.. literalinclude:: factories/009.php + Factory Parameters ================== diff --git a/user_guide_src/source/concepts/factories/008.php b/user_guide_src/source/concepts/factories/008.php new file mode 100644 index 000000000000..866daa36acb2 --- /dev/null +++ b/user_guide_src/source/concepts/factories/008.php @@ -0,0 +1,6 @@ + Date: Mon, 4 Apr 2022 12:37:48 +0900 Subject: [PATCH 0077/8282] docs: add What is Factories? --- user_guide_src/source/concepts/factories.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 05dd7559da03..f04317174273 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -9,8 +9,13 @@ Factories Introduction ============ -Like ``Services``, ``Factories`` are an extension of autoloading that helps keep your code -concise yet optimal, without having to pass around object instances between classes. At its +What is Factories? +------------------ + +Like :doc:`./services`, **Factories** are an extension of autoloading that helps keep your code +concise yet optimal, without having to pass around object instances between classes. + +At its simplest, Factories provide a common way to create a class instance and access it from anywhere. This is a great way to reuse object states and reduce memory load from keeping multiple instances loaded across your app. From c0884be3ff9b983318a2f9262cc3828f860f69b4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 13:44:20 +0900 Subject: [PATCH 0078/8282] docs: add explanation about Differences from Services and preferApp behavior --- user_guide_src/source/concepts/factories.rst | 46 +++++++++++++++---- .../source/concepts/factories/002.php | 2 +- .../source/concepts/factories/003.php | 2 +- .../source/concepts/factories/010.php | 3 ++ 4 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 user_guide_src/source/concepts/factories/010.php diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index f04317174273..2e23e448a8f9 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -20,14 +20,32 @@ simplest, Factories provide a common way to create a class instance and access i anywhere. This is a great way to reuse object states and reduce memory load from keeping multiple instances loaded across your app. -Anything can be loaded by Factories, but the best examples are those classes that are used +Any class can be loaded by Factories, but the best examples are those classes that are used to work on or transmit common data. The framework itself uses Factories internally, e.g., to make sure the correct configuration is loaded when using the ``Config`` class. -Take a look at ``Models`` as an example. You can access the Factory specific to ``Models`` -by using the magic static method of the Factories class, ``Factories::models()``. Because of -the common path structure for namespaces and folders, Factories know that the model files -and classes are found within **Models**, so you can request a model by its shorthand base name: +Differences from Services +------------------------- + +Factories require a concrete class name to instantiate, and do not have code to create instances. + +So Factories are not good for creating complex instance that needs many dependencies, +and cannot change the class of the instance to be returned. + +On the other hand, Services have code to create instances, so it can create a complex instance +that needs other services or class instances. When you get a service, Services require a service name, +not a class name, so the returned instance can be changed without changing the client code. + +Example +------- + +Take a look at **Models** as an example. You can access the Factory specific to Models +by using the magic static method of the Factories class, ``Factories::models()``. + +By default, Factories first searches in the ``App`` namespace for the path corresponding to the magic static method name. +``Factories::models()`` searches the path **Models/**. + +In the following code, if you have ``App\Models\UserModel``, the instance will be returned: .. literalinclude:: factories/001.php @@ -35,7 +53,17 @@ Or you could also request a specific class: .. literalinclude:: factories/002.php -Next time you ask for the same class anywhere in your code, ``Factories`` will be sure +If you have only ``Blog\Models\UserModel``, the instance will be returned. +But if you have both ``App\Models\UserModel`` and ``Blog\Models\UserModel``, +The instance of ``App\Models\UserModel`` will be returned. + +If you want to get ``Blog\Models\UserModel``, you need the option ``preferApp``: + +.. literalinclude:: factories/010.php + +See :ref:`factories-options` for the details. + +Next time you ask for the same class anywhere in your code, Factories will be sure you get back the instance as before: .. literalinclude:: factories/003.php @@ -112,13 +140,13 @@ Configurations To set default component options, create a new Config files at **app/Config/Factory.php** that supplies options as an array property that matches the name of the component. For example, -if you wanted to ensure that all Filters used by your app were valid framework instances, -your **Factory.php** file might look like this: +if you wanted to ensure that all **Filters** used by your app were valid framework instances, +your **app/Config/Factory.php** file might look like this: .. literalinclude:: factories/005.php This would prevent conflict of an unrelated third-party module which happened to have an -unrelated "Filters" path in its namespace. +unrelated ``Filters`` path in its namespace. setOptions Method ----------------- diff --git a/user_guide_src/source/concepts/factories/002.php b/user_guide_src/source/concepts/factories/002.php index cbc19d8b2730..6a56bbe7a374 100644 --- a/user_guide_src/source/concepts/factories/002.php +++ b/user_guide_src/source/concepts/factories/002.php @@ -1,3 +1,3 @@ false]); From 221d484dd012db99371b0d7819bded4dc043bccc Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 09:23:08 +0900 Subject: [PATCH 0079/8282] docs: fix Parser saveData description The default value for $saveData is null, and Config\View::$saveData (true by default) will be set. --- user_guide_src/source/outgoing/view_parser.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/outgoing/view_parser.rst b/user_guide_src/source/outgoing/view_parser.rst index c0705e7fa257..3cefd845de95 100644 --- a/user_guide_src/source/outgoing/view_parser.rst +++ b/user_guide_src/source/outgoing/view_parser.rst @@ -106,7 +106,7 @@ Several options can be passed to the ``render()`` or ``renderString()`` methods. - ``cache_name`` - the ID used to save/retrieve a cached view result; defaults to the viewpath; ignored for renderString() - ``saveData`` - true if the view data parameters should be retained for subsequent calls; - default is **false** + default is **true** - ``cascadeData`` - true if pseudo-variable settings should be passed on to nested substitutions; default is **true** @@ -578,7 +578,7 @@ Class Reference .. php:class:: CodeIgniter\\View\\Parser - .. php:method:: render($view[, $options[, $saveData = false]]) + .. php:method:: render($view[, $options[, $saveData]]) :param string $view: File name of the view source :param array $options: Array of options, as key/value pairs @@ -600,7 +600,7 @@ Class Reference Any conditional substitutions are performed first, then remaining substitutions are performed for each data pair. - .. php:method:: renderString($template[, $options[, $saveData = false]]) + .. php:method:: renderString($template[, $options[, $saveData]]) :param string $template: View source provided as a string :param array $options: Array of options, as key/value pairs From b95708ed0600e4eddff4dd849869594931930479 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 09:28:30 +0900 Subject: [PATCH 0080/8282] docs: fix View saveData description The default is true. --- user_guide_src/source/outgoing/views.rst | 17 +++++++++++------ user_guide_src/source/outgoing/views/010.php | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/user_guide_src/source/outgoing/views.rst b/user_guide_src/source/outgoing/views.rst index 56f736f23545..903f1fef4727 100644 --- a/user_guide_src/source/outgoing/views.rst +++ b/user_guide_src/source/outgoing/views.rst @@ -123,15 +123,20 @@ Now open your view file and change the text to variables that correspond to the Then load the page at the URL you've been using and you should see the variables replaced. -The data passed in is only available during one call to `view`. If you call the function multiple times -in a single request, you will have to pass the desired data to each view. This keeps any data from "bleeding" into -other views, potentially causing issues. If you would prefer the data to persist, you can pass the `saveData` option -into the `$option` array in the third parameter. +The saveData Option +------------------- + +The data passed in is retained for subsequent calls to ``view()``. If you call the function multiple times +in a single request, you will not have to pass the desired data to each ``view()``. + +But this might not keep any data from "bleeding" into +other views, potentially causing issues. If you would prefer to clean the data after one call, you can pass the ``saveData`` option +into the ``$option`` array in the third parameter. .. literalinclude:: views/010.php -Additionally, if you would like the default functionality of the view function to be that it does save the data -between calls, you can set ``$saveData`` to **true** in **app/Config/Views.php**. +Additionally, if you would like the default functionality of the view function to be that it does clear the data +between calls, you can set ``$saveData`` to **false** in **app/Config/Views.php**. Creating Loops ============== diff --git a/user_guide_src/source/outgoing/views/010.php b/user_guide_src/source/outgoing/views/010.php index 189b5dc9f190..8411aa134e7b 100644 --- a/user_guide_src/source/outgoing/views/010.php +++ b/user_guide_src/source/outgoing/views/010.php @@ -6,4 +6,4 @@ 'message' => 'My Message', ]; -return view('blog_view', $data, ['saveData' => true]); +return view('blog_view', $data, ['saveData' => false]); From 510810c2b27a1a2adcb9f21d05fbdf057dce4a86 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 09:31:00 +0900 Subject: [PATCH 0081/8282] docs: decorate view() function --- user_guide_src/source/outgoing/views.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/outgoing/views.rst b/user_guide_src/source/outgoing/views.rst index 903f1fef4727..3eb80427fc2a 100644 --- a/user_guide_src/source/outgoing/views.rst +++ b/user_guide_src/source/outgoing/views.rst @@ -101,7 +101,7 @@ along ``cache_name`` and the cache ID you wish to use: Adding Dynamic Data to the View =============================== -Data is passed from the controller to the view by way of an array in the second parameter of the view function. +Data is passed from the controller to the view by way of an array in the second parameter of the ``view()`` function. Here's an example: .. literalinclude:: views/008.php @@ -135,7 +135,7 @@ into the ``$option`` array in the third parameter. .. literalinclude:: views/010.php -Additionally, if you would like the default functionality of the view function to be that it does clear the data +Additionally, if you would like the default functionality of the ``view()`` function to be that it does clear the data between calls, you can set ``$saveData`` to **false** in **app/Config/Views.php**. Creating Loops From 926fc6eeabdbc9553869b7b7fba466a1c15b41b7 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Apr 2022 20:25:14 +0900 Subject: [PATCH 0082/8282] fix: Publisher::discover() may loads incorrect classname --- system/Publisher/Publisher.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Publisher/Publisher.php b/system/Publisher/Publisher.php index 5cd392d921a8..01a3c62db657 100644 --- a/system/Publisher/Publisher.php +++ b/system/Publisher/Publisher.php @@ -111,9 +111,9 @@ final public static function discover(string $directory = 'Publishers'): array // Loop over each file checking to see if it is a Publisher foreach (array_unique($files) as $file) { - $className = $locator->findQualifiedNameFromPath($file); + $className = $locator->getClassname($file); - if (is_string($className) && class_exists($className) && is_a($className, self::class, true)) { + if ($className !== '' && class_exists($className) && is_a($className, self::class, true)) { self::$discovered[$directory][] = new $className(); } } From 74fb4fb8788bd419016e8bccc53f1825814a721e Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 5 Apr 2022 10:41:27 +0900 Subject: [PATCH 0083/8282] refactor: extract withErrors() method --- system/HTTP/RedirectResponse.php | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/system/HTTP/RedirectResponse.php b/system/HTTP/RedirectResponse.php index 54e1ae12f36d..b9cabbba6793 100644 --- a/system/HTTP/RedirectResponse.php +++ b/system/HTTP/RedirectResponse.php @@ -85,18 +85,35 @@ public function back(?int $code = null, string $method = 'auto') public function withInput() { $session = Services::session(); - $session->setFlashdata('_ci_old_input', [ 'get' => $_GET ?? [], 'post' => $_POST ?? [], ]); - // If the validation has any errors, transmit those back - // so they can be displayed when the validation is handled - // within a method different than displaying the form. + // @TODO Remove this in the future. + // See https://github.com/codeigniter4/CodeIgniter4/issues/5839#issuecomment-1086624600 + $this->withErrors(); + + return $this; + } + + /** + * Set validation errors in the session. + * + * If the validation has any errors, transmit those back + * so they can be displayed when the validation is handled + * within a method different than displaying the form. + * + * @TODO Make this method public when removing $this->withErrors() in withInput(). + * + * @return $this + */ + private function withErrors(): self + { $validation = Services::validation(); if ($validation->getErrors()) { + $session = Services::session(); $session->setFlashdata('_ci_validation_errors', serialize($validation->getErrors())); } From e4f1d8817af17b3106e6ea3fbe3a1e57870813c6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 5 Apr 2022 11:10:01 +0900 Subject: [PATCH 0084/8282] fix: validation errors in model are not cleared when running validation again --- phpstan-baseline.neon.dist | 5 ----- system/BaseModel.php | 4 +++- tests/system/Models/ValidationModelTest.php | 23 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/phpstan-baseline.neon.dist b/phpstan-baseline.neon.dist index 1021eb445899..53d3710b7ea7 100644 --- a/phpstan-baseline.neon.dist +++ b/phpstan-baseline.neon.dist @@ -25,11 +25,6 @@ parameters: count: 1 path: system/Autoloader/Autoloader.php - - - message: "#^Method CodeIgniter\\\\Validation\\\\ValidationInterface\\:\\:run\\(\\) invoked with 3 parameters, 0\\-2 required\\.$#" - count: 1 - path: system/BaseModel.php - - message: "#^Property Config\\\\Cache\\:\\:\\$backupHandler \\(string\\) in isset\\(\\) is not nullable\\.$#" count: 1 diff --git a/system/BaseModel.php b/system/BaseModel.php index 64753be5b3e3..39bd9ce2cb4c 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -1344,7 +1344,9 @@ public function validate($data): bool return true; } - return $this->validation->setRules($rules, $this->validationMessages)->run($data, null, $this->DBGroup); + $this->validation->reset()->setRules($rules, $this->validationMessages); + + return $this->validation->run($data, null, $this->DBGroup); } /** diff --git a/tests/system/Models/ValidationModelTest.php b/tests/system/Models/ValidationModelTest.php index 0d60d36524ac..e7cc4a8d11e5 100644 --- a/tests/system/Models/ValidationModelTest.php +++ b/tests/system/Models/ValidationModelTest.php @@ -55,6 +55,29 @@ public function testValidationBasics(): void $this->assertSame('You forgot to name the baby.', $errors['name']); } + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/5859 + */ + public function testValidationTwice(): void + { + $data = [ + 'name' => null, + 'description' => 'some great marketing stuff', + ]; + + $this->assertFalse($this->model->insert($data)); + + $errors = $this->model->errors(); + $this->assertSame('You forgot to name the baby.', $errors['name']); + + $data = [ + 'name' => 'some name', + 'description' => 'some great marketing stuff', + ]; + + $this->assertIsInt($this->model->insert($data)); + } + public function testValidationWithSetValidationRule(): void { $data = [ From 8f1ed45f1bb3d2df34665e80767d171bd4cef167 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 5 Apr 2022 11:42:31 +0900 Subject: [PATCH 0085/8282] feat: add $includeDir option to get_filenames() --- system/Helpers/filesystem_helper.php | 23 ++++++++++++------- tests/system/Helpers/FilesystemHelperTest.php | 16 +++++++++++++ .../source/helpers/filesystem_helper.rst | 7 +++--- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 89d4d507f961..4918e87e7bf2 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -194,9 +194,14 @@ function delete_files(string $path, bool $delDir = false, bool $htdocs = false, * @param string $sourceDir Path to source * @param bool|null $includePath Whether to include the path as part of the filename; false for no path, null for a relative path, true for full path * @param bool $hidden Whether to include hidden files (files beginning with a period) + * @param bool $includeDir Whether to include directories */ - function get_filenames(string $sourceDir, ?bool $includePath = false, bool $hidden = false): array - { + function get_filenames( + string $sourceDir, + ?bool $includePath = false, + bool $hidden = false, + bool $includeDir = true + ): array { $files = []; $sourceDir = realpath($sourceDir) ?: $sourceDir; @@ -212,12 +217,14 @@ function get_filenames(string $sourceDir, ?bool $includePath = false, bool $hidd continue; } - if ($includePath === false) { - $files[] = $basename; - } elseif ($includePath === null) { - $files[] = str_replace($sourceDir, '', $name); - } else { - $files[] = $name; + if ($includeDir || ! $object->isDir()) { + if ($includePath === false) { + $files[] = $basename; + } elseif ($includePath === null) { + $files[] = str_replace($sourceDir, '', $name); + } else { + $files[] = $name; + } } } } catch (Throwable $e) { diff --git a/tests/system/Helpers/FilesystemHelperTest.php b/tests/system/Helpers/FilesystemHelperTest.php index d20d93481e39..c9f5789a9d4c 100644 --- a/tests/system/Helpers/FilesystemHelperTest.php +++ b/tests/system/Helpers/FilesystemHelperTest.php @@ -305,6 +305,22 @@ public function testGetFilenames() $this->assertSame($expected, get_filenames($vfs->url(), false)); } + public function testGetFilenamesWithoutDirectories() + { + $vfs = vfsStream::setup('root', null, $this->structure); + + $filenames = get_filenames($vfs->url(), true, false, false); + + $expected = [ + 'vfs://root/boo/far', + 'vfs://root/boo/faz', + 'vfs://root/foo/bar', + 'vfs://root/foo/baz', + 'vfs://root/simpleFile', + ]; + $this->assertSame($expected, $filenames); + } + public function testGetFilenamesWithHidden() { $this->assertTrue(function_exists('get_filenames')); diff --git a/user_guide_src/source/helpers/filesystem_helper.rst b/user_guide_src/source/helpers/filesystem_helper.rst index 4cb00fb23188..017ee9a82149 100644 --- a/user_guide_src/source/helpers/filesystem_helper.rst +++ b/user_guide_src/source/helpers/filesystem_helper.rst @@ -150,11 +150,12 @@ The following functions are available: .. note:: The files must be writable or owned by the system in order to be deleted. -.. php:function:: get_filenames($source_dir[, $include_path = false]) +.. php:function:: get_filenames($sourceDir[, $includePath = false[, $hidden = false[, $includeDir = true]]]) - :param string $source_dir: Directory path - :param bool|null $include_path: Whether to include the path as part of the filename; false for no path, null for the path relative to $source_dir, true for the full path + :param string $sourceDir: Directory path + :param bool|null $includePath: Whether to include the path as part of the filename; false for no path, null for the path relative to ``$sourceDir``, true for the full path :param bool $hidden: Whether to include hidden files (files beginning with a period) + :param bool $includeDir: Whether to include directories :returns: An array of file names :rtype: array From 7b442c6a49fb469de6998531433be82cb1ce3345 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 5 Apr 2022 13:00:45 +0900 Subject: [PATCH 0086/8282] docs: fix function reference --- .../source/helpers/filesystem_helper.rst | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/user_guide_src/source/helpers/filesystem_helper.rst b/user_guide_src/source/helpers/filesystem_helper.rst index 4cb00fb23188..e45c7ce31565 100644 --- a/user_guide_src/source/helpers/filesystem_helper.rst +++ b/user_guide_src/source/helpers/filesystem_helper.rst @@ -21,10 +21,10 @@ Available Functions The following functions are available: -.. php:function:: directory_map($source_dir[, $directory_depth = 0[, $hidden = false]]) +.. php:function:: directory_map($sourceDir[, $directoryDepth = 0[, $hidden = false]]) - :param string $source_dir: Path to the source directory - :param int $directory_depth: Depth of directories to traverse (0 = fully recursive, 1 = current dir, etc) + :param string $sourceDir: Path to the source directory + :param int $directoryDepth: Depth of directories to traverse (0 = fully recursive, 1 = current dir, etc) :param bool $hidden: Whether to include hidden paths :returns: An array of files :rtype: array @@ -167,10 +167,10 @@ The following functions are available: .. literalinclude:: filesystem_helper/010.php -.. php:function:: get_dir_file_info($source_dir, $top_level_only) +.. php:function:: get_dir_file_info($sourceDir[, $topLevelOnly = true]) - :param string $source_dir: Directory path - :param bool $top_level_only: Whether to look only at the specified directory (excluding sub-directories) + :param string $sourceDir: Directory path + :param bool $topLevelOnly: Whether to look only at the specified directory (excluding sub-directories) :returns: An array containing info on the supplied directory's contents :rtype: array @@ -183,10 +183,10 @@ The following functions are available: .. literalinclude:: filesystem_helper/011.php -.. php:function:: get_file_info($file[, $returned_values = ['name', 'server_path', 'size', 'date']]) +.. php:function:: get_file_info($file[, $returnedValues = ['name', 'server_path', 'size', 'date']]) :param string $file: File path - :param array|string $returned_values: What type of info to return to be passed as array or comma separated string + :param array|string $returnedValues: What type of info to return to be passed as array or comma separated string :returns: An array containing info on the specified file or false on failure :rtype: array @@ -194,8 +194,8 @@ The following functions are available: information attributes for a file. Second parameter allows you to explicitly declare what information you want returned. - Valid ``$returned_values`` options are: `name`, `size`, `date`, `readable`, `writeable`, - `executable` and `fileperms`. + Valid ``$returnedValues`` options are: ``name``, ``size``, ``date``, ``readable``, ``writeable``, + ``executable`` and ``fileperms``. .. php:function:: symbolic_permissions($perms) @@ -230,10 +230,10 @@ The following functions are available: .. literalinclude:: filesystem_helper/014.php -.. php:function:: set_realpath($path[, $check_existence = false]) +.. php:function:: set_realpath($path[, $checkExistence = false]) :param string $path: Path - :param bool $check_existence: Whether to check if the path actually exists + :param bool $checkExistence: Whether to check if the path actually exists :returns: An absolute path :rtype: string From e19872ad2cc39b6f7142c1f024a7e2cd8f4020f6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 5 Apr 2022 16:52:30 +0900 Subject: [PATCH 0087/8282] docs: remove HackerOne link CI4 does not use HackerOne. --- contributing/bug_report.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/contributing/bug_report.md b/contributing/bug_report.md index c564a9a734f4..43767cdd1766 100644 --- a/contributing/bug_report.md +++ b/contributing/bug_report.md @@ -27,15 +27,7 @@ have found a bug, again - please ask on the forums first. ## Security -Did you find a security issue in CodeIgniter? - -Please *don't* disclose it publicly, but e-mail us at -, or report it via our page on -[HackerOne](https://hackerone.com/codeigniter). - -If you've found a critical vulnerability, we'd be happy to credit you in -our -[ChangeLog](https://codeigniter4.github.io/CodeIgniter4/changelogs/index.html). +See [SECURITY.md](../SECURITY.md). ## Tips for a Good Issue Report From 70af24e461382a2c895a139f87e3c4960a111ba6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 5 Apr 2022 17:21:12 +0900 Subject: [PATCH 0088/8282] chore: rename php-cs-fixer config files The php-cs-fixer configuration files are all in one place and easy to find. --- .github/workflows/test-coding-standards.yml | 4 ++-- .php-cs-fixer.dist.php | 4 ++-- ...r.php-cs-fixer.dist.php => .php-cs-fixer.no-header.php | 2 +- ....php-cs-fixer.dist.php => .php-cs-fixer.user-guide.php | 2 +- admin/pre-commit | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) rename .no-header.php-cs-fixer.dist.php => .php-cs-fixer.no-header.php (95%) rename .user-guide.php-cs-fixer.dist.php => .php-cs-fixer.user-guide.php (95%) diff --git a/.github/workflows/test-coding-standards.yml b/.github/workflows/test-coding-standards.yml index e5b4a2043342..5fa13728b37d 100644 --- a/.github/workflows/test-coding-standards.yml +++ b/.github/workflows/test-coding-standards.yml @@ -52,10 +52,10 @@ jobs: run: composer update --ansi --no-interaction - name: Run lint on `app/`, `admin/`, `public/` - run: vendor/bin/php-cs-fixer fix --verbose --ansi --dry-run --config=.no-header.php-cs-fixer.dist.php --using-cache=no --diff + run: vendor/bin/php-cs-fixer fix --verbose --ansi --dry-run --config=.php-cs-fixer.no-header.php --using-cache=no --diff - name: Run lint on `system/`, `tests`, `utils/`, and root PHP files run: vendor/bin/php-cs-fixer fix --verbose --ansi --dry-run --using-cache=no --diff - name: Run lint on `user_guide_src/source/` - run: vendor/bin/php-cs-fixer fix --verbose --ansi --dry-run --config=.user-guide.php-cs-fixer.dist.php --using-cache=no --diff + run: vendor/bin/php-cs-fixer fix --verbose --ansi --dry-run --config=.php-cs-fixer.user-guide.php --using-cache=no --diff diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 7caeaec1035c..02c6549b2c03 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -29,8 +29,8 @@ ->notName('#Foobar.php$#') ->append([ __FILE__, - __DIR__ . '/.no-header.php-cs-fixer.dist.php', - __DIR__ . '/.user-guide.php-cs-fixer.dist.php', + __DIR__ . '/.php-cs-fixer.no-header.php', + __DIR__ . '/.php-cs-fixer.user-guide.php', __DIR__ . '/rector.php', __DIR__ . '/spark', __DIR__ . '/user_guide_src/renumerate.php', diff --git a/.no-header.php-cs-fixer.dist.php b/.php-cs-fixer.no-header.php similarity index 95% rename from .no-header.php-cs-fixer.dist.php rename to .php-cs-fixer.no-header.php index 153ac5bd1328..6c136cd4b164 100644 --- a/.no-header.php-cs-fixer.dist.php +++ b/.php-cs-fixer.no-header.php @@ -33,7 +33,7 @@ $overrides = []; $options = [ - 'cacheFile' => 'build/.no-header.php-cs-fixer.cache', + 'cacheFile' => 'build/.php-cs-fixer.no-header.cache', 'finder' => $finder, 'customFixers' => FixerGenerator::create('vendor/nexusphp/cs-config/src/Fixer', 'Nexus\\CsConfig\\Fixer'), 'customRules' => [ diff --git a/.user-guide.php-cs-fixer.dist.php b/.php-cs-fixer.user-guide.php similarity index 95% rename from .user-guide.php-cs-fixer.dist.php rename to .php-cs-fixer.user-guide.php index 900c26ec81e1..8081d73698ca 100644 --- a/.user-guide.php-cs-fixer.dist.php +++ b/.php-cs-fixer.user-guide.php @@ -37,7 +37,7 @@ ]; $options = [ - 'cacheFile' => 'build/.user-guide.php-cs-fixer.cache', + 'cacheFile' => 'build/.php-cs-fixer.user-guide.cache', 'finder' => $finder, 'customFixers' => FixerGenerator::create('vendor/nexusphp/cs-config/src/Fixer', 'Nexus\\CsConfig\\Fixer'), 'customRules' => [ diff --git a/admin/pre-commit b/admin/pre-commit index e8fdcff64499..1fa6b15e5984 100644 --- a/admin/pre-commit +++ b/admin/pre-commit @@ -42,9 +42,9 @@ if [ "$FILES" != "" ]; then # Run on whole codebase to skip on unnecessary filtering # Run first on app, admin, public if [ -d /proc/cygdrive ]; then - ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.no-header.php-cs-fixer.dist.php + ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.php-cs-fixer.no-header.php else - php ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.no-header.php-cs-fixer.dist.php + php ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.php-cs-fixer.no-header.php fi if [ $? != 0 ]; then @@ -66,9 +66,9 @@ if [ "$FILES" != "" ]; then # Next, run on user_guide_src/source PHP files if [ -d /proc/cygdrive ]; then - ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.user-guide.php-cs-fixer.dist.php + ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.php-cs-fixer.user-guide.php else - php ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.user-guide.php-cs-fixer.dist.php + php ./vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --config=.php-cs-fixer.user-guide.php fi if [ $? != 0 ]; then From b71ed71595ff7e0828a6ad97a9e6e0e1082f5df1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 08:50:23 +0900 Subject: [PATCH 0089/8282] docs: fix by proofreading Co-authored-by: MGatner --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 2e23e448a8f9..ac9f562732b0 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -57,7 +57,7 @@ If you have only ``Blog\Models\UserModel``, the instance will be returned. But if you have both ``App\Models\UserModel`` and ``Blog\Models\UserModel``, The instance of ``App\Models\UserModel`` will be returned. -If you want to get ``Blog\Models\UserModel``, you need the option ``preferApp``: +If you want to get ``Blog\Models\UserModel``, you need to disable the option ``preferApp``: .. literalinclude:: factories/010.php From b1a77e71cff780d9079beaf3d291bdd715d4e2a9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 08:50:40 +0900 Subject: [PATCH 0090/8282] docs: fix by proofreading Co-authored-by: MGatner --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index ac9f562732b0..ca1025168367 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -71,7 +71,7 @@ you get back the instance as before: Convenience Functions ===================== -Two functions for short cut of the Factories have been provided. These functions are always available. +Two shortcut functions for Factories have been provided. These functions are always available. config() -------- From 12aa1aaf21e651d93f6c102445e866c7fe849585 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 08:51:14 +0900 Subject: [PATCH 0091/8282] docs: fix by proofreading Co-authored-by: MGatner --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index ca1025168367..5f34b4a1bb5a 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -140,7 +140,7 @@ Configurations To set default component options, create a new Config files at **app/Config/Factory.php** that supplies options as an array property that matches the name of the component. For example, -if you wanted to ensure that all **Filters** used by your app were valid framework instances, +if you want to ensure that each ``Filter`` used by your app is an actual framework, your **app/Config/Factory.php** file might look like this: .. literalinclude:: factories/005.php From d41fbe26f89ef1d9df9158aaf305ba9b32fca3d1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 08:53:26 +0900 Subject: [PATCH 0092/8282] docs: remove duplicated "unrelated" --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 5f34b4a1bb5a..600a153d1a8c 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -145,7 +145,7 @@ your **app/Config/Factory.php** file might look like this: .. literalinclude:: factories/005.php -This would prevent conflict of an unrelated third-party module which happened to have an +This would prevent conflict of an third-party module which happened to have an unrelated ``Filters`` path in its namespace. setOptions Method From ac6a97d04c7345223427a7243dd11735672d3dba Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 09:05:57 +0900 Subject: [PATCH 0093/8282] docs: add more explanation for filters component --- user_guide_src/source/concepts/factories.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 600a153d1a8c..ff5ea4faf140 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -139,12 +139,17 @@ Configurations -------------- To set default component options, create a new Config files at **app/Config/Factory.php** -that supplies options as an array property that matches the name of the component. For example, -if you want to ensure that each ``Filter`` used by your app is an actual framework, +that supplies options as an array property that matches the name of the component. + +For example, if you want to create **Filters** by Factories, the component name wll be ``filters``. +And if you want to ensure that each filter is an instance of a class which implements CodeIgniter's ``FilterInterface``, your **app/Config/Factory.php** file might look like this: .. literalinclude:: factories/005.php +Now you can create a filter with code like ``Factories::filters('SomeFilter')``, +and the returned instance will surely be a CodeIgniter's filter. + This would prevent conflict of an third-party module which happened to have an unrelated ``Filters`` path in its namespace. From 6214ece43aaf6207bd3007822cf7ac0c3f6cefe8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 09:45:36 +0900 Subject: [PATCH 0094/8282] docs: revise the description to be more accurate The property $component does not exist. --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index ff5ea4faf140..569fbaa7db1e 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -131,7 +131,7 @@ Factories Behavior Options can be applied in one of three ways (listed in ascending priority): -* A configuration class ``Config\Factory`` with a ``$component`` property. +* A configuration class ``Config\Factory`` with a property that matches the name of a component. * The static method ``Factories::setOptions()``. * Passing options directly at call time with a parameter. From ea781ab2351e1b74bfae71884f90cbf58e3eaa72 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 5 Apr 2022 17:58:37 +0900 Subject: [PATCH 0095/8282] chore: update composer scripts --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 8d77bc48cde8..2a0329252a46 100644 --- a/composer.json +++ b/composer.json @@ -62,14 +62,14 @@ "analyze": "phpstan analyse", "test": "phpunit", "cs": [ - "php-cs-fixer fix --verbose --dry-run --diff --config=.user-guide.php-cs-fixer.dist.php", - "php-cs-fixer fix --verbose --dry-run --diff --config=.no-header.php-cs-fixer.dist.php", - "php-cs-fixer fix --verbose --dry-run --diff" + "php-cs-fixer fix --ansi --verbose --dry-run --diff --config=.php-cs-fixer.user-guide.php", + "php-cs-fixer fix --ansi --verbose --dry-run --diff --config=.php-cs-fixer.no-header.php", + "php-cs-fixer fix --ansi --verbose --dry-run --diff" ], "cs-fix": [ - "php-cs-fixer fix --verbose --diff --config=.user-guide.php-cs-fixer.dist.php", - "php-cs-fixer fix --verbose --diff --config=.no-header.php-cs-fixer.dist.php", - "php-cs-fixer fix --verbose --diff" + "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" ] }, "scripts-descriptions": { From c01667dbe1eff42313cdf115003cd4a65eef2e5b Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 17:28:08 +0900 Subject: [PATCH 0096/8282] docs: fix by proofreading Co-authored-by: John Paul E. Balandan, CPA <51850998+paulbalandan@users.noreply.github.com> --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 569fbaa7db1e..62d40b3352c9 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -9,7 +9,7 @@ Factories Introduction ============ -What is Factories? +What are Factories? ------------------ Like :doc:`./services`, **Factories** are an extension of autoloading that helps keep your code From cd2abe85e60626a26cfc5cbcbfc6517ef15515f7 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 17:29:04 +0900 Subject: [PATCH 0097/8282] docs: fix by proofreading Co-authored-by: John Paul E. Balandan, CPA <51850998+paulbalandan@users.noreply.github.com> --- user_guide_src/source/concepts/factories.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 62d40b3352c9..d33c49e2398b 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -27,10 +27,10 @@ make sure the correct configuration is loaded when using the ``Config`` class. Differences from Services ------------------------- -Factories require a concrete class name to instantiate, and do not have code to create instances. +Factories require a concrete class name to instantiate and do not have code to create instances. -So Factories are not good for creating complex instance that needs many dependencies, -and cannot change the class of the instance to be returned. +So, Factories are not good for creating a complex instance that needs many dependencies, +and you cannot change the class of the instance to be returned. On the other hand, Services have code to create instances, so it can create a complex instance that needs other services or class instances. When you get a service, Services require a service name, From 9dd0e02d0feda051865e99383ab775da6937b762 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 17:29:23 +0900 Subject: [PATCH 0098/8282] docs: fix by proofreading Co-authored-by: John Paul E. Balandan, CPA <51850998+paulbalandan@users.noreply.github.com> --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index d33c49e2398b..138b73c1342e 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -55,7 +55,7 @@ Or you could also request a specific class: If you have only ``Blog\Models\UserModel``, the instance will be returned. But if you have both ``App\Models\UserModel`` and ``Blog\Models\UserModel``, -The instance of ``App\Models\UserModel`` will be returned. +the instance of ``App\Models\UserModel`` will be returned. If you want to get ``Blog\Models\UserModel``, you need to disable the option ``preferApp``: From 1b1d03c6363bb0db9b0a935440d791fce2f54859 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 17:31:59 +0900 Subject: [PATCH 0099/8282] docs: fix by proofreading Co-authored-by: John Paul E. Balandan, CPA <51850998+paulbalandan@users.noreply.github.com> --- user_guide_src/source/concepts/services.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/services.rst b/user_guide_src/source/concepts/services.rst index b69a4e4c4f85..632ad10cedf0 100644 --- a/user_guide_src/source/concepts/services.rst +++ b/user_guide_src/source/concepts/services.rst @@ -115,7 +115,7 @@ In some instances, you will want the option to pass a setting to the class durin Since the services file is a very simple class, it is easy to make this work. A good example is the ``renderer`` service. By default, we want this class to be able -to find the views at ``APPPATH . views/``. We want the developer to have the option of +to find the views at ``APPPATH . 'views/'``. We want the developer to have the option of changing that path, though, if their needs require it. So the class accepts the ``$viewPath`` as a constructor parameter. The service method looks like this: From 606518ac686abfe275a40727f747199c8d921e60 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 17:33:37 +0900 Subject: [PATCH 0100/8282] docs: fix by proofreading Co-authored-by: John Paul E. Balandan, CPA <51850998+paulbalandan@users.noreply.github.com> --- user_guide_src/source/helpers/filesystem_helper.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/helpers/filesystem_helper.rst b/user_guide_src/source/helpers/filesystem_helper.rst index 017ee9a82149..6d7618b9d783 100644 --- a/user_guide_src/source/helpers/filesystem_helper.rst +++ b/user_guide_src/source/helpers/filesystem_helper.rst @@ -155,7 +155,7 @@ The following functions are available: :param string $sourceDir: Directory path :param bool|null $includePath: Whether to include the path as part of the filename; false for no path, null for the path relative to ``$sourceDir``, true for the full path :param bool $hidden: Whether to include hidden files (files beginning with a period) - :param bool $includeDir: Whether to include directories + :param bool $includeDir: Whether to include directories in the array output :returns: An array of file names :rtype: array From 3fe7b8a45a2455a4cb3b2e7a1e2aa47072f883ba Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 17:44:29 +0900 Subject: [PATCH 0101/8282] docs: add changelog --- user_guide_src/source/changelogs/v4.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index 99f980d6b35e..0ed69ab0760a 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -37,6 +37,7 @@ Enhancements - Exception information logged through ``log_message()`` has now improved. It now includes the file and line where the exception originated. It also does not truncate the message anymore. - The log format has also changed. If users are depending on the log format in their apps, the new log format is "<1-based count> (): " - Added support for webp files to **app/Config/Mimes.php**. +- Added 4th parameter ``$includeDir`` to ``get_filenames()``. See :php:func:`get_filenames`. Changes ******* From 9f2d3ced709e1774128b03a9831424d90b26e29f Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 6 Apr 2022 20:23:33 +0900 Subject: [PATCH 0102/8282] docs: fix title underline factories.rst:13: WARNING: Title underline too short. --- user_guide_src/source/concepts/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 138b73c1342e..754f831257e3 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -10,7 +10,7 @@ Introduction ============ What are Factories? ------------------- +------------------- Like :doc:`./services`, **Factories** are an extension of autoloading that helps keep your code concise yet optimal, without having to pass around object instances between classes. From df954fae54dc76873b6a16e883aac791f9f0a48f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Apr 2022 15:05:18 +0000 Subject: [PATCH 0103/8282] chore(deps-dev): update rector/rector requirement Updates the requirements on [rector/rector](https://github.com/rectorphp/rector) to permit the latest version. - [Release notes](https://github.com/rectorphp/rector/releases) - [Commits](https://github.com/rectorphp/rector/compare/0.12.19...0.12.20) --- updated-dependencies: - dependency-name: rector/rector dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8d77bc48cde8..e21ae85ecff3 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "phpstan/phpstan": "^1.0", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1", - "rector/rector": "0.12.19" + "rector/rector": "0.12.20" }, "suggest": { "ext-fileinfo": "Improves mime type detection for files" From 5ee5304c24cc96df3a2d6c3dcf0ca069454da40c Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 7 Apr 2022 08:47:57 +0900 Subject: [PATCH 0104/8282] docs: update `spark migrate:status` output --- user_guide_src/source/dbmgmt/migration.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/dbmgmt/migration.rst b/user_guide_src/source/dbmgmt/migration.rst index cd5787328a96..ff81f4ff0708 100644 --- a/user_guide_src/source/dbmgmt/migration.rst +++ b/user_guide_src/source/dbmgmt/migration.rst @@ -162,8 +162,13 @@ You can use (refresh) with the following options: Displays a list of all migrations and the date and time they ran, or '--' if they have not been run:: > php spark migrate:status - Filename Migrated On - First_migration.php 2016-04-25 04:44:22 + +----------------------+-------------------+-----------------------+---------+---------------------+-------+ + | Namespace | Version | Filename | Group | Migrated On | Batch | + +----------------------+-------------------+-----------------------+---------+---------------------+-------+ + | App | 2022-04-06-234508 | CreateCiSessionsTable | default | 2022-04-06 18:45:14 | 2 | + | CodeIgniter\Settings | 2021-07-04-041948 | CreateSettingsTable | default | 2022-04-06 01:23:08 | 1 | + | CodeIgniter\Settings | 2021-11-14-143905 | AddContextColumn | default | 2022-04-06 01:23:08 | 1 | + +----------------------+-------------------+-----------------------+---------+---------------------+-------+ You can use (status) with the following options: From 180dca40ece4a328892bbdbf050f5d7394cec947 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 7 Apr 2022 08:48:31 +0900 Subject: [PATCH 0105/8282] docs: update deprecated command --- user_guide_src/source/libraries/sessions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst index 59557db92cf8..11ac22f12add 100644 --- a/user_guide_src/source/libraries/sessions.rst +++ b/user_guide_src/source/libraries/sessions.rst @@ -569,10 +569,10 @@ You can choose the Database group to use by adding a new line to the .. literalinclude:: sessions/040.php -If you'd rather not do all of this by hand, you can use the ``session:migration`` command +If you'd rather not do all of this by hand, you can use the ``make:migration --session`` command from the cli to generate a migration file for you:: - > php spark session:migration + > php spark make:migration --session > php spark migrate This command will take the **sessionSavePath** and **sessionMatchIP** settings into account From 7b58b7217eb556d2e91edae5f8b5813abfb16f59 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 7 Apr 2022 13:27:54 +0900 Subject: [PATCH 0106/8282] chore: update gitattributes --- .gitattributes | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitattributes b/.gitattributes index 79c65323dade..de04ad191549 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,17 +14,23 @@ contributing/ export-ignore .editorconfig export-ignore .nojekyll export-ignore export-ignore CODE_OF_CONDUCT.md export-ignore +CONTRIBUTING.md export-ignore PULL_REQUEST_TEMPLATE.md export-ignore stale.yml export-ignore Vagrantfile.dist export-ignore # They don't want our test files +tests/AutoReview/ export-ignore tests/system/ export-ignore utils/ export-ignore +depfile.yaml export-ignore rector.php export-ignore phpunit.xml.dist export-ignore +phpstan-baseline.neon.dist export-ignore phpstan.neon.dist export-ignore .php-cs-fixer.dist.php export-ignore +.php-cs-fixer.no-header.php export-ignore +.php-cs-fixer.user-guide.php export-ignore # The source user guide, either user_guide_src/ export-ignore From 53cdb671b8b4e04b7d4a0752dd3b6046819abb52 Mon Sep 17 00:00:00 2001 From: "Michael R. Krisnadhi" Date: Fri, 8 Apr 2022 16:08:14 +0700 Subject: [PATCH 0107/8282] clarify Models user guide Inform the $useAutoIncrement default value (which is 'true'), so developers would be aware of that option if they don't use auto-incremented INT's on primary keys, especially when triggering an 'afterInsert' event which will eventually cause the $data['id'] value to be 0 instead of the actual primary key (see 'system/Model.php' line 254 : https://github.com/codeigniter4/CodeIgniter4/blob/622d50aa2976b85f7cb1479faec7558cd9ec2341/system/Model.php#L254) --- user_guide_src/source/models/model.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index f55e60656c03..fa354d7c3aff 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -99,7 +99,8 @@ is used with methods like ``find()`` to know what column to match the specified Specifies if the table uses an auto-increment feature for ``$primaryKey``. If set to ``false`` then you are responsible for providing primary key value for every record in the table. This -feature may be handy when we want to implement 1:1 relation or use UUIDs for our model. +feature may be handy when we want to implement 1:1 relation or use UUIDs for our model. The +default value is ``true``. .. note:: If you set ``$useAutoIncrement`` to ``false``, then make sure to set your primary key in the database to ``unique``. This way you will make sure that all of Model's features From 3fc0c6b61bd318f73090cf1cc672ba303c7b09b2 Mon Sep 17 00:00:00 2001 From: Toto Prayogo Date: Fri, 8 Apr 2022 20:34:42 +0700 Subject: [PATCH 0108/8282] add `literalinclude` for curlrequest -> debug --- user_guide_src/source/libraries/curlrequest.rst | 2 +- user_guide_src/source/libraries/curlrequest/034.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 user_guide_src/source/libraries/curlrequest/034.php diff --git a/user_guide_src/source/libraries/curlrequest.rst b/user_guide_src/source/libraries/curlrequest.rst index 3d0b91b511fb..b77d41e6b088 100644 --- a/user_guide_src/source/libraries/curlrequest.rst +++ b/user_guide_src/source/libraries/curlrequest.rst @@ -219,7 +219,7 @@ script execution. This is done by passing CURLOPT_VERBOSE and echoing the output server via ``spark serve`` you will see the output in the console. Otherwise, the output will be written to the server's error log. - $response->request('GET', 'http://example.com', ['debug' => true]); +.. literalinclude:: curlrequest/034.php You can pass a filename as the value for debug to have the output written to a file: diff --git a/user_guide_src/source/libraries/curlrequest/034.php b/user_guide_src/source/libraries/curlrequest/034.php new file mode 100644 index 000000000000..1560cd589fec --- /dev/null +++ b/user_guide_src/source/libraries/curlrequest/034.php @@ -0,0 +1,3 @@ +request('GET', 'http://example.com', ['debug' => true]); From fc8946faf361a6571beccba1b054fd33ecae581a Mon Sep 17 00:00:00 2001 From: Toto Prayogo Date: Fri, 8 Apr 2022 20:44:35 +0700 Subject: [PATCH 0109/8282] fix parameters display for API response > `setResponseFormat($format)` method --- user_guide_src/source/outgoing/api_responses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/api_responses.rst b/user_guide_src/source/outgoing/api_responses.rst index 6d657e6bc765..bdb853563e2b 100644 --- a/user_guide_src/source/outgoing/api_responses.rst +++ b/user_guide_src/source/outgoing/api_responses.rst @@ -60,7 +60,7 @@ Class Reference *************** .. php:method:: setResponseFormat($format) - :param string $format The type of response to return, either ``json`` or ``xml`` + :param string $format: The type of response to return, either ``json`` or ``xml`` This defines the format to be used when formatting arrays in responses. If you provide a ``null`` value for ``$format``, it will be automatically determined through content negotiation. From 5161942e2c0211698ff7242f5df9d8e8aef1e757 Mon Sep 17 00:00:00 2001 From: Toto Prayogo Date: Fri, 8 Apr 2022 21:07:41 +0700 Subject: [PATCH 0110/8282] fix paths --- 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 0681b1b534e6..fc00dcd06985 100644 --- a/user_guide_src/source/general/managing_apps.rst +++ b/user_guide_src/source/general/managing_apps.rst @@ -25,7 +25,7 @@ your main **app/Config/Paths.php** and set a *full server path* in the You will need to modify two additional files in your project root, so that they can find the **Paths** configuration file: -- **/spark** runs command line apps; the path is specified on or about line 35: +- **/spark** runs command line apps; the path is specified on or about line 49: .. literalinclude:: managing_apps/002.php From 571923915b7b431d055407f51c1201a1979f2da9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:03:24 +0000 Subject: [PATCH 0111/8282] chore(deps): bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/deploy-userguide-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-userguide-latest.yml b/.github/workflows/deploy-userguide-latest.yml index fb453f0c9694..8fc77b9f7698 100644 --- a/.github/workflows/deploy-userguide-latest.yml +++ b/.github/workflows/deploy-userguide-latest.yml @@ -29,7 +29,7 @@ jobs: # Create an artifact of the html output - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: HTML Documentation path: user_guide_src/build/html/ From 022563377e7e995fc7b6845e4a8e026d545ee876 Mon Sep 17 00:00:00 2001 From: Toto Prayogo Date: Sat, 9 Apr 2022 03:44:19 +0700 Subject: [PATCH 0112/8282] change `$request->` to `$client->` --- user_guide_src/source/libraries/curlrequest/020.php | 2 +- user_guide_src/source/libraries/curlrequest/021.php | 2 +- user_guide_src/source/libraries/curlrequest/022.php | 2 +- user_guide_src/source/libraries/curlrequest/023.php | 2 +- user_guide_src/source/libraries/curlrequest/030.php | 2 +- user_guide_src/source/libraries/curlrequest/031.php | 2 +- user_guide_src/source/libraries/curlrequest/034.php | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/user_guide_src/source/libraries/curlrequest/020.php b/user_guide_src/source/libraries/curlrequest/020.php index 6e0b1e3726a7..14fad31cdea4 100644 --- a/user_guide_src/source/libraries/curlrequest/020.php +++ b/user_guide_src/source/libraries/curlrequest/020.php @@ -1,3 +1,3 @@ request('GET', 'http://example.com', ['connect_timeout' => 0]); +$client->request('GET', 'http://example.com', ['connect_timeout' => 0]); diff --git a/user_guide_src/source/libraries/curlrequest/021.php b/user_guide_src/source/libraries/curlrequest/021.php index 4acf82aa9e0b..051df32e1a37 100644 --- a/user_guide_src/source/libraries/curlrequest/021.php +++ b/user_guide_src/source/libraries/curlrequest/021.php @@ -1,3 +1,3 @@ request('GET', 'http://example.com', ['cookie' => WRITEPATH . 'CookieSaver.txt']); +$client->request('GET', 'http://example.com', ['cookie' => WRITEPATH . 'CookieSaver.txt']); diff --git a/user_guide_src/source/libraries/curlrequest/022.php b/user_guide_src/source/libraries/curlrequest/022.php index 6acbcf0a7784..69b0e4ed8bec 100644 --- a/user_guide_src/source/libraries/curlrequest/022.php +++ b/user_guide_src/source/libraries/curlrequest/022.php @@ -1,3 +1,3 @@ request('GET', 'http://example.com', ['debug' => '/usr/local/curl_log.txt']); +$client->request('GET', 'http://example.com', ['debug' => '/usr/local/curl_log.txt']); diff --git a/user_guide_src/source/libraries/curlrequest/023.php b/user_guide_src/source/libraries/curlrequest/023.php index b6846257ad83..61e6f6df5214 100644 --- a/user_guide_src/source/libraries/curlrequest/023.php +++ b/user_guide_src/source/libraries/curlrequest/023.php @@ -1,4 +1,4 @@ request('GET', 'http://example.com', ['delay' => 2000]); +$client->request('GET', 'http://example.com', ['delay' => 2000]); diff --git a/user_guide_src/source/libraries/curlrequest/030.php b/user_guide_src/source/libraries/curlrequest/030.php index 83bf718e33e4..16c0c6fd6c49 100644 --- a/user_guide_src/source/libraries/curlrequest/030.php +++ b/user_guide_src/source/libraries/curlrequest/030.php @@ -1,3 +1,3 @@ request('GET', 'http://example.com', ['timeout' => 5]); +$client->request('GET', 'http://example.com', ['timeout' => 5]); diff --git a/user_guide_src/source/libraries/curlrequest/031.php b/user_guide_src/source/libraries/curlrequest/031.php index 1709d6d90b9c..8023c84faa39 100644 --- a/user_guide_src/source/libraries/curlrequest/031.php +++ b/user_guide_src/source/libraries/curlrequest/031.php @@ -1,3 +1,3 @@ request('GET', 'http://example.com', ['user_agent' => 'CodeIgniter Framework v4']); +$client->request('GET', 'http://example.com', ['user_agent' => 'CodeIgniter Framework v4']); diff --git a/user_guide_src/source/libraries/curlrequest/034.php b/user_guide_src/source/libraries/curlrequest/034.php index 1560cd589fec..9e343aa9d9da 100644 --- a/user_guide_src/source/libraries/curlrequest/034.php +++ b/user_guide_src/source/libraries/curlrequest/034.php @@ -1,3 +1,3 @@ request('GET', 'http://example.com', ['debug' => true]); +$client->request('GET', 'http://example.com', ['debug' => true]); From 830750edd49d9fe5219ff2f581afdfec63e93c56 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Apr 2022 09:04:37 +0900 Subject: [PATCH 0113/8282] feat: throws exception when controller name in routes contains `/` --- system/Language/en/Router.php | 1 + system/Router/Exceptions/RouterException.php | 10 ++++++++++ system/Router/Router.php | 5 +++++ tests/system/Router/RouterTest.php | 12 ++++++++++++ 4 files changed, 28 insertions(+) diff --git a/system/Language/en/Router.php b/system/Language/en/Router.php index 90aaa132b489..7d0b5cc99e25 100644 --- a/system/Language/en/Router.php +++ b/system/Language/en/Router.php @@ -14,4 +14,5 @@ 'invalidParameter' => 'A parameter does not match the expected type.', 'missingDefaultRoute' => 'Unable to determine what should be displayed. A default route has not been specified in the routing file.', 'invalidDynamicController' => 'A dynamic controller is not allowed for security reasons. Route handler: {0}', + 'invalidControllerName' => 'The namespace delimiter is a backslash (\), not a slash (/). Route handler: {0}', ]; diff --git a/system/Router/Exceptions/RouterException.php b/system/Router/Exceptions/RouterException.php index b50042b3421b..5a510e4716fe 100644 --- a/system/Router/Exceptions/RouterException.php +++ b/system/Router/Exceptions/RouterException.php @@ -68,4 +68,14 @@ public static function forDynamicController(string $handler) { return new static(lang('Router.invalidDynamicController', [$handler])); } + + /** + * Throw when controller name has `/`. + * + * @return RouterException + */ + public static function forInvalidControllerName(string $handler) + { + return new static(lang('Router.invalidControllerName', [$handler])); + } } diff --git a/system/Router/Router.php b/system/Router/Router.php index 42f2eb0c7a42..37c88114172e 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -426,6 +426,11 @@ protected function checkRoutes(string $uri): bool throw RouterException::forDynamicController($handler); } + // Checks `/` in controller name + if (strpos($controller, '/') !== false) { + throw RouterException::forInvalidControllerName($handler); + } + if (strpos($routeKey, '/') !== false) { $replacekey = str_replace('/(.*)', '', $routeKey); $handler = preg_replace('#^' . $routeKey . '$#u', $handler, $uri); diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index d3ca04422c41..38eb9b7ba48c 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -61,6 +61,7 @@ protected function setUp(): void 'closure/(:num)/(:alpha)' => static fn ($num, $str) => $num . '-' . $str, '{locale}/pages' => 'App\Pages::list_all', 'admin/admins' => 'App\Admin\Admins::list_all', + 'admin/admins/edit/(:any)' => 'App/Admin/Admins::edit_show/$1', '/some/slash' => 'App\Slash::index', 'objects/(:segment)/sort/(:segment)/([A-Z]{3,7})' => 'AdminList::objectsSortCreate/$1/$2/$3', '(:segment)/(:segment)/(:segment)' => '$2::$3/$1', @@ -402,6 +403,17 @@ public function testRouteResource() $this->assertSame('list_all', $router->methodName()); } + public function testRouteWithSlashInControllerName() + { + $this->expectExceptionMessage( + 'The namespace delimiter is a backslash (\), not a slash (/). Route handler: \App/Admin/Admins::edit_show/$1' + ); + + $router = new Router($this->collection, $this->request); + + $router->handle('admin/admins/edit/1'); + } + public function testRouteWithLeadingSlash() { $router = new Router($this->collection, $this->request); From 1640b8f89b069d66d1318768371ab0ed9cb554bf Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Apr 2022 09:29:24 +0900 Subject: [PATCH 0114/8282] docs: fix heading level --- user_guide_src/source/outgoing/api_responses.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/outgoing/api_responses.rst b/user_guide_src/source/outgoing/api_responses.rst index bdb853563e2b..0500a8ee92dd 100644 --- a/user_guide_src/source/outgoing/api_responses.rst +++ b/user_guide_src/source/outgoing/api_responses.rst @@ -56,8 +56,10 @@ So, if your request asks for JSON formatted data in an **Accept** header, the da ``respond*`` or ``fail*`` methods will be formatted by the ``CodeIgniter\Format\JSONFormatter`` class. The resulting JSON data will be sent back to the client. +*************** Class Reference *************** + .. php:method:: setResponseFormat($format) :param string $format: The type of response to return, either ``json`` or ``xml`` From 7c137dc250553cb118435d50a52b03e48beeab65 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Apr 2022 09:46:23 +0900 Subject: [PATCH 0115/8282] docs: add TOC --- user_guide_src/source/installation/upgrade_4xx.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/user_guide_src/source/installation/upgrade_4xx.rst b/user_guide_src/source/installation/upgrade_4xx.rst index 03d72ac160eb..2166dda681a4 100644 --- a/user_guide_src/source/installation/upgrade_4xx.rst +++ b/user_guide_src/source/installation/upgrade_4xx.rst @@ -25,6 +25,10 @@ them one by one. **Do read the user guide** before embarking on a project conversion! +.. contents:: + :local: + :depth: 2 + General Adjustments =================== From fd6c6e5378c9358bd92713ee0d0f9d043e0fffcc Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Apr 2022 09:46:45 +0900 Subject: [PATCH 0116/8282] docs: fix heading underlines --- .../source/installation/upgrade_4xx.rst | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/user_guide_src/source/installation/upgrade_4xx.rst b/user_guide_src/source/installation/upgrade_4xx.rst index 2166dda681a4..a97a2e339a29 100644 --- a/user_guide_src/source/installation/upgrade_4xx.rst +++ b/user_guide_src/source/installation/upgrade_4xx.rst @@ -30,18 +30,21 @@ them one by one. :depth: 2 General Adjustments -=================== +******************* -**Downloads** +Downloads +========= - CI4 is still available as a ready-to-run zip or tarball. - It can also be installed using Composer. -**Namespaces** +Namespaces +========== - CI4 is built for PHP 7.4+, and everything in the framework is namespaced, except for the helpers. -**Application Structure** +Application Structure +===================== - The **application** folder is renamed as **app** and the framework still has **system** folders, with the same interpretation as before. @@ -52,7 +55,8 @@ General Adjustments - There is no longer a nested **application/core** folder, as we have a different mechanism for extending framework components (see below). -**Model, View and Controller** +Model, View and Controller +========================== - CodeIgniter is based on the MVC concept. Thus, the changes on the Model, View and Controller are one of the most important things you have to handle. @@ -75,7 +79,8 @@ General Adjustments upgrade_views upgrade_controllers -**Class loading** +Class Loading +============= - There is no longer a CodeIgniter "superobject", with framework component references magically injected as properties of your controller. @@ -87,27 +92,31 @@ General Adjustments - You can configure the class loading to support whatever application structure you are most comfortable with, including the "HMVC" style. -**Libraries** +Libraries +========= - Your app classes can still go inside **app/Libraries**, but they don't have to. - Instead of CI3's ``$this->load->library(x);`` you can now use ``$this->x = new X();``, following namespaced conventions for your component. -**Helpers** +Helpers +======= - Helpers are pretty much the same as before, though some have been simplified. - In CI4, ``redirect()`` returns a ``RedirectResponse`` instance instead of redirecting and terminating script execution. You must return it. - `redirect() Documentation CodeIgniter 3.X `_ - `redirect() Documentation CodeIgniter 4.X <../general/common_functions.html#redirect>`_ -**Events** +Events +====== - Hooks have been replaced by Events. - Instead of CI3's ``$hook['post_controller_constructor']`` you now use ``Events::on('post_controller_constructor', ['MyClass', 'MyFunction']);``, with the namespace ``CodeIgniter\Events\Events;``. - Events are always enabled, and are available globally. -**Extending the framework** +Extending the Framework +======================= - You don't need a **core** folder to hold ``MY_...`` framework component extensions or replacements. @@ -118,7 +127,7 @@ General Adjustments your components instead of the default ones. Upgrading Libraries -=================== +******************* - Your app classes can still go inside **app/Libraries**, but they don't have to. - Instead of CI3's ``$this->load->library(x);`` you can now use ``$this->x = new X();``, From ab0d86b96726e89ce3a27b87d45190c7ea8b7968 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Apr 2022 10:43:07 +0900 Subject: [PATCH 0117/8282] docs: add link to the detailed page --- user_guide_src/source/changelogs/v4.2.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index 0ed69ab0760a..c0081761ffd4 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -27,7 +27,7 @@ Enhancements - Added the config ``$autoNonce`` in ``Config\ContentSecurityPolicy`` to disable the CSP placeholder replacement - Added the functions ``csp_script_nonce()`` and ``csp_style_nonce()`` to get nonce attributes - See :ref:`content-security-policy` for details. -- New View Decorators allow modifying the generated HTML prior to caching. +- New :doc:`../outgoing/view_decorators` allow modifying the generated HTML prior to caching. - Added Subqueries in the FROM section. See :ref:`query-builder-from-subquery`. - Added Subqueries in the SELECT section. See :ref:`query-builder-select`. - Added Validation Strict Rules. See :ref:`validation-traditional-and-strict-rules`. From 7f595062e2111b39a10a5300eca45cf8076908e8 Mon Sep 17 00:00:00 2001 From: Chl Date: Fri, 8 Apr 2022 23:31:04 +0200 Subject: [PATCH 0118/8282] script_tag: cosmetic for value-less attributes Some attributes are usually written without values, for example : `'; + return $script . 'type="text/javascript">'; } } diff --git a/tests/system/Helpers/HTMLHelperTest.php b/tests/system/Helpers/HTMLHelperTest.php index b3ed47d0c3e6..74b087228fdd 100755 --- a/tests/system/Helpers/HTMLHelperTest.php +++ b/tests/system/Helpers/HTMLHelperTest.php @@ -248,6 +248,27 @@ public function testScriptTagWithIndexpage() $this->assertSame($expected, script_tag($target, true)); } + public function testScriptTagWithSrc() + { + $target = ['src' => 'http://site.com/js/mystyles.js']; + $expected = ''; + $this->assertSame($expected, script_tag($target)); + } + + public function testScriptTagWithSrcWithoutProtocol() + { + $target = ['src' => 'js/mystyles.js']; + $expected = ''; + $this->assertSame($expected, script_tag($target)); + } + + public function testScriptTagWithSrcAndAttributes() + { + $target = ['src' => 'js/mystyles.js', 'charset' => 'UTF-8', 'defer' => '', 'async' => null]; + $expected = ''; + $this->assertSame($expected, script_tag($target)); + } + public function testLinkTag() { $target = 'css/mystyles.css'; diff --git a/user_guide_src/source/helpers/html_helper/011.php b/user_guide_src/source/helpers/html_helper/011.php index 126fd86c4952..f47bb7053770 100644 --- a/user_guide_src/source/helpers/html_helper/011.php +++ b/user_guide_src/source/helpers/html_helper/011.php @@ -1,6 +1,6 @@ 'js/printer.js']; +$script = ['src' => 'js/printer.js', 'defer' => null]; echo script_tag($script); -// +// From a8d5b894c9ae17f1a8c8721eae44c0da22bec87c Mon Sep 17 00:00:00 2001 From: Chl Date: Sun, 10 Apr 2022 03:10:15 +0200 Subject: [PATCH 0119/8282] coding style: cs-fixer is_null() rule --- system/Helpers/html_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 0fc2f110399f..a15b18ff47e8 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -208,7 +208,7 @@ function script_tag($src = '', bool $indexPage = false): string } } else { // for attributes without values, like async or defer, use NULL. - $script .= $k . (is_null($v) ? ' ' : '="' . $v . '" '); + $script .= $k . (null === $v ? ' ' : '="' . $v . '" '); } } From 16dde2391c09e725ea852685f41488d83d26f0d4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Apr 2022 18:16:27 +0900 Subject: [PATCH 0120/8282] fix: add Escaper Exception classes in $coreClassmap --- system/Config/AutoloadConfig.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/system/Config/AutoloadConfig.php b/system/Config/AutoloadConfig.php index 13a6721fd2e6..79cad2ab8d25 100644 --- a/system/Config/AutoloadConfig.php +++ b/system/Config/AutoloadConfig.php @@ -12,6 +12,9 @@ namespace CodeIgniter\Config; use Laminas\Escaper\Escaper; +use Laminas\Escaper\Exception\ExceptionInterface; +use Laminas\Escaper\Exception\InvalidArgumentException as EscaperInvalidArgumentException; +use Laminas\Escaper\Exception\RuntimeException; use Psr\Log\AbstractLogger; use Psr\Log\InvalidArgumentException; use Psr\Log\LoggerAwareInterface; @@ -103,15 +106,18 @@ class AutoloadConfig * @var array */ protected $coreClassmap = [ - AbstractLogger::class => SYSTEMPATH . 'ThirdParty/PSR/Log/AbstractLogger.php', - InvalidArgumentException::class => SYSTEMPATH . 'ThirdParty/PSR/Log/InvalidArgumentException.php', - LoggerAwareInterface::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareInterface.php', - LoggerAwareTrait::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareTrait.php', - LoggerInterface::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerInterface.php', - LoggerTrait::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerTrait.php', - LogLevel::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LogLevel.php', - NullLogger::class => SYSTEMPATH . 'ThirdParty/PSR/Log/NullLogger.php', - Escaper::class => SYSTEMPATH . 'ThirdParty/Escaper/Escaper.php', + AbstractLogger::class => SYSTEMPATH . 'ThirdParty/PSR/Log/AbstractLogger.php', + InvalidArgumentException::class => SYSTEMPATH . 'ThirdParty/PSR/Log/InvalidArgumentException.php', + LoggerAwareInterface::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareInterface.php', + LoggerAwareTrait::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareTrait.php', + LoggerInterface::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerInterface.php', + LoggerTrait::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerTrait.php', + LogLevel::class => SYSTEMPATH . 'ThirdParty/PSR/Log/LogLevel.php', + NullLogger::class => SYSTEMPATH . 'ThirdParty/PSR/Log/NullLogger.php', + ExceptionInterface::class => SYSTEMPATH . 'ThirdParty/Escaper/Exception/ExceptionInterface.php', + EscaperInvalidArgumentException::class => SYSTEMPATH . 'ThirdParty/Escaper/Exception/InvalidArgumentException.php', + RuntimeException::class => SYSTEMPATH . 'ThirdParty/Escaper/Exception/RuntimeException.php', + Escaper::class => SYSTEMPATH . 'ThirdParty/Escaper/Escaper.php', ]; /** From 61999b0205bb76825027d394ce54e00bf72eac16 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Apr 2022 11:57:03 +0900 Subject: [PATCH 0121/8282] fix: multiple CLI::color() inside CLI::write() change color of strings that shouldn't be affected --- system/CLI/CLI.php | 75 ++++++++++++++++++++++++------------ tests/system/CLI/CLITest.php | 22 ++++++++++- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 347a894f7578..4a55d2f7ef00 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -473,6 +473,10 @@ public static function color(string $text, string $foreground, ?string $backgrou return $text; } + if ($text === '') { + return $text; + } + if (! array_key_exists($foreground, static::$foreground_colors)) { throw CLIException::forInvalidColor('foreground', $foreground); } @@ -481,6 +485,51 @@ public static function color(string $text, string $foreground, ?string $backgrou throw CLIException::forInvalidColor('background', $background); } + // Reset text + $newText = ''; + + // Detect if color method was already in use with this text + if (strpos($text, "\033[0m") !== false) { + $pattern = '/\\033\\[0;.+?\\033\\[0m/u'; + + preg_match_all($pattern, $text, $matches); + $coloredStrings = $matches[0]; + + // No colored string found. Invalid strings with no `\033[0;??`. + if ($coloredStrings === []) { + $newText .= self::getColoredText($text, $foreground, $background, $format); + + return $newText; + } + + $nonColoredText = preg_replace( + $pattern, + '<<__colored_string__>>', + $text + ); + $nonColoredChunks = preg_split( + '/<<__colored_string__>>/u', + $nonColoredText + ); + + foreach ($nonColoredChunks as $i => $chunk) { + if ($chunk !== '') { + $newText .= self::getColoredText($chunk, $foreground, $background, $format); + } + + if (isset($coloredStrings[$i])) { + $newText .= $coloredStrings[$i]; + } + } + } else { + $newText .= self::getColoredText($text, $foreground, $background, $format); + } + + return $newText; + } + + private static function getColoredText(string $text, string $foreground, ?string $background, ?string $format): string + { $string = "\033[" . static::$foreground_colors[$foreground] . 'm'; if ($background !== null) { @@ -491,31 +540,9 @@ public static function color(string $text, string $foreground, ?string $backgrou $string .= "\033[4m"; } - // Detect if color method was already in use with this text - if (strpos($text, "\033[0m") !== false) { - // Split the text into parts so that we can see - // if any part missing the color definition - $chunks = mb_split('\\033\\[0m', $text); - // Reset text - $text = ''; - - foreach ($chunks as $chunk) { - if ($chunk === '') { - continue; - } - - // If chunk doesn't have colors defined we need to add them - if (strpos($chunk, "\033[") === false) { - $chunk = static::color($chunk, $foreground, $background, $format); - // Add color reset before chunk and clear end of the string - $text .= rtrim("\033[0m" . $chunk, "\033[0m"); - } else { - $text .= $chunk; - } - } - } + $string .= $text . "\033[0m"; - return $string . $text . "\033[0m"; + return $string; } /** diff --git a/tests/system/CLI/CLITest.php b/tests/system/CLI/CLITest.php index 107ac1463e67..0c2b81c0b3e4 100644 --- a/tests/system/CLI/CLITest.php +++ b/tests/system/CLI/CLITest.php @@ -122,6 +122,7 @@ public function testColorSupportOnNoColor() CLI::init(); // force re-check on env $this->assertSame('test', CLI::color('test', 'white', 'green')); + putenv($nocolor ? "NO_COLOR={$nocolor}" : 'NO_COLOR'); } @@ -132,6 +133,7 @@ public function testColorSupportOnHyperTerminals() CLI::init(); // force re-check on env $this->assertSame("\033[1;37m\033[42m\033[4mtest\033[0m", CLI::color('test', 'white', 'green', 'underline')); + putenv($termProgram ? "TERM_PROGRAM={$termProgram}" : 'TERM_PROGRAM'); } @@ -190,14 +192,30 @@ public function testWriteForeground() public function testWriteForegroundWithColorBefore() { CLI::write(CLI::color('green', 'green') . ' red', 'red'); - $expected = "\033[0;31m\033[0;32mgreen\033[0m\033[0;31m red\033[0m" . PHP_EOL; + + $expected = "\033[0;32mgreen\033[0m\033[0;31m red\033[0m" . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); } public function testWriteForegroundWithColorAfter() { CLI::write('red ' . CLI::color('green', 'green'), 'red'); - $expected = "\033[0;31mred \033[0;32mgreen\033[0m" . PHP_EOL; + + $expected = "\033[0;31mred \033[0m\033[0;32mgreen\033[0m" . PHP_EOL; + $this->assertSame($expected, CITestStreamFilter::$buffer); + } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/5892 + */ + public function testWriteForegroundWithColorTwice() + { + CLI::write( + CLI::color('green', 'green') . ' red ' . CLI::color('green', 'green'), + 'red' + ); + + $expected = "\033[0;32mgreen\033[0m\033[0;31m red \033[0m\033[0;32mgreen\033[0m" . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); } From f406f4aee220b3b85474219b3a4bf0397e914f48 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Apr 2022 12:47:57 +0900 Subject: [PATCH 0122/8282] refactor: vendor/bin/rector process --- system/CLI/CLI.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 4a55d2f7ef00..b43d0005ff77 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -497,9 +497,7 @@ public static function color(string $text, string $foreground, ?string $backgrou // No colored string found. Invalid strings with no `\033[0;??`. if ($coloredStrings === []) { - $newText .= self::getColoredText($text, $foreground, $background, $format); - - return $newText; + return $newText . self::getColoredText($text, $foreground, $background, $format); } $nonColoredText = preg_replace( @@ -540,9 +538,7 @@ private static function getColoredText(string $text, string $foreground, ?string $string .= "\033[4m"; } - $string .= $text . "\033[0m"; - - return $string; + return $string . ($text . "\033[0m"); } /** From 7688add69c8b1e5e87daf04e173d937bd09e4f7f Mon Sep 17 00:00:00 2001 From: Pooya Parsa Dadashi Date: Wed, 13 Apr 2022 09:02:52 +0430 Subject: [PATCH 0123/8282] mailPath Alright! --- user_guide_src/source/libraries/email.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst index b610e69a20df..1287f961713a 100644 --- a/user_guide_src/source/libraries/email.rst +++ b/user_guide_src/source/libraries/email.rst @@ -108,7 +108,7 @@ Preference Default Value Options Descript =================== ====================== ============================ ======================================================================= **userAgent** CodeIgniter None The "user agent". **protocol** mail mail, sendmail, or smtp The mail sending protocol. -**mailpath** /usr/sbin/sendmail None The server path to Sendmail. +**mailPath** /usr/sbin/sendmail None The server path to Sendmail. **SMTPHost** No Default None SMTP Server Address. **SMTPUser** No Default None SMTP Username. **SMTPPass** No Default None SMTP Password. From 8995da98a74748cbca558f13addcc422b743cc05 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Apr 2022 21:24:16 +0900 Subject: [PATCH 0124/8282] test: add line breaks --- tests/system/CLI/CLITest.php | 51 +++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/tests/system/CLI/CLITest.php b/tests/system/CLI/CLITest.php index 0c2b81c0b3e4..2f569e5c81c5 100644 --- a/tests/system/CLI/CLITest.php +++ b/tests/system/CLI/CLITest.php @@ -39,18 +39,21 @@ protected function tearDown(): void public function testNew() { $actual = new CLI(); + $this->assertInstanceOf(CLI::class, $actual); } public function testBeep() { $this->expectOutputString("\x07"); + CLI::beep(); } public function testBeep4() { $this->expectOutputString("\x07\x07\x07\x07"); + CLI::beep(4); } @@ -96,6 +99,7 @@ public function testIsWindows() public function testNewLine() { $this->expectOutputString(''); + CLI::newLine(); } @@ -119,8 +123,8 @@ public function testColorSupportOnNoColor() { $nocolor = getenv('NO_COLOR'); putenv('NO_COLOR=1'); - CLI::init(); // force re-check on env + $this->assertSame('test', CLI::color('test', 'white', 'green')); putenv($nocolor ? "NO_COLOR={$nocolor}" : 'NO_COLOR'); @@ -130,8 +134,8 @@ public function testColorSupportOnHyperTerminals() { $termProgram = getenv('TERM_PROGRAM'); putenv('TERM_PROGRAM=Hyper'); - CLI::init(); // force re-check on env + $this->assertSame("\033[1;37m\033[42m\033[4mtest\033[0m", CLI::color('test', 'white', 'green', 'underline')); putenv($termProgram ? "TERM_PROGRAM={$termProgram}" : 'TERM_PROGRAM'); @@ -148,36 +152,41 @@ public function testColor() // After the tests on NO_COLOR and TERM_PROGRAM above, // the $isColored variable is rigged. So we reset this. CLI::init(); - $this->assertSame("\033[1;37m\033[42m\033[4mtest\033[0m", CLI::color('test', 'white', 'green', 'underline')); + + $this->assertSame( + "\033[1;37m\033[42m\033[4mtest\033[0m", + CLI::color('test', 'white', 'green', 'underline') + ); } public function testPrint() { CLI::print('test'); - $expected = 'test'; + $expected = 'test'; $this->assertSame($expected, CITestStreamFilter::$buffer); } public function testPrintForeground() { CLI::print('test', 'red'); - $expected = "\033[0;31mtest\033[0m"; + $expected = "\033[0;31mtest\033[0m"; $this->assertSame($expected, CITestStreamFilter::$buffer); } public function testPrintBackground() { CLI::print('test', 'red', 'green'); - $expected = "\033[0;31m\033[42mtest\033[0m"; + $expected = "\033[0;31m\033[42mtest\033[0m"; $this->assertSame($expected, CITestStreamFilter::$buffer); } public function testWrite() { CLI::write('test'); + $expected = PHP_EOL . 'test' . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); } @@ -185,6 +194,7 @@ public function testWrite() public function testWriteForeground() { CLI::write('test', 'red'); + $expected = "\033[0;31mtest\033[0m" . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); } @@ -222,6 +232,7 @@ public function testWriteForegroundWithColorTwice() public function testWriteBackground() { CLI::write('test', 'red', 'green'); + $expected = "\033[0;31m\033[42mtest\033[0m" . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); } @@ -229,7 +240,9 @@ public function testWriteBackground() public function testError() { $this->stream_filter = stream_filter_append(STDERR, 'CITestStreamFilter'); + CLI::error('test'); + // red expected cuz stderr $expected = "\033[1;31mtest\033[0m" . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); @@ -238,7 +251,9 @@ public function testError() public function testErrorForeground() { $this->stream_filter = stream_filter_append(STDERR, 'CITestStreamFilter'); + CLI::error('test', 'purple'); + $expected = "\033[0;35mtest\033[0m" . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); } @@ -246,7 +261,9 @@ public function testErrorForeground() public function testErrorBackground() { $this->stream_filter = stream_filter_append(STDERR, 'CITestStreamFilter'); + CLI::error('test', 'purple', 'green'); + $expected = "\033[0;35m\033[42mtest\033[0m" . PHP_EOL; $this->assertSame($expected, CITestStreamFilter::$buffer); } @@ -291,9 +308,18 @@ public function testShowProgressWithoutBar() public function testWrap() { $this->assertSame('', CLI::wrap('')); - $this->assertSame('1234' . PHP_EOL . ' 5678' . PHP_EOL . ' 90' . PHP_EOL . ' abc' . PHP_EOL . ' de' . PHP_EOL . ' fghij' . PHP_EOL . ' 0987654321', CLI::wrap('1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321', 5, 1)); - $this->assertSame('1234 5678 90' . PHP_EOL . ' abc de fghij' . PHP_EOL . ' 0987654321', CLI::wrap('1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321', 999, 2)); - $this->assertSame('1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321', CLI::wrap('1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321')); + $this->assertSame( + '1234' . PHP_EOL . ' 5678' . PHP_EOL . ' 90' . PHP_EOL . ' abc' . PHP_EOL . ' de' . PHP_EOL . ' fghij' . PHP_EOL . ' 0987654321', + CLI::wrap('1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321', 5, 1) + ); + $this->assertSame( + '1234 5678 90' . PHP_EOL . ' abc de fghij' . PHP_EOL . ' 0987654321', + CLI::wrap('1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321', 999, 2) + ); + $this->assertSame( + '1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321', + CLI::wrap('1234 5678 90' . PHP_EOL . 'abc de fghij' . PHP_EOL . '0987654321') + ); } public function testParseCommand() @@ -305,6 +331,7 @@ public function testParseCommand() ]; $_SERVER['argc'] = 3; CLI::init(); + $this->assertNull(CLI::getSegment(3)); $this->assertSame('b', CLI::getSegment(1)); $this->assertSame('c', CLI::getSegment(2)); @@ -330,6 +357,7 @@ public function testParseCommandMixed() 'sure', ]; CLI::init(); + $this->assertNull(CLI::getSegment(7)); $this->assertSame('b', CLI::getSegment(1)); $this->assertSame('c', CLI::getSegment(2)); @@ -353,6 +381,7 @@ public function testParseCommandOption() 'd', ]; CLI::init(); + $this->assertSame(['parm' => 'pvalue'], CLI::getOptions()); $this->assertSame('pvalue', CLI::getOption('parm')); $this->assertSame('-parm pvalue ', CLI::getOptionString()); @@ -377,6 +406,7 @@ public function testParseCommandMultipleOptions() 'value 3', ]; CLI::init(); + $this->assertSame(['parm' => 'pvalue', 'p2' => null, 'p3' => 'value 3'], CLI::getOptions()); $this->assertSame('pvalue', CLI::getOption('parm')); $this->assertSame('-parm pvalue -p2 -p3 "value 3" ', CLI::getOptionString()); @@ -391,11 +421,13 @@ public function testWindow() $height = new ReflectionProperty(CLI::class, 'height'); $height->setAccessible(true); $height->setValue(null); + $this->assertIsInt(CLI::getHeight()); $width = new ReflectionProperty(CLI::class, 'width'); $width->setAccessible(true); $width->setValue(null); + $this->assertIsInt(CLI::getWidth()); } @@ -409,6 +441,7 @@ public function testWindow() public function testTable($tbody, $thead, $expected) { CLI::table($tbody, $thead); + $this->assertSame(CITestStreamFilter::$buffer, $expected); } From 1b26129b2443f7a083968b9bcf8f111e093878a5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Apr 2022 21:28:55 +0900 Subject: [PATCH 0125/8282] test: add test --- tests/system/CLI/CLITest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/system/CLI/CLITest.php b/tests/system/CLI/CLITest.php index 2f569e5c81c5..a8452cdfa7a6 100644 --- a/tests/system/CLI/CLITest.php +++ b/tests/system/CLI/CLITest.php @@ -159,6 +159,14 @@ public function testColor() ); } + public function testColorEmtpyString() + { + $this->assertSame( + '', + CLI::color('', 'white', 'green', 'underline') + ); + } + public function testPrint() { CLI::print('test'); From 768dd238f5eb85a516668f89bde8fc620e0a8022 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 14 Apr 2022 17:05:07 +0900 Subject: [PATCH 0126/8282] docs: improve Managing your Applications - add note for composer.json autoload.psr-4 - change example from Zip install to Composer install --- .../source/general/managing_apps.rst | 72 ++++++++++++++----- .../source/general/managing_apps/001.php | 2 + .../source/general/managing_apps/005.php | 12 ++++ 3 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 user_guide_src/source/general/managing_apps/005.php diff --git a/user_guide_src/source/general/managing_apps.rst b/user_guide_src/source/general/managing_apps.rst index fc00dcd06985..ddba276825d2 100644 --- a/user_guide_src/source/general/managing_apps.rst +++ b/user_guide_src/source/general/managing_apps.rst @@ -8,10 +8,27 @@ 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``. + + .. code-block:: text + + { + ... + "autoload": { + "psr-4": { + "App\\": "app", <-- Remove this line + "Config\\": "app/Config" <-- Remove this line + } + }, + ... + } + .. contents:: :local: :depth: 2 +.. _renaming-app-directory: + Renaming or Relocating the Application Directory ================================================ @@ -46,32 +63,49 @@ and **bar**. You could structure your application project directories like this: .. code-block:: text - /foo - /app - /public - /tests - /writable + foo/ + app/ + public/ + tests/ + writable/ + env + phpunit.xml.dist spark - /bar - /app - /public - /tests - /writable + bar/ + app/ + public/ + tests/ + writable/ + env + phpunit.xml.dist spark - /codeigniter - /system + vendor/ + autoload.php + codeigniter4/framework/ + composer.json + composer.lock + +.. note:: If you install CodeIgniter from the Zip file, the directory structure would be following: + + .. code-block:: text + + foo/ + bar/ + codeigniter4/system/ This would have two apps, **foo** and **bar**, both having standard application directories -and a **public** folder, and sharing a common **codeigniter** framework. +and a **public** folder, and sharing a common **codeigniter4/framework**. -The **index.php** inside each application would refer to its own configuration, -``../app/Config/Paths.php``, and the ``$systemDirectory`` variable in **app/Config/Paths.php** inside each -of those would be set to refer to the shared common **system** folder. +The ``$systemDirectory`` variable in **app/Config/Paths.php** inside each +of those would be set to refer to the shared common **codeigniter4/framework** folder: -If either of the applications had a command-line component, then you would also -modify **spark** inside each application's project folder, as directed above. +.. literalinclude:: managing_apps/005.php -When you use Composer autoloader, fix the ``COMPOSER_PATH`` constant in **app/Config/Constants.php** inside each +.. note:: If you install CodeIgniter from the Zip file, the ``$systemDirectory`` would be ``__DIR__ . '/../../../codeigniter4/system'``. + +And modify the ``COMPOSER_PATH`` constant in **app/Config/Constants.php** inside each of those: .. literalinclude:: managing_apps/004.php + +Only when you change the Application Directory, see :ref:`renaming-app-directory` and modify the paths in the **index.php** and **spark**. diff --git a/user_guide_src/source/general/managing_apps/001.php b/user_guide_src/source/general/managing_apps/001.php index c116317678be..da4991fd738d 100644 --- a/user_guide_src/source/general/managing_apps/001.php +++ b/user_guide_src/source/general/managing_apps/001.php @@ -4,6 +4,8 @@ class Paths { + // ... + public $appDirectory = '/path/to/your/app'; // ... diff --git a/user_guide_src/source/general/managing_apps/005.php b/user_guide_src/source/general/managing_apps/005.php new file mode 100644 index 000000000000..4f5cbf47244c --- /dev/null +++ b/user_guide_src/source/general/managing_apps/005.php @@ -0,0 +1,12 @@ + Date: Fri, 15 Apr 2022 11:16:07 +0900 Subject: [PATCH 0127/8282] docs: replace /app/ with app/ Because there are more places listed as app/. --- user_guide_src/source/cli/cli_commands.rst | 4 ++-- user_guide_src/source/concepts/autoloader.rst | 4 ++-- user_guide_src/source/concepts/mvc.rst | 8 ++++---- user_guide_src/source/general/configuration.rst | 6 +++--- user_guide_src/source/incoming/controllers.rst | 2 +- user_guide_src/source/libraries/throttler.rst | 2 +- user_guide_src/source/libraries/validation.rst | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/user_guide_src/source/cli/cli_commands.rst b/user_guide_src/source/cli/cli_commands.rst index 30d92a195390..524ab80f3644 100644 --- a/user_guide_src/source/cli/cli_commands.rst +++ b/user_guide_src/source/cli/cli_commands.rst @@ -88,7 +88,7 @@ File Location ============= Commands must be stored within a directory named **Commands**. However, that directory can be located anywhere -that the :doc:`Autoloader ` can locate it. This could be in **/app/Commands**, or +that the :doc:`Autoloader ` can locate it. This could be in **app/Commands**, or a directory that you keep commands in to use in all of your project development, like **Acme/Commands**. .. note:: When the commands are executed, the full CodeIgniter CLI environment has been loaded, making it @@ -98,7 +98,7 @@ An Example Command ================== Let's step through an example command whose only function is to report basic information about the application -itself, for demonstration purposes. Start by creating a new file at **/app/Commands/AppInfo.php**. It +itself, for demonstration purposes. Start by creating a new file at **app/Commands/AppInfo.php**. It should contain the following code: .. literalinclude:: cli_commands/002.php diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index b748e520fd98..06f6fa45025c 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -28,7 +28,7 @@ beginning of the framework's execution. Configuration ============= -Initial configuration is done in **/app/Config/Autoload.php**. This file contains two primary +Initial configuration is done in **app/Config/Autoload.php**. This file contains two primary arrays: one for the classmap, and one for PSR-4 compatible namespaces. Namespaces @@ -48,7 +48,7 @@ have a trailing slash. By default, the application folder is namespace to the ``App`` namespace. While you are not forced to namespace the controllers, libraries, or models in the application directory, if you do, they will be found under the ``App`` namespace. -You may change this namespace by editing the **/app/Config/Constants.php** file and setting the +You may change this namespace by editing the **app/Config/Constants.php** file and setting the new namespace value under the ``APP_NAMESPACE`` setting: .. literalinclude:: autoloader/002.php diff --git a/user_guide_src/source/concepts/mvc.rst b/user_guide_src/source/concepts/mvc.rst index 11aa8efcd0af..1338290cc8b0 100644 --- a/user_guide_src/source/concepts/mvc.rst +++ b/user_guide_src/source/concepts/mvc.rst @@ -37,11 +37,11 @@ Views get the data to display from the controllers, who pass it to the views as with simple ``echo`` calls. You can also display other views within a view, making it pretty simple to display a common header or footer on every page. -Views are generally stored in **/app/Views**, but can quickly become unwieldy if not organized in some fashion. +Views are generally stored in **app/Views**, but can quickly become unwieldy if not organized in some fashion. CodeIgniter does not enforce any type of organization, but a good rule of thumb would be to create a new directory in the **Views** directory for each controller. Then, name views by the method name. This makes them very easy to find later on. For example, a user's profile might be displayed in a controller named ``User``, and a method named ``profile``. -You might store the view file for this method in **/app/Views/User/Profile.php**. +You might store the view file for this method in **app/Views/User/Profile.php**. That type of organization works great as a base habit to get into. At times you might need to organize it differently. That's not a problem. As long as CodeIgniter can find the file, it can display it. @@ -61,7 +61,7 @@ it's saved to meet company standards, or formatting a column in a certain way be By keeping these business requirements in the model, you won't repeat code throughout several controllers and accidentally miss updating an area. -Models are typically stored in **/app/Models**, though they can use a namespace to be grouped however you need. +Models are typically stored in **app/Models**, though they can use a namespace to be grouped however you need. :doc:`Find out more about models ` @@ -77,7 +77,7 @@ The other responsibility of the controller is to handle everything that pertains authentication, web safety, encoding, etc. In short, the controller is where you make sure that people are allowed to be there, and they get the data they need in a format they can use. -Controllers are typically stored in **/app/Controllers**, though they can use a namespace to be grouped however +Controllers are typically stored in **app/Controllers**, though they can use a namespace to be grouped however you need. :doc:`Find out more about controllers ` diff --git a/user_guide_src/source/general/configuration.rst b/user_guide_src/source/general/configuration.rst index 85bdc91fe62c..88d389c456a2 100644 --- a/user_guide_src/source/general/configuration.rst +++ b/user_guide_src/source/general/configuration.rst @@ -9,7 +9,7 @@ the required settings are public properties. Unlike many other frameworks, CodeIgniter configurable items aren't contained in a single file. Instead, each class that needs configurable items will have a configuration file with the same name as the class that uses it. You will find -the application configuration files in the **/app/Config** folder. +the application configuration files in the **app/Config** folder. .. contents:: :local: @@ -33,7 +33,7 @@ All configuration object properties are public, so you access the settings like .. literalinclude:: configuration/003.php If no namespace is provided, it will look for the file in all defined namespaces -as well as **/app/Config/**. +as well as **app/Config/**. All of the configuration files that ship with CodeIgniter are namespaced with ``Config``. Using this namespace in your application will provide the best @@ -48,7 +48,7 @@ Creating Configuration Files ============================ When you need a new configuration, first you create a new file at your desired location. -The default file location (recommended for most cases) is **/app/Config**. +The default file location (recommended for most cases) is **app/Config**. The class should use the appropriate namespace, and it should extend ``CodeIgniter\Config\BaseConfig`` to ensure that it can receive environment-specific settings. diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index c5a963c81fdd..9074bf18046f 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -146,7 +146,7 @@ For security reasons be sure to declare any new utility methods as ``protected`` .. literalinclude:: controllers/008.php -Then save the file to your **/app/Controllers/** directory. +Then save the file to your **app/Controllers/** directory. .. important:: The file must be called **Helloworld.php**, with a capital ``H``. diff --git a/user_guide_src/source/libraries/throttler.rst b/user_guide_src/source/libraries/throttler.rst index df0dfeb29d37..27a3283dae6e 100644 --- a/user_guide_src/source/libraries/throttler.rst +++ b/user_guide_src/source/libraries/throttler.rst @@ -64,7 +64,7 @@ Applying the Filter We don't necessarily need to throttle every page on the site. For many web applications, this makes the most sense to apply only to POST requests, though API's might want to limit every request made by a user. In order to apply -this to incoming requests, you need to edit **/app/Config/Filters.php** and first add an alias to the +this to incoming requests, you need to edit **app/Config/Filters.php** and first add an alias to the filter: .. literalinclude:: throttler/003.php diff --git a/user_guide_src/source/libraries/validation.rst b/user_guide_src/source/libraries/validation.rst index 8d3204804e92..641058b29a87 100644 --- a/user_guide_src/source/libraries/validation.rst +++ b/user_guide_src/source/libraries/validation.rst @@ -498,7 +498,7 @@ Creating the Views The first step is to create custom views. These can be placed anywhere that the ``view()`` method can locate them, which means the standard View directory, or any namespaced View folder will work. For example, you could create -a new view at **/app/Views/_errors_list.php**: +a new view at **app/Views/_errors_list.php**: .. literalinclude:: validation/030.php From 33ef2742a96880e7eb1819305c7737f2ec59e94f Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 15 Apr 2022 11:29:10 +0900 Subject: [PATCH 0128/8282] docs: small improvement --- user_guide_src/source/intro/requirements.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/intro/requirements.rst b/user_guide_src/source/intro/requirements.rst index a55b8c46d0d9..974cf108c406 100644 --- a/user_guide_src/source/intro/requirements.rst +++ b/user_guide_src/source/intro/requirements.rst @@ -7,7 +7,10 @@ Server Requirements installed. The following PHP extensions should be enabled on your server: -``php-json``, ``php-mysqlnd``, ``php-xml`` + + - ``php-json`` + - ``php-mysqlnd`` (if you use MySQL) + - ``php-xml`` In order to use the :doc:`CURLRequest `, you will need `libcurl `_ installed. @@ -15,7 +18,7 @@ In order to use the :doc:`CURLRequest `, you will need A database is required for most web application programming. Currently supported databases are: - - MySQL (5.1+) via the *MySQLi* driver + - MySQL via the *MySQLi* driver (version 5.1 and above only) - PostgreSQL via the *Postgre* driver - SQLite3 via the *SQLite3* driver - MSSQL via the *SQLSRV* driver (version 2005 and above only) From 6c01476b8323b18c8838d3371cddaf643e44b4b8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 15 Apr 2022 11:56:03 +0900 Subject: [PATCH 0129/8282] docs: add TOC --- user_guide_src/source/concepts/autoloader.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index b748e520fd98..ecd4428a0cfa 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -2,6 +2,10 @@ Autoloading Files ################# +.. contents:: + :local: + :depth: 2 + Every application consists of a large number of classes in many different locations. The framework provides classes for core functionality. Your application will have a number of libraries, models, and other entities to make it work. You might have third-party From a5c8c50cae82cb1acefcc83447eefc89c363eb68 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 15 Apr 2022 11:56:18 +0900 Subject: [PATCH 0130/8282] docs: remove out of dated description The core classes are not added to the classmap. --- user_guide_src/source/concepts/autoloader.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index ecd4428a0cfa..577a70bf2430 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -18,8 +18,6 @@ It can locate individual namespaced classes that adhere to `PSR-4 `_ autoloading directory structures. -For performance improvement, the core CodeIgniter components have been added to the classmap. - The autoloader works great by itself, but can also work with other autoloaders, like `Composer `_, or even your own custom autoloaders, if needed. Because they're all registered through From 5d737089b8e90fb6665404a1881fdf83cb944956 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 15 Apr 2022 11:58:10 +0900 Subject: [PATCH 0131/8282] docs: remove unneeded description We don't need a trailing back slash, so escaping back slashes is not needed. Double-quotes have nothing to do with escaping. --- user_guide_src/source/concepts/autoloader.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index 577a70bf2430..7e931c677995 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -43,9 +43,8 @@ those classes can be found in: .. literalinclude:: autoloader/001.php -The key of each row is the namespace itself. This does not need a trailing slash. If you use double-quotes -to define the array, be sure to escape the backward slash. That means that it would be ``My\\App``, -not ``My\App``. The value is the location to the directory the classes can be found in. They should +The key of each row is the namespace itself. This does not need a trailing slash. +The value is the location to the directory the classes can be found in. They should have a trailing slash. By default, the application folder is namespace to the ``App`` namespace. While you are not forced to namespace the controllers, From 43fb2fc9b94ca90f8b7a52f8b48582cb20992273 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 15 Apr 2022 12:04:00 +0900 Subject: [PATCH 0132/8282] docs: add `back` --- user_guide_src/source/concepts/autoloader.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index 7e931c677995..5dd349065860 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -43,7 +43,7 @@ those classes can be found in: .. literalinclude:: autoloader/001.php -The key of each row is the namespace itself. This does not need a trailing slash. +The key of each row is the namespace itself. This does not need a trailing back slash. The value is the location to the directory the classes can be found in. They should have a trailing slash. From c2bb64594ada1e76b3ad862f3d0f1909a0e24f5e Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 15 Apr 2022 12:04:26 +0900 Subject: [PATCH 0133/8282] docs: fix the file path text decration --- user_guide_src/source/concepts/autoloader.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index 5dd349065860..3c68567486da 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -76,7 +76,7 @@ Composer Support Composer support is automatically initialized by default. By default, it looks for Composer's autoload file at ``ROOTPATH . 'vendor/autoload.php'``. If you need to change the location of that file for any reason, you can modify -the value defined in ``Config\Constants.php``. +the value defined in **app/Config/Constants.php**. .. note:: If the same namespace is defined in both CodeIgniter and Composer, CodeIgniter's autoloader will be the first one to get a chance to locate the file. From 0208c720f256ee0b3257f1b9c6efd9fdb65548fd Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 15 Apr 2022 14:00:25 +0900 Subject: [PATCH 0134/8282] fix: Composer PSR-4 overwrites Config\Autoload::$psr4 --- system/Autoloader/Autoloader.php | 2 +- tests/system/Autoloader/AutoloaderTest.php | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 520e669be685..311428fe4165 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -328,7 +328,7 @@ private function loadComposerNamespaces(ClassLoader $composer): void $newPaths[rtrim($key, '\\ ')] = $value; } - $this->prefixes = array_merge($this->prefixes, $newPaths); + $this->addNamespace($newPaths); } private function loadComposerClassmap(ClassLoader $composer): void diff --git a/tests/system/Autoloader/AutoloaderTest.php b/tests/system/Autoloader/AutoloaderTest.php index 82e0307affad..1279910d2289 100644 --- a/tests/system/Autoloader/AutoloaderTest.php +++ b/tests/system/Autoloader/AutoloaderTest.php @@ -233,6 +233,23 @@ public function testFindsComposerRoutes() $this->assertArrayHasKey('Laminas\\Escaper', $namespaces); } + public function testComposerNamespaceDoesNotOverwriteConfigAutoloadPsr4() + { + $config = new Autoload(); + $config->psr4 = [ + 'Psr\Log' => '/Config/Autoload/Psr/Log/', + ]; + $modules = new Modules(); + $modules->discoverInComposer = true; + + $this->loader = new Autoloader(); + $this->loader->initialize($config, $modules); + + $namespaces = $this->loader->getNamespace(); + $this->assertSame('/Config/Autoload/Psr/Log/', $namespaces['Psr\Log'][0]); + $this->assertStringContainsString(VENDORPATH, $namespaces['Psr\Log'][1]); + } + public function testFindsComposerRoutesWithComposerPathNotFound() { $composerPath = COMPOSER_PATH; From c3ecc1971d1b6b1bbacfe67372c48b6eb07f15d7 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 8 Apr 2022 01:12:00 +0700 Subject: [PATCH 0135/8282] [PHPStan] Prepare for PHPStan 1.6.x-dev --- composer.json | 2 +- phpstan.neon.dist | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 578156bd017c..e6935404bae9 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "mikey179/vfsstream": "^1.6", "nexusphp/cs-config": "^3.3", "nexusphp/tachycardia": "^1.0", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan": "1.6.x-dev", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1", "rector/rector": "0.12.20" diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8aa28016ca33..cbacdfb82745 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,6 +7,10 @@ services: class: Utils\PHPStan\CheckFrameworkExceptionInstantiationViaNamedConstructorRule tags: - phpstan.rules.rule + - + class: PhpParser\NodeVisitor\NodeConnectingVisitor + tags: + - phpstan.parser.richParserNodeVisitor includes: - phpstan-baseline.neon.dist From fc9a7b8fbd6274768069fa2da42d2ffd316fd213 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 15 Apr 2022 20:36:37 +0700 Subject: [PATCH 0136/8282] set conditional tag --- composer.json | 2 +- phpstan.neon.dist | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index e6935404bae9..d3e0b85bdee8 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "mikey179/vfsstream": "^1.6", "nexusphp/cs-config": "^3.3", "nexusphp/tachycardia": "^1.0", - "phpstan/phpstan": "1.6.x-dev", + "phpstan/phpstan": "^1.5.6", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1", "rector/rector": "0.12.20" diff --git a/phpstan.neon.dist b/phpstan.neon.dist index cbacdfb82745..e4832b125ae2 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,3 +1,7 @@ +conditionalTags: + PhpParser\NodeVisitor\NodeConnectingVisitor: + phpstan.parser.richParserNodeVisitor: true + services: - class: Utils\PHPStan\CheckUseStatementsAfterLicenseRule @@ -7,10 +11,6 @@ services: class: Utils\PHPStan\CheckFrameworkExceptionInstantiationViaNamedConstructorRule tags: - phpstan.rules.rule - - - class: PhpParser\NodeVisitor\NodeConnectingVisitor - tags: - - phpstan.parser.richParserNodeVisitor includes: - phpstan-baseline.neon.dist From 9a60eb3d57671ffe2d69413136c8bcd2aba4c775 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Dadashi Date: Sun, 17 Apr 2022 01:18:58 +0330 Subject: [PATCH 0137/8282] docs:Correction due to 404 Not Found --- tests/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/README.md b/tests/README.md index 0df329864c6b..80f9439d1e6d 100644 --- a/tests/README.md +++ b/tests/README.md @@ -18,7 +18,7 @@ If running under OS X or Linux, you can create a symbolic link to make running t > ln -s ./vendor/bin/phpunit ./phpunit -You also need to install [XDebug](https://xdebug.org/index.php) in order +You also need to install [XDebug](https://xdebug.org/docs/install) in order for code coverage to be calculated successfully. ## Setting Up From ae56d6c66260064b4d1c71629c3fb12d81d79d77 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 08:15:27 +0900 Subject: [PATCH 0138/8282] docs: remove out of dated comment --- system/CLI/CLI.php | 1 - 1 file changed, 1 deletion(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index b43d0005ff77..f017346604a7 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -485,7 +485,6 @@ public static function color(string $text, string $foreground, ?string $backgrou throw CLIException::forInvalidColor('background', $background); } - // Reset text $newText = ''; // Detect if color method was already in use with this text From 63207331448887c8ff81e675b7ac80b34cac15ed Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 08:17:48 +0900 Subject: [PATCH 0139/8282] refactor: combine if statements --- system/CLI/CLI.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index f017346604a7..ae61d50e41d4 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -469,11 +469,7 @@ public static function clearScreen() */ public static function color(string $text, string $foreground, ?string $background = null, ?string $format = null): string { - if (! static::$isColored) { - return $text; - } - - if ($text === '') { + if (! static::$isColored || $text === '') { return $text; } From 9dcb39cb1e2d87bffb0cdc8c85be55253af2041d Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 08:18:18 +0900 Subject: [PATCH 0140/8282] refacter: remove unneeded parentheses --- system/CLI/CLI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index ae61d50e41d4..d88374e80944 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -533,7 +533,7 @@ private static function getColoredText(string $text, string $foreground, ?string $string .= "\033[4m"; } - return $string . ($text . "\033[0m"); + return $string . $text . "\033[0m"; } /** From 2aa93487f2d22365eb18237b48e2fdbea3ddce44 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 08:27:32 +0900 Subject: [PATCH 0141/8282] docs: fix heading underlines --- user_guide_src/source/cli/cli_library.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/cli/cli_library.rst b/user_guide_src/source/cli/cli_library.rst index 4ea8e71e4996..8d269aeead09 100644 --- a/user_guide_src/source/cli/cli_library.rst +++ b/user_guide_src/source/cli/cli_library.rst @@ -15,7 +15,7 @@ CodeIgniter's CLI library makes creating interactive command-line scripts simple :depth: 2 Initializing the Class -====================== +********************** You do not need to create an instance of the CLI library, since all of it's methods are static. Instead, you simply need to ensure your controller can locate it via a ``use`` statement above your class: @@ -25,7 +25,7 @@ need to ensure your controller can locate it via a ``use`` statement above your The class is automatically initialized when the file is loaded the first time. Getting Input from the User -=========================== +*************************** Sometimes you need to ask the user for more information. They might not have provided optional command-line arguments, or the script may have encountered an existing file and needs confirmation before overwriting. This is @@ -66,7 +66,7 @@ Named keys are also possible: Finally, you can pass :ref:`validation ` rules to the answer input as the third parameter, the acceptable answers are automatically restricted to the passed options. Providing Feedback -================== +****************** **write()** From 85670436edbc7a605f85d8898d61dec374455481 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 08:31:59 +0900 Subject: [PATCH 0142/8282] docs: change method names to headings Otherwise, I can't link to it. --- user_guide_src/source/cli/cli_library.rst | 33 +++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/user_guide_src/source/cli/cli_library.rst b/user_guide_src/source/cli/cli_library.rst index 8d269aeead09..bfb3a2479903 100644 --- a/user_guide_src/source/cli/cli_library.rst +++ b/user_guide_src/source/cli/cli_library.rst @@ -52,7 +52,8 @@ Validation rules can also be written in the array syntax: .. literalinclude:: cli_library/006.php -**promptByKey()** +promptByKey() +============= Predefined answers (options) for prompt sometimes need to be described or are too complex to select via their value. ``promptByKey()`` allows the user to select an option by its key instead of its value: @@ -68,7 +69,8 @@ Finally, you can pass :ref:`validation ` rules to the answer input a Providing Feedback ****************** -**write()** +write() +======= Several methods are provided for you to provide feedback to your users. This can be as simple as a single status update or a complex table of information that wraps to the user's terminal window. At the core of this is the ``write()`` @@ -116,7 +118,8 @@ And a smaller number are available as background colors: * light_gray * magenta -**print()** +print() +======= Print functions identically to the ``write()`` method, except that it does not force a newline either before or after. Instead it prints it to the screen wherever the cursor is currently. This allows you to print multiple items all on @@ -125,7 +128,8 @@ print "Done" on the same line: .. literalinclude:: cli_library/012.php -**color()** +color() +======= While the ``write()`` command will write a single line to the terminal, ending it with a EOL character, you can use the ``color()`` method to make a string fragment that can be used in the same way, except that it will not force @@ -137,7 +141,8 @@ it inside of a ``write()`` method to create a string of a different color inside This example would write a single line to the window, with ``fileA`` in yellow, followed by a tab, and then ``/path/to/file`` in white text. -**error()** +error() +======= If you need to output errors, you should use the appropriately named ``error()`` method. This writes light-red text to STDERR, instead of STDOUT, like ``write()`` and ``color()`` do. This can be useful if you have scripts watching @@ -146,7 +151,8 @@ exactly as you would the ``write()`` method: .. literalinclude:: cli_library/014.php -**wrap()** +wrap() +====== This command will take a string, start printing it on the current line, and wrap it to a set length on new lines. This might be useful when displaying a list of options with descriptions that you want to wrap in the current @@ -179,13 +185,15 @@ Would create something like this: industry's standard dummy text ever since the -**newLine()** +newLine() +========= The ``newLine()`` method displays a blank line to the user. It does not take any parameters: .. literalinclude:: cli_library/018.php -**clearScreen()** +clearScreen() +============= You can clear the current terminal window with the ``clearScreen()`` method. In most versions of Windows, this will simply insert 40 blank lines since Windows doesn't support this feature. Windows 10 bash integration should change @@ -193,7 +201,8 @@ this: .. literalinclude:: cli_library/019.php -**showProgress()** +showProgress() +============== If you have a long-running task that you would like to keep the user updated with the progress, you can use the ``showProgress()`` method which displays something like the following: @@ -210,7 +219,8 @@ pass ``false`` as the first parameter and the progress bar will be removed. .. literalinclude:: cli_library/020.php -**table()** +table() +======= .. literalinclude:: cli_library/021.php @@ -223,7 +233,8 @@ pass ``false`` as the first parameter and the progress bar will be removed. | 8 | Another great item title | 2017-11-16 13:46:54 | 0 | +----+--------------------------+---------------------+--------+ -**wait()** +wait() +====== Waits a certain number of seconds, optionally showing a wait message and waiting for a key press. From c0ca99f51475e25d3fb87d9a7ed02dd74baf4725 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 08:38:10 +0900 Subject: [PATCH 0143/8282] docs: add changelog --- user_guide_src/source/changelogs/v4.2.0.rst | 1 + user_guide_src/source/cli/cli_library.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index c0081761ffd4..449ad50f970d 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -18,6 +18,7 @@ BREAKING - ``spark`` - The method signature of ``CodeIgniter\CLI\CommandRunner::_remap()`` has been changed to fix a bug. - The ``CodeIgniter\Autoloader\Autoloader::initialize()`` has changed the behavior to fix a bug. It used to use Composer classmap only when ``$modules->discoverInComposer`` is true. Now it always uses the Composer classmap if Composer is available. +- The color code output by :ref:`CLI::color() ` has been changed to fix a bug. Enhancements ************ diff --git a/user_guide_src/source/cli/cli_library.rst b/user_guide_src/source/cli/cli_library.rst index bfb3a2479903..29bc6b252f88 100644 --- a/user_guide_src/source/cli/cli_library.rst +++ b/user_guide_src/source/cli/cli_library.rst @@ -128,6 +128,8 @@ print "Done" on the same line: .. literalinclude:: cli_library/012.php +.. _cli-library-color: + color() ======= From 4ad1edfdbb16ec7fc7dc903f832dd975e2063b4b Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 13:07:27 +0900 Subject: [PATCH 0144/8282] docs: fix heading underline level --- user_guide_src/source/models/model.rst | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index fa354d7c3aff..e626662bef2d 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -7,7 +7,7 @@ Using CodeIgniter's Model :depth: 2 Models -====== +****** The CodeIgniter's Model provides convenience features and additional functionality that people commonly use to make working with a **single table** in your database more convenient. @@ -17,7 +17,7 @@ methods for much of the standard ways you would need to interact with a database updating records, deleting records, and more. Accessing Models -================ +**************** Models are typically stored in the ``app/Models`` directory. They should have a namespace that matches their location within the directory, like ``namespace App\Models``. @@ -27,7 +27,7 @@ You can access models within your classes by creating a new instance or using th .. literalinclude:: model/001.php CodeIgniter's Model -=================== +******************* CodeIgniter does provide a model class that provides a few nice features, including: @@ -41,7 +41,7 @@ This class provides a solid base from which to build your own models, allowing y rapidly build out your application's model layer. Creating Your Model -=================== +******************* To take advantage of CodeIgniter's model, you would simply create a new model class that extends ``CodeIgniter\Model``: @@ -58,7 +58,7 @@ extra steps without repeating the constructor parameters, for example extending .. literalinclude:: model/003.php Connecting to the Database --------------------------- +========================== When the class is first instantiated, if no database connection instance is passed to the constructor, it will automatically connect to the default database group, as set in the configuration. You can @@ -72,7 +72,7 @@ You would replace "group_name" with the name of a defined database group from th configuration file. Configuring Your Model ----------------------- +====================== The model class has a few configuration options that can be set to allow the class' methods to work seamlessly for you. The first two are used by all of the CRUD methods to determine @@ -190,10 +190,10 @@ time specified in the property name. Whether the callbacks defined above should be used. Working With Data -================= +***************** Finding Data ------------- +============ Several functions are provided for doing basic CRUD work on your tables, including ``find()``, ``insert()``, ``update()``, ``delete()`` and more. @@ -258,7 +258,7 @@ the next **find*()** methods to return only soft deleted rows: .. literalinclude:: model/014.php Saving Data ------------ +=========== **insert()** @@ -316,7 +316,7 @@ model's ``save()`` method to inspect the class, grab any public and private prop that provides several handy features that make developing Entities simpler. Deleting Data -------------- +============= **delete()** @@ -343,7 +343,7 @@ Cleans out the database table by permanently removing all rows that have 'delete .. literalinclude:: model/026.php Validating Data ---------------- +=============== For many people, validating data in the model is the preferred way to ensure the data is kept to a single standard, without duplicating code. The Model class provides a way to automatically have all data validated @@ -416,7 +416,7 @@ and simply set ``$validationRules`` to the name of the validation rule group you .. literalinclude:: model/034.php Retrieving Validation Rules ---------------------------- +=========================== You can retrieve a model's validation rules by accessing its ``validationRules`` property: @@ -435,7 +435,7 @@ value an array of fieldnames of interest: .. literalinclude:: model/037.php Validation Placeholders ------------------------ +======================= The model provides a simple method to replace parts of your rules based on data that's being passed into it. This sounds fairly obscure but can be especially handy with the ``is_unique`` validation rule. Placeholders are simply @@ -459,7 +459,7 @@ This can also be used to create more dynamic rules at runtime, as long as you ta keys passed in don't conflict with your form data. Protecting Fields ------------------ +================= To help protect against Mass Assignment Attacks, the Model class **requires** that you list all of the field names that can be changed during inserts and updates in the ``$allowedFields`` class property. Any data provided @@ -474,7 +474,7 @@ testing, migrations, or seeds. In these cases, you can turn the protection on or .. literalinclude:: model/042.php Working With Query Builder --------------------------- +========================== You can get access to a shared instance of the Query Builder for that model's database connection any time you need it: @@ -501,7 +501,7 @@ very elegant use: .. literalinclude:: model/046.php Runtime Return Type Changes ----------------------------- +=========================== You can specify the format that data should be returned as when using the **find*()** methods as the class property, ``$returnType``. There may be times that you would like the data back in a different format, though. The Model @@ -523,7 +523,7 @@ Returns data from the next **find*()** method as standard objects or custom clas .. literalinclude:: model/048.php Processing Large Amounts of Data --------------------------------- +================================ Sometimes, you need to process large amounts of data and would run the risk of running out of memory. To make this simpler, you may use the chunk() method to get smaller chunks of data that you can then @@ -535,7 +535,7 @@ This is best used during cronjobs, data exports, or other large tasks. .. literalinclude:: model/049.php Model Events -============ +************ There are several points within the model's execution that you can specify multiple callback methods to run. These methods can be used to normalize data, hash passwords, save related entities, and much more. The following @@ -543,7 +543,7 @@ points in the model's execution can be affected, each through a class property: ``$beforeUpdate``, ``$afterUpdate``, ``$afterFind``, and ``$afterDelete``. Defining Callbacks ------------------- +================== You specify the callbacks by first creating a new class method in your model to use. This class will always receive a ``$data`` array as its only parameter. The exact contents of the ``$data`` array will vary between events, but @@ -555,7 +555,7 @@ must return the original $data array so other callbacks have the full informatio .. literalinclude:: model/050.php Specifying Callbacks To Run ---------------------------- +=========================== You specify when to run the callbacks by adding the method name to the appropriate class property (``$beforeInsert``, ``$afterUpdate``, etc). Multiple callbacks can be added to a single event and they will be processed one after the other. You can @@ -572,7 +572,7 @@ You may also change this setting temporarily for a single model call sing the `` .. literalinclude:: model/053.php Event Parameters ----------------- +================ Since the exact data passed to each callback varies a bit, here are the details on what is in the ``$data`` parameter passed to each event: @@ -607,7 +607,7 @@ afterDelete **id** = primary key of row being deleted. ================ ========================================================================================================= Modifying Find* Data --------------------- +==================== The ``beforeFind`` and ``afterFind`` methods can both return a modified set of data to override the normal response from the model. For ``afterFind`` any changes made to ``data`` in the return array will automatically be passed back @@ -617,7 +617,7 @@ boolean, ``returnData``: .. literalinclude:: model/054.php Manual Model Creation -===================== +********************* You do not need to extend any special class to create a model for your application. All you need is to get an instance of the database connection and you're good to go. This allows you to bypass the features CodeIgniter's From f3a34f72ff38fe9fb9ebfe95beb84daf512b043c Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 18 Apr 2022 13:08:21 +0900 Subject: [PATCH 0145/8282] docs: change **foo** to headings You can link to headings, but cannot link to `**foo**`. --- user_guide_src/source/models/model.rst | 101 ++++++++++++++++--------- 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index e626662bef2d..84fdbf4be249 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -80,13 +80,15 @@ what table to use and how we can find the required records: .. literalinclude:: model/005.php -**$table** +$table +------ Specifies the database table that this model primarily works with. This only applies to the built-in CRUD methods. You are not restricted to using only this table in your own queries. -**$primaryKey** +$primaryKey +----------- This is the name of the column that uniquely identifies the records in this table. This does not necessarily have to match the primary key that is specified in the database, but @@ -95,7 +97,8 @@ is used with methods like ``find()`` to know what column to match the specified .. note:: All Models must have a primaryKey specified to allow all of the features to work as expected. -**$useAutoIncrement** +$useAutoIncrement +----------------- Specifies if the table uses an auto-increment feature for ``$primaryKey``. If set to ``false`` then you are responsible for providing primary key value for every record in the table. This @@ -106,7 +109,8 @@ default value is ``true``. key in the database to ``unique``. This way you will make sure that all of Model's features will still work the same as before. -**$returnType** +$returnType +----------- The Model's CRUD methods will take a step of work away from you and automatically return the resulting data, instead of the Result object. This setting allows you to define @@ -114,7 +118,8 @@ the type of data that is returned. Valid values are '**array**' (the default), ' qualified name of a class** that can be used with the Result object's ``getCustomResultObject()`` method. -**$useSoftDeletes** +$useSoftDeletes +--------------- If true, then any ``delete()`` method calls will set ``deleted_at`` in the database, instead of actually deleting the row. This can preserve data when it might be referenced elsewhere, or @@ -126,66 +131,81 @@ This requires either a DATETIME or INTEGER field in the database as per the mode ``$dateFormat`` setting. The default field name is ``deleted_at`` however this name can be configured to any name of your choice by using ``$deletedField`` property. -**$allowedFields** +$allowedFields +-------------- This array should be updated with the field names that can be set during ``save()``, ``insert()``, or ``update()`` methods. Any field names other than these will be discarded. This helps to protect against just taking input from a form and throwing it all at the model, resulting in potential mass assignment vulnerabilities. -**$useTimestamps** +$useTimestamps +-------------- This boolean value determines whether the current date is automatically added to all inserts and updates. If true, will set the current time in the format specified by ``$dateFormat``. This requires that the table have columns named **created_at** and **updated_at** in the appropriate data type. -**$createdField** +$createdField +------------- Specifies which database field to use for data record create timestamp. Leave it empty to avoid updating it (even if ``$useTimestamps`` is enabled). -**$updatedField** +$updatedField +------------- Specifies which database field should use for keep data record update timestamp. Leave it empty to avoid update it (even ``$useTimestamps`` is enabled). -**$dateFormat** +$dateFormat +----------- This value works with ``$useTimestamps`` and ``$useSoftDeletes`` to ensure that the correct type of date value gets inserted into the database. By default, this creates DATETIME values, but valid options are: ``'datetime'``, ``'date'``, or ``'int'`` (a PHP timestamp). Using **useSoftDeletes** or -**useTimestamps** with an invalid or missing dateFormat will cause an exception. +useTimestamps with an invalid or missing dateFormat will cause an exception. -**$validationRules** +$validationRules +---------------- Contains either an array of validation rules as described in :ref:`validation-array` or a string containing the name of a validation group, as described in the same section. Described in more detail below. -**$validationMessages** +$validationMessages +------------------- Contains an array of custom error messages that should be used during validation, as described in :ref:`validation-custom-errors`. Described in more detail below. -**$skipValidation** +$skipValidation +--------------- Whether validation should be skipped during all **inserts** and **updates**. The default value is false, meaning that data will always attempt to be validated. This is primarily used by the ``skipValidation()`` method, but may be changed to ``true`` so this model will never validate. -**$beforeInsert** -**$afterInsert** -**$beforeUpdate** -**$afterUpdate** -**$afterFind** -**$afterDelete** +$beforeInsert +------------- +$afterInsert +------------ +$beforeUpdate +------------- +$afterUpdate +------------ +$afterFind +---------- +$afterDelete +------------ These arrays allow you to specify callback methods that will be run on the data at the time specified in the property name. -**$allowCallbacks** +$allowCallbacks +--------------- Whether the callbacks defined above should be used. @@ -198,7 +218,8 @@ Finding Data Several functions are provided for doing basic CRUD work on your tables, including ``find()``, ``insert()``, ``update()``, ``delete()`` and more. -**find()** +find() +------ Returns a single row where the primary key matches the value passed in as the first parameter: @@ -214,7 +235,8 @@ of just one: If no parameters are passed in, will return all rows in that model's table, effectively acting like ``findAll()``, though less explicit. -**findColumn()** +findColumn() +------------ Returns null or an indexed array of column values: @@ -222,7 +244,8 @@ Returns null or an indexed array of column values: ``$column_name`` should be a name of single column else you will get the DataException. -**findAll()** +findAll() +--------- Returns all results: @@ -237,20 +260,23 @@ parameters, respectively: .. literalinclude:: model/011.php -**first()** +first() +------- Returns the first row in the result set. This is best used in combination with the query builder. .. literalinclude:: model/012.php -**withDeleted()** +withDeleted() +------------- If ``$useSoftDeletes`` is true, then the **find*()** methods will not return any rows where 'deleted_at IS NOT NULL'. To temporarily override this, you can use the ``withDeleted()`` method prior to calling the **find*()** method. .. literalinclude:: model/013.php -**onlyDeleted()** +onlyDeleted() +------------- Whereas ``withDeleted()`` will return both deleted and not-deleted rows, this method modifies the next **find*()** methods to return only soft deleted rows: @@ -260,7 +286,8 @@ the next **find*()** methods to return only soft deleted rows: Saving Data =========== -**insert()** +insert() +-------- An associative array of data is passed into this method as the only parameter to create a new row of data in the database. The array's keys must match the name of the columns in a ``$table``, while @@ -268,7 +295,8 @@ the array's values are the values to save for that key: .. literalinclude:: model/015.php -**update()** +update() +-------- Updates an existing record in the database. The first parameter is the ``$primaryKey`` of the record to update. An associative array of data is passed into this method as the second parameter. The array's keys must match the name @@ -285,7 +313,8 @@ update command, with the added benefit of validation, events, etc: .. literalinclude:: model/018.php -**save()** +save() +------ This is a wrapper around the ``insert()`` and ``update()`` methods that handle inserting or updating the record automatically, based on whether it finds an array key matching the **primary key** value: @@ -318,7 +347,8 @@ model's ``save()`` method to inspect the class, grab any public and private prop Deleting Data ============= -**delete()** +delete() +-------- Takes a primary key value as the first parameter and deletes the matching record from the model's table: @@ -336,7 +366,8 @@ previously: .. literalinclude:: model/025.php -**purgeDeleted()** +purgeDeleted() +-------------- Cleans out the database table by permanently removing all rows that have 'deleted_at IS NOT NULL'. @@ -510,13 +541,15 @@ provides methods that allow you to do just that. .. note:: These methods only change the return type for the next **find*()** method call. After that, it is reset to its default value. -**asArray()** +asArray() +--------- Returns data from the next **find*()** method as associative arrays: .. literalinclude:: model/047.php -**asObject()** +asObject() +---------- Returns data from the next **find*()** method as standard objects or custom class intances: From 8f1b0c80f73745abfeb9354e491391cdb41a0863 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Apr 2022 15:02:55 +0000 Subject: [PATCH 0146/8282] chore(deps-dev): update rector/rector requirement Updates the requirements on [rector/rector](https://github.com/rectorphp/rector) to permit the latest version. - [Release notes](https://github.com/rectorphp/rector/releases) - [Commits](https://github.com/rectorphp/rector/compare/0.12.20...0.12.21) --- updated-dependencies: - dependency-name: rector/rector dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d3e0b85bdee8..9c1ee4adb46e 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "phpstan/phpstan": "^1.5.6", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1", - "rector/rector": "0.12.20" + "rector/rector": "0.12.21" }, "suggest": { "ext-fileinfo": "Improves mime type detection for files" From 185754ed87b80dc846810097bd38e147a9bac4fb Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 18 Apr 2022 22:15:59 +0700 Subject: [PATCH 0147/8282] clean up removed service and updated to use new RectorConfig --- rector.php | 98 ++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/rector.php b/rector.php index 9ee8a4217d21..79aa188aa94f 100644 --- a/rector.php +++ b/rector.php @@ -21,12 +21,11 @@ use Rector\CodeQuality\Rector\If_\ShortenElseIfRector; use Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector; use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector; -use Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector; use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector; use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector; use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; -use Rector\Core\Configuration\Option; +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector; use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector; use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector; @@ -45,31 +44,31 @@ use Rector\PSR4\Rector\FileWithoutNamespace\NormalizeNamespaceByPSR4ComposerAutoloadRector; use Rector\Set\ValueObject\LevelSetList; use Rector\Set\ValueObject\SetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Utils\Rector\PassStrictParameterToFunctionParameterRector; use Utils\Rector\RemoveErrorSuppressInTryCatchStmtsRector; use Utils\Rector\RemoveVarTagFromClassConstantRector; use Utils\Rector\UnderscoreToCamelCaseVariableNameRector; -return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import(SetList::DEAD_CODE); - $containerConfigurator->import(LevelSetList::UP_TO_PHP_74); - $containerConfigurator->import(PHPUnitSetList::PHPUNIT_SPECIFIC_METHOD); - $containerConfigurator->import(PHPUnitSetList::PHPUNIT_80); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->sets([ + SetList::DEAD_CODE, + LevelSetList::UP_TO_PHP_74, + PHPUnitSetList::PHPUNIT_SPECIFIC_METHOD, + PHPUnitSetList::PHPUNIT_80, + ]); - $parameters = $containerConfigurator->parameters(); + $rectorConfig->parallel(); - $parameters->set(Option::PARALLEL, true); // paths to refactor; solid alternative to CLI arguments - $parameters->set(Option::PATHS, [__DIR__ . '/app', __DIR__ . '/system', __DIR__ . '/tests', __DIR__ . '/utils/Rector']); + $rectorConfig->paths([__DIR__ . '/app', __DIR__ . '/system', __DIR__ . '/tests', __DIR__ . '/utils/Rector']); // do you need to include constants, class aliases or custom autoloader? files listed will be executed - $parameters->set(Option::BOOTSTRAP_FILES, [ + $rectorConfig->bootstrapFiles([ __DIR__ . '/system/Test/bootstrap.php', ]); // is there a file you need to skip? - $parameters->set(Option::SKIP, [ + $rectorConfig->skip([ __DIR__ . '/app/Views', __DIR__ . '/system/Debug/Toolbar/Views/toolbar.tpl.php', __DIR__ . '/system/ThirdParty', @@ -124,42 +123,39 @@ ]); // auto import fully qualified class names - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(UnderscoreToCamelCaseVariableNameRector::class); - $services->set(SimplifyUselessVariableRector::class); - $services->set(RemoveAlwaysElseRector::class); - $services->set(PassStrictParameterToFunctionParameterRector::class); - $services->set(CountArrayToEmptyArrayComparisonRector::class); - $services->set(ForToForeachRector::class); - $services->set(ChangeNestedForeachIfsToEarlyContinueRector::class); - $services->set(ChangeIfElseValueAssignToEarlyReturnRector::class); - $services->set(SimplifyStrposLowerRector::class); - $services->set(CombineIfRector::class); - $services->set(SimplifyIfReturnBoolRector::class); - $services->set(InlineIfToExplicitIfRector::class); - $services->set(PreparedValueToEarlyReturnRector::class); - $services->set(ShortenElseIfRector::class); - $services->set(SimplifyIfElseToTernaryRector::class); - $services->set(UnusedForeachValueToArrayKeysRector::class); - $services->set(ChangeArrayPushToArrayAssignRector::class); - $services->set(UnnecessaryTernaryExpressionRector::class); - $services->set(RemoveErrorSuppressInTryCatchStmtsRector::class); - $services->set(RemoveVarTagFromClassConstantRector::class); - $services->set(AddPregQuoteDelimiterRector::class); - $services->set(SimplifyRegexPatternRector::class); - $services->set(FuncGetArgsToVariadicParamRector::class); - $services->set(MakeInheritedMethodVisibilitySameAsParentRector::class); - $services->set(SimplifyEmptyArrayCheckRector::class); - $services->set(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); - $services->set(StringClassNameToClassConstantRector::class) - ->configure([ - 'Error', - 'Exception', - 'InvalidArgumentException', - 'Closure', - 'stdClass', - 'SQLite3', - ]); + $rectorConfig->importNames(); + + $rectorConfig->rule(UnderscoreToCamelCaseVariableNameRector::class); + $rectorConfig->rule(RemoveAlwaysElseRector::class); + $rectorConfig->rule(PassStrictParameterToFunctionParameterRector::class); + $rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class); + $rectorConfig->rule(ForToForeachRector::class); + $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class); + $rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class); + $rectorConfig->rule(SimplifyStrposLowerRector::class); + $rectorConfig->rule(CombineIfRector::class); + $rectorConfig->rule(SimplifyIfReturnBoolRector::class); + $rectorConfig->rule(InlineIfToExplicitIfRector::class); + $rectorConfig->rule(PreparedValueToEarlyReturnRector::class); + $rectorConfig->rule(ShortenElseIfRector::class); + $rectorConfig->rule(SimplifyIfElseToTernaryRector::class); + $rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class); + $rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class); + $rectorConfig->rule(UnnecessaryTernaryExpressionRector::class); + $rectorConfig->rule(RemoveErrorSuppressInTryCatchStmtsRector::class); + $rectorConfig->rule(RemoveVarTagFromClassConstantRector::class); + $rectorConfig->rule(AddPregQuoteDelimiterRector::class); + $rectorConfig->rule(SimplifyRegexPatternRector::class); + $rectorConfig->rule(FuncGetArgsToVariadicParamRector::class); + $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class); + $rectorConfig->rule(SimplifyEmptyArrayCheckRector::class); + $rectorConfig->rule(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); + $rectorConfig->ruleWithConfiguration(StringClassNameToClassConstantRector::class, [ + 'Error', + 'Exception', + 'InvalidArgumentException', + 'Closure', + 'stdClass', + 'SQLite3', + ]); }; From 4fd7ac58307590112e6bdb6add99c1d9d085e57f Mon Sep 17 00:00:00 2001 From: Chl Date: Mon, 18 Apr 2022 16:43:20 +0200 Subject: [PATCH 0148/8282] script_tag: changelog, doc + test with default arg --- system/Helpers/html_helper.php | 4 ++-- tests/system/Helpers/HTMLHelperTest.php | 10 ++++++++++ user_guide_src/source/changelogs/v4.2.0.rst | 1 + user_guide_src/source/helpers/html_helper.rst | 4 ++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index a15b18ff47e8..c2e48d890fef 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -189,8 +189,8 @@ function doctype(string $type = 'html5'): string * * Generates link to a JS file * - * @param mixed $src Script source or an array - * @param bool $indexPage Should indexPage be added to the JS path + * @param array|string $src Script source or an array of attributes + * @param bool $indexPage Should indexPage be added to the JS path */ function script_tag($src = '', bool $indexPage = false): string { diff --git a/tests/system/Helpers/HTMLHelperTest.php b/tests/system/Helpers/HTMLHelperTest.php index 74b087228fdd..d539d7421be6 100755 --- a/tests/system/Helpers/HTMLHelperTest.php +++ b/tests/system/Helpers/HTMLHelperTest.php @@ -269,6 +269,16 @@ public function testScriptTagWithSrcAndAttributes() $this->assertSame($expected, script_tag($target)); } + /** + * This test has probably no real-world value but may help detecting + * a change in the default behaviour. + */ + public function testScriptTagWithoutAnyArg() + { + $expected = ''; + $this->assertSame($expected, script_tag()); + } + public function testLinkTag() { $target = 'css/mystyles.css'; diff --git a/user_guide_src/source/changelogs/v4.2.0.rst b/user_guide_src/source/changelogs/v4.2.0.rst index 0ed69ab0760a..fa88608dc355 100644 --- a/user_guide_src/source/changelogs/v4.2.0.rst +++ b/user_guide_src/source/changelogs/v4.2.0.rst @@ -38,6 +38,7 @@ Enhancements - The log format has also changed. If users are depending on the log format in their apps, the new log format is "<1-based count> (): " - Added support for webp files to **app/Config/Mimes.php**. - Added 4th parameter ``$includeDir`` to ``get_filenames()``. See :php:func:`get_filenames`. +- HTML helper ``script_tag()`` now uses ``null`` values to write boolean attributes in minimized form: `` + - -
-
-

getCode() ? ' #' . $exception->getCode() : '') ?>

-

- getMessage())) ?> - getMessage())) ?>" - rel="noreferrer" target="_blank">search → -

-
-
- - -
-

at line

- - -
- -
- -
- -
- - - -
- - -
- -
    - $row) : ?> - -
  1. -

    - - - +

    +
    +

    getCode() ? ' #' . $exception->getCode() : '') ?>

    +

    + getMessage())) ?> + getMessage())) ?>" + rel="noreferrer" target="_blank">search → +

    +
    +
    + + +
    +

    at line

    + + +
    + +
    + +
    + +
    + + + +
    + + +
    + +
      + $row) : ?> + +
    1. +

      + + + - - {PHP internal code} - - - - -   —   - - - ( arguments ) -

      - - - + {PHP internal code} + + + + +   —   + + + ( arguments ) +
      +
      + + $value) : ?> - - - - - - -
      name : "#{$key}") ?>
      -
      - - () - - - - -   —   () - -

      - - - -
      - -
      - -
    2. - - -
    - -
    - - -
    - - + name : "#{$key}") ?> +
    + + + + +
    + + () + + + + +   —   () + +

    + + + +
    + +
    + +
  2. + + +
+ +
+ + +
+ + -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- - - - - - -

Constants

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PathgetUri()) ?>
HTTP MethodgetMethod())) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
- - - - - $ + + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + + +

Constants

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathgetUri()) ?>
HTTP MethodgetMethod())) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
+ + + + + - - -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- - - - - -
- No $_GET, $_POST, or $_COOKIE Information to show. -
- - - - getHeaders(); ?> - - -

Headers

- - - - - - - - - - - + +

$

+ +
HeaderValue
+ + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + +
+ No $_GET, $_POST, or $_COOKIE Information to show. +
+ + + + getHeaders(); ?> + + +

Headers

+ + + + + + + + + + + - - - - - - - - -
HeaderValue
getName(), 'html') ?>getValueLine(), 'html') ?>
- - -
- - - + + getName(), 'html') ?> + getValueLine(), 'html') ?> + + + + + + + +
+ + + setStatusCode(http_response_code()); ?> -
- - - - - -
Response StatusgetStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
- - getHeaders(); ?> - - - -

Headers

- - - - - - - - - - $value) : ?> - - - - - - -
HeaderValue
getHeaderLine($name), 'html') ?>
- - -
- - -
- - -
    - -
  1. - -
-
- - -
- - - - - - - - - - - - - - - - -
Memory Usage
Peak Memory Usage:
Memory Limit:
- -
- -
- - - - +
+ + + + + +
Response StatusgetStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
+ + getHeaders(); ?> + + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
getHeaderLine($name), 'html') ?>
+ + +
+ + +
+ + +
    + +
  1. + +
+
+ + +
+ + + + + + + + + + + + + + + + +
Memory Usage
Peak Memory Usage:
Memory Limit:
+ +
+ + + + + + diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php index cca49c2ed9c3..9faa4a15b783 100644 --- a/app/Views/errors/html/production.php +++ b/app/Views/errors/html/production.php @@ -1,24 +1,24 @@ - - + + - Whoops! + Whoops! - + -
+
-

Whoops!

+

Whoops!

-

We seem to have hit a snag. Please try again later...

+

We seem to have hit a snag. Please try again later...

-
+
diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php index b982f58aa158..c66a9615c6be 100644 --- a/app/Views/welcome_message.php +++ b/app/Views/welcome_message.php @@ -1,229 +1,229 @@ - - Welcome to CodeIgniter 4! - - - - - - - + + Welcome to CodeIgniter 4! + + + + + + +
- - -
- -

Welcome to CodeIgniter

- -

The small framework with powerful features

- -
+ + +
+ +

Welcome to CodeIgniter

+ +

The small framework with powerful features

+ +
@@ -231,91 +231,91 @@
-

About this page

+

About this page

-

The page you are looking at is being generated dynamically by CodeIgniter.

+

The page you are looking at is being generated dynamically by CodeIgniter.

-

If you would like to edit this page you will find it located at:

+

If you would like to edit this page you will find it located at:

-
app/Views/welcome_message.php
+
app/Views/welcome_message.php
-

The corresponding controller for this page can be found at:

+

The corresponding controller for this page can be found at:

-
app/Controllers/Home.php
+
app/Controllers/Home.php
-
+
-

Go further

+

Go further

-

- - Learn -

+

+ + Learn +

-

The User Guide contains an introduction, tutorial, a number of "how to" - guides, and then reference documentation for the components that make up - the framework. Check the User Guide !

+

The User Guide contains an introduction, tutorial, a number of "how to" + guides, and then reference documentation for the components that make up + the framework. Check the User Guide !

-

- - Discuss -

+

+ + Discuss +

-

CodeIgniter is a community-developed open source project, with several - venues for the community members to gather and exchange ideas. View all - the threads on CodeIgniter's forum, or chat on Slack !

+

CodeIgniter is a community-developed open source project, with several + venues for the community members to gather and exchange ideas. View all + the threads on CodeIgniter's forum, or chat on Slack !

-

- - Contribute -

+

+ + Contribute +

-

CodeIgniter is a community driven project and accepts contributions - of code and documentation from the community. Why not - - join us ?

+

CodeIgniter is a community driven project and accepts contributions + of code and documentation from the community. Why not + + join us ?

-
+
-
+
-

Page rendered in {elapsed_time} seconds

+

Page rendered in {elapsed_time} seconds

-

Environment:

+

Environment:

-
+
-
+
-

© CodeIgniter Foundation. CodeIgniter is open source project released under the MIT - open source licence.

+

© CodeIgniter Foundation. CodeIgniter is open source project released under the MIT + open source licence.

-
+
diff --git a/app/index.html b/app/index.html index b702fbc3967b..69df4e1dff68 100644 --- a/app/index.html +++ b/app/index.html @@ -1,7 +1,7 @@ - 403 Forbidden + 403 Forbidden diff --git a/system/Debug/Toolbar/Views/_config.tpl b/system/Debug/Toolbar/Views/_config.tpl index 3934cf62ebee..b6c7e0c3d86f 100644 --- a/system/Debug/Toolbar/Views/_config.tpl +++ b/system/Debug/Toolbar/Views/_config.tpl @@ -1,48 +1,48 @@

- Read the CodeIgniter docs... + Read the CodeIgniter docs...

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeIgniter Version:{ ciVersion }
PHP Version:{ phpVersion }
PHP SAPI:{ phpSAPI }
Environment:{ environment }
Base URL: - { if $baseURL == '' } -
- The $baseURL should always be set manually to prevent possible URL personification from external parties. -
- { else } - { baseURL } - { endif } -
Timezone:{ timezone }
Locale:{ locale }
Content Security Policy Enabled:{ if $cspEnabled } Yes { else } No { endif }
CodeIgniter Version:{ ciVersion }
PHP Version:{ phpVersion }
PHP SAPI:{ phpSAPI }
Environment:{ environment }
Base URL: + { if $baseURL == '' } +
+ The $baseURL should always be set manually to prevent possible URL personification from external parties. +
+ { else } + { baseURL } + { endif } +
Timezone:{ timezone }
Locale:{ locale }
Content Security Policy Enabled:{ if $cspEnabled } Yes { else } No { endif }
diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index 3bd05d5e9592..7805a99dda05 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -4,684 +4,684 @@ var ciDebugBar = { - toolbarContainer : null, - toolbar : null, - icon : null, - - init : function () { - this.toolbarContainer = document.getElementById('toolbarContainer'); - this.toolbar = document.getElementById('debug-bar'); - this.icon = document.getElementById('debug-icon'); - - ciDebugBar.createListeners(); - ciDebugBar.setToolbarState(); - ciDebugBar.setToolbarPosition(); - ciDebugBar.setToolbarTheme(); - ciDebugBar.toggleViewsHints(); - ciDebugBar.routerLink(); - - document.getElementById('debug-bar-link').addEventListener('click', ciDebugBar.toggleToolbar, true); - document.getElementById('debug-icon-link').addEventListener('click', ciDebugBar.toggleToolbar, true); - - // Allows to highlight the row of the current history request - var btn = this.toolbar.querySelector('button[data-time="' + localStorage.getItem('debugbar-time') + '"]'); - ciDebugBar.addClass(btn.parentNode.parentNode, 'current'); - - historyLoad = this.toolbar.getElementsByClassName('ci-history-load'); - - for (var i = 0; i < historyLoad.length; i++) - { - historyLoad[i].addEventListener('click', function () { - loadDoc(this.getAttribute('data-time')); - }, true); - } - - // Display the active Tab on page load - var tab = ciDebugBar.readCookie('debug-bar-tab'); - if (document.getElementById(tab)) - { - var el = document.getElementById(tab); - el.style.display = 'block'; - ciDebugBar.addClass(el, 'active'); - tab = document.querySelector('[data-tab=' + tab + ']'); - if (tab) - { - ciDebugBar.addClass(tab.parentNode, 'active'); - } - } - }, - - createListeners : function () { - var buttons = [].slice.call(this.toolbar.querySelectorAll('.ci-label a')); - - for (var i = 0; i < buttons.length; i++) - { - buttons[i].addEventListener('click', ciDebugBar.showTab, true); - } - - // Hook up generic toggle via data attributes `data-toggle="foo"` - var links = this.toolbar.querySelectorAll('[data-toggle]'); - for (var i = 0; i < links.length; i++) - { - links[i].addEventListener('click', ciDebugBar.toggleRows, true); - } - }, - - showTab: function () { - // Get the target tab, if any - var tab = document.getElementById(this.getAttribute('data-tab')); - - // If the label have not a tab stops here - if (! tab) - { - return; - } - - // Remove debug-bar-tab cookie - ciDebugBar.createCookie('debug-bar-tab', '', -1); - - // Check our current state. - var state = tab.style.display; - - // Hide all tabs - var tabs = document.querySelectorAll('#debug-bar .tab'); - - for (var i = 0; i < tabs.length; i++) - { - tabs[i].style.display = 'none'; - } - - // Mark all labels as inactive - var labels = document.querySelectorAll('#debug-bar .ci-label'); - - for (var i = 0; i < labels.length; i++) - { - ciDebugBar.removeClass(labels[i], 'active'); - } - - // Show/hide the selected tab - if (state != 'block') - { - tab.style.display = 'block'; - ciDebugBar.addClass(this.parentNode, 'active'); - // Create debug-bar-tab cookie to persistent state - ciDebugBar.createCookie('debug-bar-tab', this.getAttribute('data-tab'), 365); - } - }, - - addClass : function (el, className) { - if (el.classList) - { - el.classList.add(className); - } - else - { - el.className += ' ' + className; - } - }, - - removeClass : function (el, className) { - if (el.classList) - { - el.classList.remove(className); - } - else - { - el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); - } - }, - - /** - * Toggle display of another object based on - * the data-toggle value of this object - * - * @param event - */ - toggleRows : function(event) { - if(event.target) - { - let row = event.target.closest('tr'); - let target = document.getElementById(row.getAttribute('data-toggle')); - target.style.display = target.style.display === 'none' ? 'table-row' : 'none'; - } - }, - - /** - * Toggle display of a data table - * - * @param obj - */ - toggleDataTable : function (obj) { - if (typeof obj == 'string') - { - obj = document.getElementById(obj + '_table'); - } - - if (obj) - { - obj.style.display = obj.style.display === 'none' ? 'block' : 'none'; - } - }, - - /** - * Toggle display of timeline child elements - * - * @param obj - */ - toggleChildRows : function (obj) { - if (typeof obj == 'string') - { - par = document.getElementById(obj + '_parent') - obj = document.getElementById(obj + '_children'); - } - - if (par && obj) - { - obj.style.display = obj.style.display === 'none' ? '' : 'none'; - par.classList.toggle('timeline-parent-open'); - } - }, - - - //-------------------------------------------------------------------- - - /** - * Toggle tool bar from full to icon and icon to full - */ - toggleToolbar : function () { - var open = ciDebugBar.toolbar.style.display != 'none'; - - ciDebugBar.icon.style.display = open == true ? 'inline-block' : 'none'; - ciDebugBar.toolbar.style.display = open == false ? 'inline-block' : 'none'; - - // Remember it for other page loads on this site - ciDebugBar.createCookie('debug-bar-state', '', -1); - ciDebugBar.createCookie('debug-bar-state', open == true ? 'minimized' : 'open' , 365); - }, - - /** - * Sets the initial state of the toolbar (open or minimized) when - * the page is first loaded to allow it to remember the state between refreshes. - */ - setToolbarState: function () { - var open = ciDebugBar.readCookie('debug-bar-state'); - - ciDebugBar.icon.style.display = open != 'open' ? 'inline-block' : 'none'; - ciDebugBar.toolbar.style.display = open == 'open' ? 'inline-block' : 'none'; - }, - - toggleViewsHints: function () { - // Avoid toggle hints on history requests that are not the initial - if (localStorage.getItem('debugbar-time') != localStorage.getItem('debugbar-time-new')) - { - var a = document.querySelector('a[data-tab="ci-views"]'); - a.href = '#'; - return; - } - - var nodeList = []; // [ Element, NewElement( 1 )/OldElement( 0 ) ] - var sortedComments = []; - var comments = []; - - var getComments = function () { - var nodes = []; - var result = []; - var xpathResults = document.evaluate( "//comment()[starts-with(., ' DEBUG-VIEW')]", document, null, XPathResult.ANY_TYPE, null); - var nextNode = xpathResults.iterateNext(); - while ( nextNode ) - { - nodes.push( nextNode ); - nextNode = xpathResults.iterateNext(); - } - - // sort comment by opening and closing tags - for (var i = 0; i < nodes.length; ++i) - { - // get file path + name to use as key - var path = nodes[i].nodeValue.substring( 18, nodes[i].nodeValue.length - 1 ); - - if ( nodes[i].nodeValue[12] === 'S' ) // simple check for start comment - { - // create new entry - result[path] = [ nodes[i], null ]; - } - else if (result[path]) - { - // add to existing entry - result[path][1] = nodes[i]; - } - } - - return result; - }; - - // find node that has TargetNode as parentNode - var getParentNode = function ( node, targetNode ) { - if ( node.parentNode === null ) - { - return null; - } - - if ( node.parentNode !== targetNode ) - { - return getParentNode( node.parentNode, targetNode ); - } - - return node; - }; - - // define invalid & outer ( also invalid ) elements - const INVALID_ELEMENTS = [ 'NOSCRIPT', 'SCRIPT', 'STYLE' ]; - const OUTER_ELEMENTS = [ 'HTML', 'BODY', 'HEAD' ]; - - var getValidElementInner = function ( node, reverse ) { - // handle invalid tags - if ( OUTER_ELEMENTS.indexOf( node.nodeName ) !== -1 ) - { - for (var i = 0; i < document.body.children.length; ++i) - { - var index = reverse ? document.body.children.length - ( i + 1 ) : i; - var element = document.body.children[index]; - - // skip invalid tags - if ( INVALID_ELEMENTS.indexOf( element.nodeName ) !== -1 ) - { - continue; - } - - return [ element, reverse ]; - } - - return null; - } - - // get to next valid element - while ( node !== null && INVALID_ELEMENTS.indexOf( node.nodeName ) !== -1 ) - { - node = reverse ? node.previousElementSibling : node.nextElementSibling; - } - - // return non array if we couldnt find something - if ( node === null ) - { - return null; - } - - return [ node, reverse ]; - }; - - // get next valid element ( to be safe to add divs ) - // @return [ element, skip element ] or null if we couldnt find a valid place - var getValidElement = function ( nodeElement ) { - if (nodeElement) - { - if ( nodeElement.nextElementSibling !== null ) - { - return getValidElementInner( nodeElement.nextElementSibling, false ) - || getValidElementInner( nodeElement.previousElementSibling, true ); - } - if ( nodeElement.previousElementSibling !== null ) - { - return getValidElementInner( nodeElement.previousElementSibling, true ); - } - } - - // something went wrong! -> element is not in DOM - return null; - }; - - function showHints() - { - // Had AJAX? Reset view blocks - sortedComments = getComments(); - - for (var key in sortedComments) - { - var startElement = getValidElement( sortedComments[key][0] ); - var endElement = getValidElement( sortedComments[key][1] ); - - // skip if we couldnt get a valid element - if ( startElement === null || endElement === null ) - { - continue; - } - - // find element which has same parent as startelement - var jointParent = getParentNode( endElement[0], startElement[0].parentNode ); - if ( jointParent === null ) - { - // find element which has same parent as endelement - jointParent = getParentNode( startElement[0], endElement[0].parentNode ); - if ( jointParent === null ) - { - // both tries failed - continue; - } - else - { - startElement[0] = jointParent; - } - } - else - { - endElement[0] = jointParent; - } - - var debugDiv = document.createElement( 'div' ); // holder - var debugPath = document.createElement( 'div' ); // path - var childArray = startElement[0].parentNode.childNodes; // target child array - var parent = startElement[0].parentNode; - var start, end; - - // setup container - debugDiv.classList.add( 'debug-view' ); - debugDiv.classList.add( 'show-view' ); - debugPath.classList.add( 'debug-view-path' ); - debugPath.innerText = key; - debugDiv.appendChild( debugPath ); - - // calc distance between them - // start - for (var i = 0; i < childArray.length; ++i) - { - // check for comment ( start & end ) -> if its before valid start element - if ( childArray[i] === sortedComments[key][1] || - childArray[i] === sortedComments[key][0] || - childArray[i] === startElement[0] ) - { - start = i; - if ( childArray[i] === sortedComments[key][0] ) - { - start++; // increase to skip the start comment - } - break; - } - } - // adjust if we want to skip the start element - if ( startElement[1] ) - { - start++; - } - - // end - for (var i = start; i < childArray.length; ++i) - { - if ( childArray[i] === endElement[0] ) - { - end = i; - // dont break to check for end comment after end valid element - } - else if ( childArray[i] === sortedComments[key][1] ) - { - // if we found the end comment, we can break - end = i; - break; - } - } - - // move elements - var number = end - start; - if ( endElement[1] ) - { - number++; - } - for (var i = 0; i < number; ++i) - { - if ( INVALID_ELEMENTS.indexOf( childArray[start] ) !== -1 ) - { - // skip invalid childs that can cause problems if moved - start++; - continue; - } - debugDiv.appendChild( childArray[start] ); - } - - // add container to DOM - nodeList.push( parent.insertBefore( debugDiv, childArray[start] ) ); - } - - ciDebugBar.createCookie('debug-view', 'show', 365); - ciDebugBar.addClass(btn, 'active'); - } - - function hideHints() - { - for (var i = 0; i < nodeList.length; ++i) - { - var index; - - // find index - for (var j = 0; j < nodeList[i].parentNode.childNodes.length; ++j) - { - if ( nodeList[i].parentNode.childNodes[j] === nodeList[i] ) - { - index = j; - break; - } - } - - // move child back - while ( nodeList[i].childNodes.length !== 1 ) - { - nodeList[i].parentNode.insertBefore( nodeList[i].childNodes[1], nodeList[i].parentNode.childNodes[index].nextSibling ); - index++; - } - - nodeList[i].parentNode.removeChild( nodeList[i] ); - } - nodeList.length = 0; - - ciDebugBar.createCookie('debug-view', '', -1); - ciDebugBar.removeClass(btn, 'active'); - } - - var btn = document.querySelector('[data-tab=ci-views]'); - - // If the Views Collector is inactive stops here - if (! btn) - { - return; - } - - btn.parentNode.onclick = function () { - if (ciDebugBar.readCookie('debug-view')) - { - hideHints(); - } - else - { - showHints(); - } - }; - - // Determine Hints state on page load - if (ciDebugBar.readCookie('debug-view')) - { - showHints(); - } - }, - - setToolbarPosition: function () { - var btnPosition = this.toolbar.querySelector('#toolbar-position'); - - if (ciDebugBar.readCookie('debug-bar-position') === 'top') - { - ciDebugBar.addClass(ciDebugBar.icon, 'fixed-top'); - ciDebugBar.addClass(ciDebugBar.toolbar, 'fixed-top'); - } - - btnPosition.addEventListener('click', function () { - var position = ciDebugBar.readCookie('debug-bar-position'); - - ciDebugBar.createCookie('debug-bar-position', '', -1); - - if (!position || position === 'bottom') - { - ciDebugBar.createCookie('debug-bar-position', 'top', 365); - ciDebugBar.addClass(ciDebugBar.icon, 'fixed-top'); - ciDebugBar.addClass(ciDebugBar.toolbar, 'fixed-top'); - } - else - { - ciDebugBar.createCookie('debug-bar-position', 'bottom', 365); - ciDebugBar.removeClass(ciDebugBar.icon, 'fixed-top'); - ciDebugBar.removeClass(ciDebugBar.toolbar, 'fixed-top'); - } - }, true); - }, - - setToolbarTheme: function () { - var btnTheme = this.toolbar.querySelector('#toolbar-theme'); - var isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches; - var isLightMode = window.matchMedia("(prefers-color-scheme: light)").matches; - - // If a cookie is set with a value, we force the color scheme - if (ciDebugBar.readCookie('debug-bar-theme') === 'dark') - { - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'light'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'dark'); - } - else if (ciDebugBar.readCookie('debug-bar-theme') === 'light') - { - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); - } - - btnTheme.addEventListener('click', function () { - var theme = ciDebugBar.readCookie('debug-bar-theme'); - - if (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches) - { - // If there is no cookie, and "prefers-color-scheme" is set to "dark" - // It means that the user wants to switch to light mode - ciDebugBar.createCookie('debug-bar-theme', 'light', 365); - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); - } - else - { - if (theme === 'dark') - { - ciDebugBar.createCookie('debug-bar-theme', 'light', 365); - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); - } - else - { - // In any other cases: if there is no cookie, or the cookie is set to - // "light", or the "prefers-color-scheme" is "light"... - ciDebugBar.createCookie('debug-bar-theme', 'dark', 365); - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'light'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'dark'); - } - } - }, true); - }, - - /** - * Helper to create a cookie. - * - * @param name - * @param value - * @param days - */ - createCookie : function (name,value,days) { - if (days) - { - var date = new Date(); - - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - - var expires = "; expires=" + date.toGMTString(); - } - else - { - var expires = ""; - } - - document.cookie = name + "=" + value + expires + "; path=/; samesite=Lax"; - }, - - readCookie : function (name) { - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - - for (var i = 0; i < ca.length; i++) - { - var c = ca[i]; - while (c.charAt(0) == ' ') - { - c = c.substring(1,c.length); - } - if (c.indexOf(nameEQ) == 0) - { - return c.substring(nameEQ.length,c.length); - } - } - return null; - }, - - trimSlash: function (text) { - return text.replace(/^\/|\/$/g, ''); - }, - - routerLink: function () { - var row, _location; - var rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"]'); - var patt = /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/; - - for (var i = 0; i < rowGet.length; i++) - { - row = rowGet[i]; - if (!/\/\(.+?\)/.test(rowGet[i].innerText)) - { - row.style = 'cursor: pointer;'; - row.setAttribute('title', location.origin + '/' + ciDebugBar.trimSlash(row.innerText)); - row.addEventListener('click', function (ev) { - _location = location.origin + '/' + ciDebugBar.trimSlash(ev.target.innerText); - var redirectWindow = window.open(_location, '_blank'); - redirectWindow.location; - }); - } - else - { - row.innerHTML = '
' + row.innerText + '
' - + '
' - + row.innerText.replace(patt, '') - + '' - + '
'; - } - } - - rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"] form'); - for (var i = 0; i < rowGet.length; i++) - { - row = rowGet[i]; - - row.addEventListener('submit', function (event) { - event.preventDefault() - var inputArray = [], t = 0; - var input = event.target.querySelectorAll('input[type=text]'); - var tpl = event.target.getAttribute('data-debugbar-route-tpl'); - - for (var n = 0; n < input.length; n++) - { - if (input[n].value.length > 0) - { - inputArray.push(input[n].value); - } - } - - if (inputArray.length > 0) - { - _location = location.origin + '/' + tpl.replace(/\?/g, function () { - return inputArray[t++] - }); - - var redirectWindow = window.open(_location, '_blank'); - redirectWindow.location; - } - }) - } - } + toolbarContainer : null, + toolbar : null, + icon : null, + + init : function () { + this.toolbarContainer = document.getElementById('toolbarContainer'); + this.toolbar = document.getElementById('debug-bar'); + this.icon = document.getElementById('debug-icon'); + + ciDebugBar.createListeners(); + ciDebugBar.setToolbarState(); + ciDebugBar.setToolbarPosition(); + ciDebugBar.setToolbarTheme(); + ciDebugBar.toggleViewsHints(); + ciDebugBar.routerLink(); + + document.getElementById('debug-bar-link').addEventListener('click', ciDebugBar.toggleToolbar, true); + document.getElementById('debug-icon-link').addEventListener('click', ciDebugBar.toggleToolbar, true); + + // Allows to highlight the row of the current history request + var btn = this.toolbar.querySelector('button[data-time="' + localStorage.getItem('debugbar-time') + '"]'); + ciDebugBar.addClass(btn.parentNode.parentNode, 'current'); + + historyLoad = this.toolbar.getElementsByClassName('ci-history-load'); + + for (var i = 0; i < historyLoad.length; i++) + { + historyLoad[i].addEventListener('click', function () { + loadDoc(this.getAttribute('data-time')); + }, true); + } + + // Display the active Tab on page load + var tab = ciDebugBar.readCookie('debug-bar-tab'); + if (document.getElementById(tab)) + { + var el = document.getElementById(tab); + el.style.display = 'block'; + ciDebugBar.addClass(el, 'active'); + tab = document.querySelector('[data-tab=' + tab + ']'); + if (tab) + { + ciDebugBar.addClass(tab.parentNode, 'active'); + } + } + }, + + createListeners : function () { + var buttons = [].slice.call(this.toolbar.querySelectorAll('.ci-label a')); + + for (var i = 0; i < buttons.length; i++) + { + buttons[i].addEventListener('click', ciDebugBar.showTab, true); + } + + // Hook up generic toggle via data attributes `data-toggle="foo"` + var links = this.toolbar.querySelectorAll('[data-toggle]'); + for (var i = 0; i < links.length; i++) + { + links[i].addEventListener('click', ciDebugBar.toggleRows, true); + } + }, + + showTab: function () { + // Get the target tab, if any + var tab = document.getElementById(this.getAttribute('data-tab')); + + // If the label have not a tab stops here + if (! tab) + { + return; + } + + // Remove debug-bar-tab cookie + ciDebugBar.createCookie('debug-bar-tab', '', -1); + + // Check our current state. + var state = tab.style.display; + + // Hide all tabs + var tabs = document.querySelectorAll('#debug-bar .tab'); + + for (var i = 0; i < tabs.length; i++) + { + tabs[i].style.display = 'none'; + } + + // Mark all labels as inactive + var labels = document.querySelectorAll('#debug-bar .ci-label'); + + for (var i = 0; i < labels.length; i++) + { + ciDebugBar.removeClass(labels[i], 'active'); + } + + // Show/hide the selected tab + if (state != 'block') + { + tab.style.display = 'block'; + ciDebugBar.addClass(this.parentNode, 'active'); + // Create debug-bar-tab cookie to persistent state + ciDebugBar.createCookie('debug-bar-tab', this.getAttribute('data-tab'), 365); + } + }, + + addClass : function (el, className) { + if (el.classList) + { + el.classList.add(className); + } + else + { + el.className += ' ' + className; + } + }, + + removeClass : function (el, className) { + if (el.classList) + { + el.classList.remove(className); + } + else + { + el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); + } + }, + + /** + * Toggle display of another object based on + * the data-toggle value of this object + * + * @param event + */ + toggleRows : function(event) { + if(event.target) + { + let row = event.target.closest('tr'); + let target = document.getElementById(row.getAttribute('data-toggle')); + target.style.display = target.style.display === 'none' ? 'table-row' : 'none'; + } + }, + + /** + * Toggle display of a data table + * + * @param obj + */ + toggleDataTable : function (obj) { + if (typeof obj == 'string') + { + obj = document.getElementById(obj + '_table'); + } + + if (obj) + { + obj.style.display = obj.style.display === 'none' ? 'block' : 'none'; + } + }, + + /** + * Toggle display of timeline child elements + * + * @param obj + */ + toggleChildRows : function (obj) { + if (typeof obj == 'string') + { + par = document.getElementById(obj + '_parent') + obj = document.getElementById(obj + '_children'); + } + + if (par && obj) + { + obj.style.display = obj.style.display === 'none' ? '' : 'none'; + par.classList.toggle('timeline-parent-open'); + } + }, + + + //-------------------------------------------------------------------- + + /** + * Toggle tool bar from full to icon and icon to full + */ + toggleToolbar : function () { + var open = ciDebugBar.toolbar.style.display != 'none'; + + ciDebugBar.icon.style.display = open == true ? 'inline-block' : 'none'; + ciDebugBar.toolbar.style.display = open == false ? 'inline-block' : 'none'; + + // Remember it for other page loads on this site + ciDebugBar.createCookie('debug-bar-state', '', -1); + ciDebugBar.createCookie('debug-bar-state', open == true ? 'minimized' : 'open' , 365); + }, + + /** + * Sets the initial state of the toolbar (open or minimized) when + * the page is first loaded to allow it to remember the state between refreshes. + */ + setToolbarState: function () { + var open = ciDebugBar.readCookie('debug-bar-state'); + + ciDebugBar.icon.style.display = open != 'open' ? 'inline-block' : 'none'; + ciDebugBar.toolbar.style.display = open == 'open' ? 'inline-block' : 'none'; + }, + + toggleViewsHints: function () { + // Avoid toggle hints on history requests that are not the initial + if (localStorage.getItem('debugbar-time') != localStorage.getItem('debugbar-time-new')) + { + var a = document.querySelector('a[data-tab="ci-views"]'); + a.href = '#'; + return; + } + + var nodeList = []; // [ Element, NewElement( 1 )/OldElement( 0 ) ] + var sortedComments = []; + var comments = []; + + var getComments = function () { + var nodes = []; + var result = []; + var xpathResults = document.evaluate( "//comment()[starts-with(., ' DEBUG-VIEW')]", document, null, XPathResult.ANY_TYPE, null); + var nextNode = xpathResults.iterateNext(); + while ( nextNode ) + { + nodes.push( nextNode ); + nextNode = xpathResults.iterateNext(); + } + + // sort comment by opening and closing tags + for (var i = 0; i < nodes.length; ++i) + { + // get file path + name to use as key + var path = nodes[i].nodeValue.substring( 18, nodes[i].nodeValue.length - 1 ); + + if ( nodes[i].nodeValue[12] === 'S' ) // simple check for start comment + { + // create new entry + result[path] = [ nodes[i], null ]; + } + else if (result[path]) + { + // add to existing entry + result[path][1] = nodes[i]; + } + } + + return result; + }; + + // find node that has TargetNode as parentNode + var getParentNode = function ( node, targetNode ) { + if ( node.parentNode === null ) + { + return null; + } + + if ( node.parentNode !== targetNode ) + { + return getParentNode( node.parentNode, targetNode ); + } + + return node; + }; + + // define invalid & outer ( also invalid ) elements + const INVALID_ELEMENTS = [ 'NOSCRIPT', 'SCRIPT', 'STYLE' ]; + const OUTER_ELEMENTS = [ 'HTML', 'BODY', 'HEAD' ]; + + var getValidElementInner = function ( node, reverse ) { + // handle invalid tags + if ( OUTER_ELEMENTS.indexOf( node.nodeName ) !== -1 ) + { + for (var i = 0; i < document.body.children.length; ++i) + { + var index = reverse ? document.body.children.length - ( i + 1 ) : i; + var element = document.body.children[index]; + + // skip invalid tags + if ( INVALID_ELEMENTS.indexOf( element.nodeName ) !== -1 ) + { + continue; + } + + return [ element, reverse ]; + } + + return null; + } + + // get to next valid element + while ( node !== null && INVALID_ELEMENTS.indexOf( node.nodeName ) !== -1 ) + { + node = reverse ? node.previousElementSibling : node.nextElementSibling; + } + + // return non array if we couldnt find something + if ( node === null ) + { + return null; + } + + return [ node, reverse ]; + }; + + // get next valid element ( to be safe to add divs ) + // @return [ element, skip element ] or null if we couldnt find a valid place + var getValidElement = function ( nodeElement ) { + if (nodeElement) + { + if ( nodeElement.nextElementSibling !== null ) + { + return getValidElementInner( nodeElement.nextElementSibling, false ) + || getValidElementInner( nodeElement.previousElementSibling, true ); + } + if ( nodeElement.previousElementSibling !== null ) + { + return getValidElementInner( nodeElement.previousElementSibling, true ); + } + } + + // something went wrong! -> element is not in DOM + return null; + }; + + function showHints() + { + // Had AJAX? Reset view blocks + sortedComments = getComments(); + + for (var key in sortedComments) + { + var startElement = getValidElement( sortedComments[key][0] ); + var endElement = getValidElement( sortedComments[key][1] ); + + // skip if we couldnt get a valid element + if ( startElement === null || endElement === null ) + { + continue; + } + + // find element which has same parent as startelement + var jointParent = getParentNode( endElement[0], startElement[0].parentNode ); + if ( jointParent === null ) + { + // find element which has same parent as endelement + jointParent = getParentNode( startElement[0], endElement[0].parentNode ); + if ( jointParent === null ) + { + // both tries failed + continue; + } + else + { + startElement[0] = jointParent; + } + } + else + { + endElement[0] = jointParent; + } + + var debugDiv = document.createElement( 'div' ); // holder + var debugPath = document.createElement( 'div' ); // path + var childArray = startElement[0].parentNode.childNodes; // target child array + var parent = startElement[0].parentNode; + var start, end; + + // setup container + debugDiv.classList.add( 'debug-view' ); + debugDiv.classList.add( 'show-view' ); + debugPath.classList.add( 'debug-view-path' ); + debugPath.innerText = key; + debugDiv.appendChild( debugPath ); + + // calc distance between them + // start + for (var i = 0; i < childArray.length; ++i) + { + // check for comment ( start & end ) -> if its before valid start element + if ( childArray[i] === sortedComments[key][1] || + childArray[i] === sortedComments[key][0] || + childArray[i] === startElement[0] ) + { + start = i; + if ( childArray[i] === sortedComments[key][0] ) + { + start++; // increase to skip the start comment + } + break; + } + } + // adjust if we want to skip the start element + if ( startElement[1] ) + { + start++; + } + + // end + for (var i = start; i < childArray.length; ++i) + { + if ( childArray[i] === endElement[0] ) + { + end = i; + // dont break to check for end comment after end valid element + } + else if ( childArray[i] === sortedComments[key][1] ) + { + // if we found the end comment, we can break + end = i; + break; + } + } + + // move elements + var number = end - start; + if ( endElement[1] ) + { + number++; + } + for (var i = 0; i < number; ++i) + { + if ( INVALID_ELEMENTS.indexOf( childArray[start] ) !== -1 ) + { + // skip invalid childs that can cause problems if moved + start++; + continue; + } + debugDiv.appendChild( childArray[start] ); + } + + // add container to DOM + nodeList.push( parent.insertBefore( debugDiv, childArray[start] ) ); + } + + ciDebugBar.createCookie('debug-view', 'show', 365); + ciDebugBar.addClass(btn, 'active'); + } + + function hideHints() + { + for (var i = 0; i < nodeList.length; ++i) + { + var index; + + // find index + for (var j = 0; j < nodeList[i].parentNode.childNodes.length; ++j) + { + if ( nodeList[i].parentNode.childNodes[j] === nodeList[i] ) + { + index = j; + break; + } + } + + // move child back + while ( nodeList[i].childNodes.length !== 1 ) + { + nodeList[i].parentNode.insertBefore( nodeList[i].childNodes[1], nodeList[i].parentNode.childNodes[index].nextSibling ); + index++; + } + + nodeList[i].parentNode.removeChild( nodeList[i] ); + } + nodeList.length = 0; + + ciDebugBar.createCookie('debug-view', '', -1); + ciDebugBar.removeClass(btn, 'active'); + } + + var btn = document.querySelector('[data-tab=ci-views]'); + + // If the Views Collector is inactive stops here + if (! btn) + { + return; + } + + btn.parentNode.onclick = function () { + if (ciDebugBar.readCookie('debug-view')) + { + hideHints(); + } + else + { + showHints(); + } + }; + + // Determine Hints state on page load + if (ciDebugBar.readCookie('debug-view')) + { + showHints(); + } + }, + + setToolbarPosition: function () { + var btnPosition = this.toolbar.querySelector('#toolbar-position'); + + if (ciDebugBar.readCookie('debug-bar-position') === 'top') + { + ciDebugBar.addClass(ciDebugBar.icon, 'fixed-top'); + ciDebugBar.addClass(ciDebugBar.toolbar, 'fixed-top'); + } + + btnPosition.addEventListener('click', function () { + var position = ciDebugBar.readCookie('debug-bar-position'); + + ciDebugBar.createCookie('debug-bar-position', '', -1); + + if (!position || position === 'bottom') + { + ciDebugBar.createCookie('debug-bar-position', 'top', 365); + ciDebugBar.addClass(ciDebugBar.icon, 'fixed-top'); + ciDebugBar.addClass(ciDebugBar.toolbar, 'fixed-top'); + } + else + { + ciDebugBar.createCookie('debug-bar-position', 'bottom', 365); + ciDebugBar.removeClass(ciDebugBar.icon, 'fixed-top'); + ciDebugBar.removeClass(ciDebugBar.toolbar, 'fixed-top'); + } + }, true); + }, + + setToolbarTheme: function () { + var btnTheme = this.toolbar.querySelector('#toolbar-theme'); + var isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches; + var isLightMode = window.matchMedia("(prefers-color-scheme: light)").matches; + + // If a cookie is set with a value, we force the color scheme + if (ciDebugBar.readCookie('debug-bar-theme') === 'dark') + { + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'light'); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'dark'); + } + else if (ciDebugBar.readCookie('debug-bar-theme') === 'light') + { + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); + } + + btnTheme.addEventListener('click', function () { + var theme = ciDebugBar.readCookie('debug-bar-theme'); + + if (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches) + { + // If there is no cookie, and "prefers-color-scheme" is set to "dark" + // It means that the user wants to switch to light mode + ciDebugBar.createCookie('debug-bar-theme', 'light', 365); + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); + } + else + { + if (theme === 'dark') + { + ciDebugBar.createCookie('debug-bar-theme', 'light', 365); + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); + } + else + { + // In any other cases: if there is no cookie, or the cookie is set to + // "light", or the "prefers-color-scheme" is "light"... + ciDebugBar.createCookie('debug-bar-theme', 'dark', 365); + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'light'); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'dark'); + } + } + }, true); + }, + + /** + * Helper to create a cookie. + * + * @param name + * @param value + * @param days + */ + createCookie : function (name,value,days) { + if (days) + { + var date = new Date(); + + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + + var expires = "; expires=" + date.toGMTString(); + } + else + { + var expires = ""; + } + + document.cookie = name + "=" + value + expires + "; path=/; samesite=Lax"; + }, + + readCookie : function (name) { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + + for (var i = 0; i < ca.length; i++) + { + var c = ca[i]; + while (c.charAt(0) == ' ') + { + c = c.substring(1,c.length); + } + if (c.indexOf(nameEQ) == 0) + { + return c.substring(nameEQ.length,c.length); + } + } + return null; + }, + + trimSlash: function (text) { + return text.replace(/^\/|\/$/g, ''); + }, + + routerLink: function () { + var row, _location; + var rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"]'); + var patt = /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/; + + for (var i = 0; i < rowGet.length; i++) + { + row = rowGet[i]; + if (!/\/\(.+?\)/.test(rowGet[i].innerText)) + { + row.style = 'cursor: pointer;'; + row.setAttribute('title', location.origin + '/' + ciDebugBar.trimSlash(row.innerText)); + row.addEventListener('click', function (ev) { + _location = location.origin + '/' + ciDebugBar.trimSlash(ev.target.innerText); + var redirectWindow = window.open(_location, '_blank'); + redirectWindow.location; + }); + } + else + { + row.innerHTML = '
' + row.innerText + '
' + + '
' + + row.innerText.replace(patt, '') + + '' + + '
'; + } + } + + rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"] form'); + for (var i = 0; i < rowGet.length; i++) + { + row = rowGet[i]; + + row.addEventListener('submit', function (event) { + event.preventDefault() + var inputArray = [], t = 0; + var input = event.target.querySelectorAll('input[type=text]'); + var tpl = event.target.getAttribute('data-debugbar-route-tpl'); + + for (var n = 0; n < input.length; n++) + { + if (input[n].value.length > 0) + { + inputArray.push(input[n].value); + } + } + + if (inputArray.length > 0) + { + _location = location.origin + '/' + tpl.replace(/\?/g, function () { + return inputArray[t++] + }); + + var redirectWindow = window.open(_location, '_blank'); + redirectWindow.location; + } + }) + } + } }; diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 077fc9f070ba..0e73076f0e02 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -19,250 +19,250 @@ */ ?>
-
- - 🔅 - - - - ms   MB - - +
+ + 🔅 + + + + ms   MB + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - Vars - - + + + + Vars + + -

- - - - - - -

+

+ + + + + + +

- - - - -
+ + + + +
- -
- - - - - - - - - - - - - renderTimeline($collectors, $startTime, $segmentCount, $segmentDuration, $styles) ?> - -
NAMECOMPONENTDURATION ms
-
+ +
+ + + + + + + + + + + + + renderTimeline($collectors, $startTime, $segmentCount, $segmentDuration, $styles) ?> + +
NAMECOMPONENTDURATION ms
+
- - - - -
-

+ + + + +
+

- setData($c['display'])->render("_{$c['titleSafe']}.tpl") ?> -
- - - + setData($c['display'])->render("_{$c['titleSafe']}.tpl") ?> +
+ + + - -
+ +
- - - $items) : ?> + + + $items) : ?> - -

-
+ +

+
- + - - - $value) : ?> - - - - - - -
+ + + $value) : ?> + + + + + + +
- -

No data to display.

- - - + +

No data to display.

+ + + - - -

Session User Data

-
+ + +

Session User Data

+
- - - - - $value) : ?> - - - - - - -
- -

No data to display.

- - -

Session doesn't seem to be active.

- + + + + + $value) : ?> + + + + + + +
+ +

No data to display.

+ + +

Session doesn't seem to be active.

+ -

Request ( )

+

Request ( )

- - -

$_GET

-
+ + +

$_GET

+
- - - $value) : ?> - - - - - - -
- + + + $value) : ?> + + + + + + +
+ - - -

$_POST

-
+ + +

$_POST

+
- - - $value) : ?> - - - - - - -
- + + + $value) : ?> + + + + + + +
+ - - -

Headers

-
+ + +

Headers

+
- - - $value) : ?> - - - - - - -
- + + + $value) : ?> + + + + + + +
+ - - -

Cookies

-
+ + +

Cookies

+
- - - $value) : ?> - - - - - - - - + + + $value) : ?> + + + + + + + + -

Response - ( ) -

+

Response + ( ) +

- - -

Headers

-
+ + +

Headers

+
- - - $value) : ?> - - - - - - -
- -
+ + + $value) : ?> + + + + + + +
+ +
- -
-

System Configuration

+ +
+

System Configuration

- setData($config)->render('_config.tpl') ?> -
+ setData($config)->render('_config.tpl') ?> +
+ + + +
+ +

+ +

+ +
+ + + + From 4fc4b12a9d7312bf6780da8829130fdea7618399 Mon Sep 17 00:00:00 2001 From: Stefan Bauer Date: Wed, 6 Jul 2022 07:51:29 +0200 Subject: [PATCH 0776/8282] Update production.php --- app/Views/errors/html/production.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php index 9faa4a15b783..b6964026150d 100644 --- a/app/Views/errors/html/production.php +++ b/app/Views/errors/html/production.php @@ -4,7 +4,7 @@ - Whoops! + <?= lang('Errors.whoops') ?> - - - -
- -

- -

- -
- - - - + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +// Errors language settings +return [ + 'whoops' => 'Whoops!', + 'weHitASnag' => 'We seem to have hit a snag. Please try again later...', +]; From cb351810c20ca11f755a8b5b959f9bf42449902b Mon Sep 17 00:00:00 2001 From: Stefan Bauer Date: Wed, 6 Jul 2022 12:15:10 +0200 Subject: [PATCH 0778/8282] Update production.php fix indentation --- app/Views/errors/html/production.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php index b6964026150d..2a26d767fd61 100644 --- a/app/Views/errors/html/production.php +++ b/app/Views/errors/html/production.php @@ -12,13 +12,13 @@ -
+
-

+

-

+

-
+
From ea17779800d177cad3862456cd86a593eecf0f84 Mon Sep 17 00:00:00 2001 From: Stefan Bauer Date: Wed, 6 Jul 2022 12:18:08 +0200 Subject: [PATCH 0779/8282] Update production.php remove obsolete type="text/css" attribute --- app/Views/errors/html/production.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php index 2a26d767fd61..2f59a8de1bc7 100644 --- a/app/Views/errors/html/production.php +++ b/app/Views/errors/html/production.php @@ -6,7 +6,7 @@ <?= lang('Errors.whoops') ?> - From ffa88d2fc82daa772bd86039244b37ea781fc566 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 7 Jul 2022 14:27:23 +0900 Subject: [PATCH 0780/8282] docs: add note about the status code for set404Override() --- user_guide_src/source/incoming/routing.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 4496057a5247..a92eaa7d54f9 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -519,6 +519,11 @@ a valid class/method pair, just like you would show in any route, or a Closure: .. literalinclude:: routing/051.php +.. note:: The ``set404Override()`` method does not change the Response status code to ``404``. + If you don't set the status code in the controller you set, the default status code ``200`` + will be returned. See :php:func:`Response::setStatusCode() ` for + information on how to set the status code. + Route processing by priority ============================ From 40c0e92ae2d3467d0557d3790875f306cb6b01ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:04:54 +0000 Subject: [PATCH 0781/8282] build(deps-dev): update rector/rector requirement from 0.13.7 to 0.13.8 Updates the requirements on [rector/rector](https://github.com/rectorphp/rector) to permit the latest version. - [Release notes](https://github.com/rectorphp/rector/releases) - [Commits](https://github.com/rectorphp/rector/compare/0.13.7...0.13.8) --- updated-dependencies: - dependency-name: rector/rector dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7520948f965b..8ad8b1e91a9f 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "phpstan/phpstan": "^1.7.1", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1 || ^2.0", - "rector/rector": "0.13.7" + "rector/rector": "0.13.8" }, "suggest": { "ext-fileinfo": "Improves mime type detection for files" From 85563a325988ca81d0b82a31ed52b1a795aa9692 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 8 Jul 2022 00:10:58 +0700 Subject: [PATCH 0782/8282] re-run rector for remove just variabel used for assign variable --- system/HTTP/URI.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index e0a6ff9f2386..7634b415ec7e 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -661,10 +661,8 @@ public function setAuthority(string $str) */ public function setScheme(string $str) { - $str = strtolower($str); - $str = preg_replace('#:(//)?$#', '', $str); - - $this->scheme = $str; + $str = strtolower($str); + $this->scheme = preg_replace('#:(//)?$#', '', $str); return $this; } @@ -932,8 +930,7 @@ protected function applyParts(array $parts) // Port if (isset($parts['port']) && $parts['port'] !== null) { // Valid port numbers are enforced by earlier parse_url or setPort() - $port = $parts['port']; - $this->port = $port; + $this->port = $parts['port']; } if (isset($parts['pass'])) { From ca9d244517e152d54c3da0dca2de905a06eeb42b Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 8 Jul 2022 10:25:24 +0900 Subject: [PATCH 0783/8282] test: add test case --- .../Validation/StrictRules/ValidationTest.php | 14 ++++++++++---- tests/system/Validation/ValidationTest.php | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/system/Validation/StrictRules/ValidationTest.php b/tests/system/Validation/StrictRules/ValidationTest.php index 865d3a026d9c..1e2e8a97ecc8 100644 --- a/tests/system/Validation/StrictRules/ValidationTest.php +++ b/tests/system/Validation/StrictRules/ValidationTest.php @@ -202,17 +202,23 @@ public function testRunReturnsLocalizedErrors(): void public function testRunWithCustomErrors(): void { - $data = ['foo' => 'notanumber']; - + $data = [ + 'foo' => 'notanumber', + 'bar' => 'notanumber', + ]; $messages = [ 'foo' => [ 'is_numeric' => 'Nope. Not a number.', ], + 'bar' => [ + 'is_numeric' => 'No. Not a number.', + ], ]; - - $this->validation->setRules(['foo' => 'is_numeric'], $messages); + $this->validation->setRules(['foo' => 'is_numeric', 'bar' => 'is_numeric'], $messages); $this->validation->run($data); + $this->assertSame('Nope. Not a number.', $this->validation->getError('foo')); + $this->assertSame('No. Not a number.', $this->validation->getError('bar')); } public function testCheck(): void diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index c43b7d997d88..05f07aae58de 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -305,17 +305,23 @@ public function testRunReturnsLocalizedErrors(): void public function testRunWithCustomErrors(): void { - $data = ['foo' => 'notanumber']; - + $data = [ + 'foo' => 'notanumber', + 'bar' => 'notanumber', + ]; $messages = [ 'foo' => [ 'is_numeric' => 'Nope. Not a number.', ], + 'bar' => [ + 'is_numeric' => 'No. Not a number.', + ], ]; - - $this->validation->setRules(['foo' => 'is_numeric'], $messages); + $this->validation->setRules(['foo' => 'is_numeric', 'bar' => 'is_numeric'], $messages); $this->validation->run($data); + $this->assertSame('Nope. Not a number.', $this->validation->getError('foo')); + $this->assertSame('No. Not a number.', $this->validation->getError('bar')); } public function testCheck(): void From 8baf85fa3651d8311c18d9c77711495510cf2416 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 8 Jul 2022 10:43:16 +0900 Subject: [PATCH 0784/8282] fix: custom validation error is cleared when calling setRule() twice --- system/Validation/Validation.php | 2 +- .../Validation/StrictRules/ValidationTest.php | 27 +++++++++++++++++++ tests/system/Validation/ValidationTest.php | 27 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index eb91114ca218..2b190ef2963a 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -399,7 +399,7 @@ public function setRule(string $field, ?string $label, $rules, array $errors = [ $ruleSet[$field]['errors'] = $errors; } - $this->setRules($ruleSet + $this->getRules()); + $this->setRules($ruleSet + $this->getRules(), $this->customErrors); return $this; } diff --git a/tests/system/Validation/StrictRules/ValidationTest.php b/tests/system/Validation/StrictRules/ValidationTest.php index 1e2e8a97ecc8..ba0e8c6ec0c5 100644 --- a/tests/system/Validation/StrictRules/ValidationTest.php +++ b/tests/system/Validation/StrictRules/ValidationTest.php @@ -221,6 +221,33 @@ public function testRunWithCustomErrors(): void $this->assertSame('No. Not a number.', $this->validation->getError('bar')); } + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/6239 + */ + public function testSetRuleWithCustomErrors(): void + { + $data = [ + 'foo' => 'notanumber', + 'bar' => 'notanumber', + ]; + $this->validation->setRule( + 'foo', + 'Foo', + ['foo' => 'is_numeric'], + ['is_numeric' => 'Nope. Not a number.'] + ); + $this->validation->setRule( + 'bar', + 'Bar', + ['bar' => 'is_numeric'], + ['is_numeric' => 'Nope. Not a number.'] + ); + $this->validation->run($data); + + $this->assertSame('Nope. Not a number.', $this->validation->getError('foo')); + $this->assertSame('Nope. Not a number.', $this->validation->getError('bar')); + } + public function testCheck(): void { $this->assertFalse($this->validation->check('notanumber', 'is_numeric')); diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 05f07aae58de..867dd6475d8e 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -324,6 +324,33 @@ public function testRunWithCustomErrors(): void $this->assertSame('No. Not a number.', $this->validation->getError('bar')); } + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/6239 + */ + public function testSetRuleWithCustomErrors(): void + { + $data = [ + 'foo' => 'notanumber', + 'bar' => 'notanumber', + ]; + $this->validation->setRule( + 'foo', + 'Foo', + ['foo' => 'is_numeric'], + ['is_numeric' => 'Nope. Not a number.'] + ); + $this->validation->setRule( + 'bar', + 'Bar', + ['bar' => 'is_numeric'], + ['is_numeric' => 'Nope. Not a number.'] + ); + $this->validation->run($data); + + $this->assertSame('Nope. Not a number.', $this->validation->getError('foo')); + $this->assertSame('Nope. Not a number.', $this->validation->getError('bar')); + } + public function testCheck(): void { $this->assertFalse($this->validation->check('notanumber', 'is_numeric')); From d6a2327f5a1b70c9197521b5ea6bc60d49a26e6f Mon Sep 17 00:00:00 2001 From: Andrey Pyzhikov <5071@mail.ru> Date: Fri, 8 Jul 2022 15:31:54 +0800 Subject: [PATCH 0785/8282] Fix: Validation. Leading asterisk. --- system/Validation/Validation.php | 6 ++++-- tests/system/Validation/ValidationTest.php | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index eb91114ca218..61fbb60e7bdd 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -147,7 +147,9 @@ public function run(?array $data = null, ?string $group = null, ?string $dbGroup if (strpos($field, '*') !== false) { $values = array_filter(array_flatten_with_dots($data), static fn ($key) => preg_match( - '/^' . str_replace('\.\*', '\..+', preg_quote($field, '/')) . '$/', + '/^' + . str_replace(['\.\*', '\*\.'], ['\..+', '.+\.'], preg_quote($field, '/')) + . '$/', $key ), ARRAY_FILTER_USE_KEY); // if keys not found @@ -657,7 +659,7 @@ public function getError(?string $field = null): string } $errors = array_filter($this->getErrors(), static fn ($key) => preg_match( - '/^' . str_replace('\.\*', '\..+', preg_quote($field, '/')) . '$/', + '/^' . str_replace(['\.\*', '\*\.'], ['\..+', '.+\.'], preg_quote($field, '/')) . '$/', $key ), ARRAY_FILTER_USE_KEY); diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index c43b7d997d88..530205243976 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1098,6 +1098,12 @@ public function validationArrayDataCaseProvider(): iterable ['foo' => ['boz']], ]], ]; + + yield 'leading-asterisk' => [ + true, + ['*.foo' => 'required'], + [['foo' => 'bar']], + ]; } /** @@ -1324,4 +1330,17 @@ public function testNestedArrayThrowsException(): void 'beneficiaries_accounts.account_2.purpose' => 'The PURPOSE field must be at least 3 characters in length.', ], $this->validation->getErrors()); } + + public function testRuleWithLeadingAsterisk(): void + { + $data = [ + ['foo' => 1], + ['foo' => null], + ]; + + $this->validation->setRules(['*.foo' => 'required'], ['1.foo' => ['required' => 'Required {field}']]); + + $this->assertFalse($this->validation->run($data)); + $this->assertSame('Required *.foo', $this->validation->getError('*.foo')); + } } From 770d5a1206973e97a104317540ac6ca425c8876c Mon Sep 17 00:00:00 2001 From: Andrey Pyzhikov <5071@mail.ru> Date: Fri, 8 Jul 2022 15:36:54 +0800 Subject: [PATCH 0786/8282] Fix: Validation. Leading asterisk. --- user_guide_src/source/changelogs/v4.2.2.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/changelogs/v4.2.2.rst b/user_guide_src/source/changelogs/v4.2.2.rst index 45fbeb205d54..eb457750e7c5 100644 --- a/user_guide_src/source/changelogs/v4.2.2.rst +++ b/user_guide_src/source/changelogs/v4.2.2.rst @@ -25,6 +25,7 @@ Changes ******* - Fixed: ``BaseBuilder::increment()`` and ``BaseBuilder::decrement()`` do not reset the ``BaseBuilder`` state after a query. +- Fixed: Validation of fields with a leading asterisk (wildcard). - Now ``CLIRequest::isCLI()`` always returns true. - Now ``IncommingRequest::isCLI()`` always returns false. From ed6c368e8a4a0b94fda5cf720cc7cbc2b7dd9a45 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 1 Jul 2022 16:11:52 -0700 Subject: [PATCH 0787/8282] rework SQLite Connection::_getIndexData() and add/fix tests --- system/Database/SQLite3/Connection.php | 50 +++++--- tests/system/Database/Live/ForgeTest.php | 13 +- .../Database/Live/SQLite/AlterTableTest.php | 2 +- .../Database/Live/SQLite/GetIndexDataTest.php | 121 ++++++++++++++++++ 4 files changed, 160 insertions(+), 26 deletions(-) create mode 100644 tests/system/Database/Live/SQLite/GetIndexDataTest.php diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 4f9990e1b11d..159438aae7c8 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -265,34 +265,50 @@ protected function _fieldData(string $table): array */ protected function _indexData(string $table): array { - // Get indexes - // Don't use PRAGMA index_list, so we can preserve index order - $sql = "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=" . $this->escape(strtolower($table)); + $sql = "SELECT 'PRIMARY' as indexname, l.name as fieldname, 'PRIMARY' as indextype + FROM pragma_table_info(" . $this->escape(strtolower($table)) . ") as l + WHERE l.pk <> 0 + UNION ALL + SELECT sqlite_master.name as indexname, ii.name as fieldname, + CASE + WHEN ti.pk <> 0 AND sqlite_master.name LIKE 'sqlite_autoindex_%' THEN 'PRIMARY' + WHEN sqlite_master.name LIKE 'sqlite_autoindex_%' THEN 'UNIQUE' + WHEN sqlite_master.sql LIKE '% UNIQUE %' THEN 'UNIQUE' + ELSE '' + END as indextype + FROM sqlite_master + INNER JOIN pragma_index_xinfo(sqlite_master.name) ii ON ii.name IS NOT NULL + LEFT JOIN pragma_table_info(" . $this->escape(strtolower($table)) . ") ti ON ti.name = ii.name + WHERE sqlite_master.type='index' AND sqlite_master.tbl_name = " . $this->escape(strtolower($table)) . ' COLLATE NOCASE'; + if (($query = $this->query($sql)) === false) { throw new DatabaseException(lang('Database.failGetIndexData')); } $query = $query->getResultObject(); - $retVal = []; + $tempVal = []; foreach ($query as $row) { - $obj = new stdClass(); - - $obj->name = $row->name; - - // Get fields for index - $obj->fields = []; - - if (false === $fields = $this->query('PRAGMA index_info(' . $this->escape(strtolower($row->name)) . ')')) { - throw new DatabaseException(lang('Database.failGetIndexData')); + if ($row->indextype === 'PRIMARY') { + $tempVal['PRIMARY']['indextype'] = $row->indextype; + $tempVal['PRIMARY']['indexname'] = $row->indexname; + $tempVal['PRIMARY']['fields'][$row->fieldname] = $row->fieldname; + } else { + $tempVal[$row->indexname]['indextype'] = $row->indextype; + $tempVal[$row->indexname]['indexname'] = $row->indexname; + $tempVal[$row->indexname]['fields'][$row->fieldname] = $row->fieldname; } + } - $fields = $fields->getResultObject(); + $retVal = []; - foreach ($fields as $field) { - $obj->fields[] = $field->name; - } + foreach ($tempVal as $val) { + $fields = array_values($val['fields']); + $obj = new stdClass(); + $obj->name = $val['indexname']; + $obj->fields = $fields; + $obj->type = $val['indextype']; $retVal[$obj->name] = $obj; } diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 2122afb36a45..2470faaee9ef 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -879,14 +879,11 @@ public function testAddFields() public function testCompositeKey() { - // SQLite3 uses auto increment different - $uniqueOrAuto = $this->db->DBDriver === 'SQLite3' ? 'unique' : 'auto_increment'; - $this->forge->addField([ 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 3, - $uniqueOrAuto => true, + 'type' => 'INTEGER', + 'constraint' => 3, + 'auto_increment' => true, ], 'code' => [ 'type' => 'VARCHAR', @@ -929,8 +926,8 @@ public function testCompositeKey() $this->assertSame($keys['db_forge_test_1_code_active']->fields, ['code', 'active']); $this->assertSame($keys['db_forge_test_1_code_active']->type, 'UNIQUE'); } elseif ($this->db->DBDriver === 'SQLite3') { - $this->assertSame($keys['sqlite_autoindex_db_forge_test_1_1']->name, 'sqlite_autoindex_db_forge_test_1_1'); - $this->assertSame($keys['sqlite_autoindex_db_forge_test_1_1']->fields, ['id']); + $this->assertSame($keys['PRIMARY']->name, 'PRIMARY'); + $this->assertSame($keys['PRIMARY']->fields, ['id']); $this->assertSame($keys['db_forge_test_1_code_company']->name, 'db_forge_test_1_code_company'); $this->assertSame($keys['db_forge_test_1_code_company']->fields, ['code', 'company']); $this->assertSame($keys['db_forge_test_1_code_active']->name, 'db_forge_test_1_code_active'); diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index 7eb2216139e5..83ab4586c7c9 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -107,7 +107,7 @@ public function testFromTableFillsDetails() $keys = $this->getPrivateProperty($this->table, 'keys'); - $this->assertCount(3, $keys); + $this->assertCount(4, $keys); $this->assertArrayHasKey('foo_name', $keys); $this->assertSame(['fields' => ['name'], 'type' => 'index'], $keys['foo_name']); $this->assertArrayHasKey('id', $keys); diff --git a/tests/system/Database/Live/SQLite/GetIndexDataTest.php b/tests/system/Database/Live/SQLite/GetIndexDataTest.php new file mode 100644 index 000000000000..15d1e56f831f --- /dev/null +++ b/tests/system/Database/Live/SQLite/GetIndexDataTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\Live\SQLite; + +use CodeIgniter\Database\SQLite3\Connection; +use CodeIgniter\Database\SQLite3\Forge; +use CodeIgniter\Test\CIUnitTestCase; +use CodeIgniter\Test\DatabaseTestTrait; +use Config\Database; +use stdClass; + +/** + * @group DatabaseLive + * + * @internal + */ +final class GetIndexDataTest extends CIUnitTestCase +{ + use DatabaseTestTrait; + + /** + * In setUp() db connection is changed. So migration doesn't work + * + * @var bool + */ + protected $migrate = false; + + /** + * @var Connection + */ + protected $db; + + private Forge $forge; + + protected function setUp(): void + { + parent::setUp(); + + $config = [ + 'DBDriver' => 'SQLite3', + 'database' => 'database.db', + 'DBDebug' => true, + ]; + + $this->db = db_connect($config); + $this->forge = Database::forge($config); + } + + public function testGetIndexData() + { + $this->forge->addField([ + 'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true], + 'name' => ['type' => 'VARCHAR', 'constraint' => 80], + 'email' => ['type' => 'VARCHAR', 'constraint' => 100], + 'country' => ['type' => 'VARCHAR', 'constraint' => 40], + 'created_at' => ['type' => 'DATETIME', 'null' => true], + 'updated_at' => ['type' => 'DATETIME', 'null' => true], + 'deleted_at' => ['type' => 'DATETIME', 'null' => true], + ])->addKey(['id'], true)->createTable('userforeign', true); + + // INTEGER PRIMARY KEY AUTO_INCREMENT doesn't get an index by default + $this->forge->addField([ + 'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true], + 'userid' => ['type' => 'INTEGER', 'constraint' => 3], + 'name' => ['type' => 'VARCHAR', 'constraint' => 80], + 'email' => ['type' => 'VARCHAR', 'constraint' => 100], + 'country' => ['type' => 'VARCHAR', 'constraint' => 40], + 'created_at' => ['type' => 'DATETIME', 'null' => true], + 'updated_at' => ['type' => 'DATETIME', 'null' => true], + 'deleted_at' => ['type' => 'DATETIME', 'null' => true], + ]) + ->addKey(['id'], true)->addUniqueKey('email') + ->addKey('country') + ->addForeignKey('userid', 'userforeign', 'id') + ->addForeignKey('email', 'userforeign', 'email') + ->createTable('testuser', true); + + $expectedIndexes = []; + + $row = new stdclass(); + $row->name = 'PRIMARY'; + $row->fields = ['id']; + $row->type = 'PRIMARY'; + $expectedIndexes['PRIMARY'] = $row; + + $row = new stdclass(); + $row->name = 'testuser_email'; + $row->fields = ['email']; + $row->type = 'UNIQUE'; + $expectedIndexes['testuser_email'] = $row; + + $row = new stdclass(); + $row->name = 'testuser_country'; + $row->fields = ['country']; + $row->type = ''; + $expectedIndexes['testuser_country'] = $row; + + $indexes = $this->db->getIndexData('testuser'); + + $this->assertSame($expectedIndexes['PRIMARY']->fields, $indexes['PRIMARY']->fields); + $this->assertSame($expectedIndexes['PRIMARY']->type, $indexes['PRIMARY']->type); + + $this->assertSame($expectedIndexes['testuser_email']->fields, $indexes['testuser_email']->fields); + $this->assertSame($expectedIndexes['testuser_email']->type, $indexes['testuser_email']->type); + + $this->assertSame($expectedIndexes['testuser_country']->fields, $indexes['testuser_country']->fields); + $this->assertSame($expectedIndexes['testuser_country']->type, $indexes['testuser_country']->type); + + $this->forge->dropTable('testuser', true); + $this->forge->dropTable('userforeign', true); + } +} From 0be91e23a044e44ceb101c3dd4fca7217b9e0374 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 1 Jul 2022 16:56:37 -0700 Subject: [PATCH 0788/8282] removed unused code from GetIndexDataTest --- .../Database/Live/SQLite/GetIndexDataTest.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/tests/system/Database/Live/SQLite/GetIndexDataTest.php b/tests/system/Database/Live/SQLite/GetIndexDataTest.php index 15d1e56f831f..f9498cd0301d 100644 --- a/tests/system/Database/Live/SQLite/GetIndexDataTest.php +++ b/tests/system/Database/Live/SQLite/GetIndexDataTest.php @@ -57,16 +57,6 @@ protected function setUp(): void public function testGetIndexData() { - $this->forge->addField([ - 'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true], - 'name' => ['type' => 'VARCHAR', 'constraint' => 80], - 'email' => ['type' => 'VARCHAR', 'constraint' => 100], - 'country' => ['type' => 'VARCHAR', 'constraint' => 40], - 'created_at' => ['type' => 'DATETIME', 'null' => true], - 'updated_at' => ['type' => 'DATETIME', 'null' => true], - 'deleted_at' => ['type' => 'DATETIME', 'null' => true], - ])->addKey(['id'], true)->createTable('userforeign', true); - // INTEGER PRIMARY KEY AUTO_INCREMENT doesn't get an index by default $this->forge->addField([ 'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true], @@ -78,10 +68,9 @@ public function testGetIndexData() 'updated_at' => ['type' => 'DATETIME', 'null' => true], 'deleted_at' => ['type' => 'DATETIME', 'null' => true], ]) - ->addKey(['id'], true)->addUniqueKey('email') + ->addKey(['id'], true) + ->addUniqueKey('email') ->addKey('country') - ->addForeignKey('userid', 'userforeign', 'id') - ->addForeignKey('email', 'userforeign', 'email') ->createTable('testuser', true); $expectedIndexes = []; From b25b29dd878571d54316f2c5dcc2e16024b1c810 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 1 Jul 2022 19:52:52 -0700 Subject: [PATCH 0789/8282] fix --- system/Database/SQLite3/Connection.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 159438aae7c8..c59963f7c843 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -303,11 +303,9 @@ protected function _indexData(string $table): array $retVal = []; foreach ($tempVal as $val) { - $fields = array_values($val['fields']); - $obj = new stdClass(); $obj->name = $val['indexname']; - $obj->fields = $fields; + $obj->fields = array_values($val['fields']); $obj->type = $val['indextype']; $retVal[$obj->name] = $obj; } From 9a7c4c1ef812311da9e22a84ae3e912afc777439 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 5 Jul 2022 15:50:42 -0700 Subject: [PATCH 0790/8282] fix alter table test and SQLite Table - deal with psudo index PRIMARY --- system/Database/SQLite3/Connection.php | 2 +- system/Database/SQLite3/Table.php | 15 +++++++++++---- tests/system/Database/Live/ForgeTest.php | 11 +++++++++++ .../Database/Live/SQLite/AlterTableTest.php | 10 +++++----- .../Database/Live/SQLite/GetIndexDataTest.php | 3 +-- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index c59963f7c843..24918cb7be97 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -274,7 +274,7 @@ protected function _indexData(string $table): array WHEN ti.pk <> 0 AND sqlite_master.name LIKE 'sqlite_autoindex_%' THEN 'PRIMARY' WHEN sqlite_master.name LIKE 'sqlite_autoindex_%' THEN 'UNIQUE' WHEN sqlite_master.sql LIKE '% UNIQUE %' THEN 'UNIQUE' - ELSE '' + ELSE 'INDEX' END as indextype FROM sqlite_master INNER JOIN pragma_index_xinfo(sqlite_master.name) ii ON ii.name IS NOT NULL diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 1ecb28fdf8b7..3eb5437c6a44 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -110,6 +110,13 @@ public function fromTable(string $table) $this->keys = array_merge($this->keys, $this->formatKeys($this->db->getIndexData($table))); + // if primary key index exists twice then remove psuedo index name 'primary'. + $primaryIndexes = array_filter($this->keys, static fn ($index) => $index['type'] === 'primary'); + + if (! empty($primaryIndexes) && count($primaryIndexes) > 1 && array_key_exists('primary', $this->keys)) { + unset($this->keys['primary']); + } + $this->foreignKeys = $this->db->getForeignKeyData($table); return $this; @@ -316,7 +323,7 @@ protected function formatFields($fields) ]; if ($field->primary_key) { - $this->keys[$field->name] = [ + $this->keys['primary'] = [ 'fields' => [$field->name], 'type' => 'primary', ]; @@ -343,9 +350,9 @@ protected function formatKeys($keys) $return = []; foreach ($keys as $name => $key) { - $return[$name] = [ + $return[strtolower($name)] = [ 'fields' => $key->fields, - 'type' => 'index', + 'type' => strtolower($key->type), ]; } @@ -363,7 +370,7 @@ protected function dropIndexes() } foreach ($this->keys as $name => $key) { - if ($key['type'] === 'primary' || $key['type'] === 'unique') { + if ($name === 'primary') { continue; } diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 2470faaee9ef..6eacb6ba176c 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -909,9 +909,11 @@ public function testCompositeKey() $this->assertSame($keys['PRIMARY']->name, 'PRIMARY'); $this->assertSame($keys['PRIMARY']->fields, ['id']); $this->assertSame($keys['PRIMARY']->type, 'PRIMARY'); + $this->assertSame($keys['code_company']->name, 'code_company'); $this->assertSame($keys['code_company']->fields, ['code', 'company']); $this->assertSame($keys['code_company']->type, 'INDEX'); + $this->assertSame($keys['code_active']->name, 'code_active'); $this->assertSame($keys['code_active']->fields, ['code', 'active']); $this->assertSame($keys['code_active']->type, 'UNIQUE'); @@ -919,19 +921,26 @@ public function testCompositeKey() $this->assertSame($keys['pk_db_forge_test_1']->name, 'pk_db_forge_test_1'); $this->assertSame($keys['pk_db_forge_test_1']->fields, ['id']); $this->assertSame($keys['pk_db_forge_test_1']->type, 'PRIMARY'); + $this->assertSame($keys['db_forge_test_1_code_company']->name, 'db_forge_test_1_code_company'); $this->assertSame($keys['db_forge_test_1_code_company']->fields, ['code', 'company']); $this->assertSame($keys['db_forge_test_1_code_company']->type, 'INDEX'); + $this->assertSame($keys['db_forge_test_1_code_active']->name, 'db_forge_test_1_code_active'); $this->assertSame($keys['db_forge_test_1_code_active']->fields, ['code', 'active']); $this->assertSame($keys['db_forge_test_1_code_active']->type, 'UNIQUE'); } elseif ($this->db->DBDriver === 'SQLite3') { $this->assertSame($keys['PRIMARY']->name, 'PRIMARY'); $this->assertSame($keys['PRIMARY']->fields, ['id']); + $this->assertSame($keys['PRIMARY']->type, 'PRIMARY'); + $this->assertSame($keys['db_forge_test_1_code_company']->name, 'db_forge_test_1_code_company'); $this->assertSame($keys['db_forge_test_1_code_company']->fields, ['code', 'company']); + $this->assertSame($keys['db_forge_test_1_code_company']->type, 'INDEX'); + $this->assertSame($keys['db_forge_test_1_code_active']->name, 'db_forge_test_1_code_active'); $this->assertSame($keys['db_forge_test_1_code_active']->fields, ['code', 'active']); + $this->assertSame($keys['db_forge_test_1_code_active']->type, 'UNIQUE'); } elseif ($this->db->DBDriver === 'SQLSRV') { $this->assertSame($keys['pk_db_forge_test_1']->name, 'pk_db_forge_test_1'); $this->assertSame($keys['pk_db_forge_test_1']->fields, ['id']); @@ -948,9 +957,11 @@ public function testCompositeKey() $this->assertSame($keys['pk_db_forge_test_1']->name, 'pk_db_forge_test_1'); $this->assertSame($keys['pk_db_forge_test_1']->fields, ['id']); $this->assertSame($keys['pk_db_forge_test_1']->type, 'PRIMARY'); + $this->assertSame($keys['db_forge_test_1_code_company']->name, 'db_forge_test_1_code_company'); $this->assertSame($keys['db_forge_test_1_code_company']->fields, ['code', 'company']); $this->assertSame($keys['db_forge_test_1_code_company']->type, 'INDEX'); + $this->assertSame($keys['db_forge_test_1_code_active']->name, 'db_forge_test_1_code_active'); $this->assertSame($keys['db_forge_test_1_code_active']->fields, ['code', 'active']); $this->assertSame($keys['db_forge_test_1_code_active']->type, 'UNIQUE'); diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index 83ab4586c7c9..ce1055d04c2b 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -107,13 +107,13 @@ public function testFromTableFillsDetails() $keys = $this->getPrivateProperty($this->table, 'keys'); - $this->assertCount(4, $keys); + $this->assertCount(3, $keys); $this->assertArrayHasKey('foo_name', $keys); $this->assertSame(['fields' => ['name'], 'type' => 'index'], $keys['foo_name']); - $this->assertArrayHasKey('id', $keys); - $this->assertSame(['fields' => ['id'], 'type' => 'primary'], $keys['id']); - $this->assertArrayHasKey('id', $keys); - $this->assertSame(['fields' => ['id'], 'type' => 'primary'], $keys['id']); + $this->assertArrayHasKey('foo_email', $keys); + $this->assertSame(['fields' => ['email'], 'type' => 'unique'], $keys['foo_email']); + $this->assertArrayHasKey('primary', $keys); + $this->assertSame(['fields' => ['id'], 'type' => 'primary'], $keys['primary']); } public function testDropColumnSuccess() diff --git a/tests/system/Database/Live/SQLite/GetIndexDataTest.php b/tests/system/Database/Live/SQLite/GetIndexDataTest.php index f9498cd0301d..74074b17b823 100644 --- a/tests/system/Database/Live/SQLite/GetIndexDataTest.php +++ b/tests/system/Database/Live/SQLite/GetIndexDataTest.php @@ -90,7 +90,7 @@ public function testGetIndexData() $row = new stdclass(); $row->name = 'testuser_country'; $row->fields = ['country']; - $row->type = ''; + $row->type = 'INDEX'; $expectedIndexes['testuser_country'] = $row; $indexes = $this->db->getIndexData('testuser'); @@ -105,6 +105,5 @@ public function testGetIndexData() $this->assertSame($expectedIndexes['testuser_country']->type, $indexes['testuser_country']->type); $this->forge->dropTable('testuser', true); - $this->forge->dropTable('userforeign', true); } } From def6e1f3146d65e9ec56e3b723f3f445ee905c7b Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 5 Jul 2022 16:28:41 -0700 Subject: [PATCH 0791/8282] fix cs --- system/Database/SQLite3/Table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 3eb5437c6a44..6c3328557578 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -369,7 +369,7 @@ protected function dropIndexes() return; } - foreach ($this->keys as $name => $key) { + foreach (array_keys($this->keys) as $name) { if ($name === 'primary') { continue; } From 60ce81d94338690ba87e85865f7827b78990af9d Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 16 Jun 2022 15:35:33 +0900 Subject: [PATCH 0792/8282] feat: add class ContentReplacer --- system/Publisher/ContentReplacer.php | 87 ++++++++ .../system/Publisher/ContentReplacerTest.php | 200 ++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 system/Publisher/ContentReplacer.php create mode 100644 tests/system/Publisher/ContentReplacerTest.php diff --git a/system/Publisher/ContentReplacer.php b/system/Publisher/ContentReplacer.php new file mode 100644 index 000000000000..4c31b604c89e --- /dev/null +++ b/system/Publisher/ContentReplacer.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Publisher; + +class ContentReplacer +{ + /** + * Replace content + * + * @param array $replaces [search => replace] + */ + public function replace(string $content, array $replaces): string + { + return strtr($content, $replaces); + } + + /** + * Add text + * + * @param string $text Text to add. + * @param string $pattern Regexp search pattern. + * @param string $replace Regexp replacement including text to add. + * + * @return bool|string true: already updated, false: regexp error. + */ + public function add(string $content, string $text, string $pattern, string $replace) + { + $return = preg_match('/' . preg_quote($text, '/') . '/u', $content); + + if ($return === 1) { + // It has already been updated. + + return true; + } + + if ($return === false) { + // Regexp error. + + return false; + } + + return preg_replace($pattern, $replace, $content); + } + + /** + * Add line after the line with the string + * + * @param string $content Whole content. + * @param string $line Line to add. + * @param string $after String to search. + * + * @return bool|string true: already updated, false: regexp error. + */ + public function addAfter(string $content, string $line, string $after) + { + $pattern = '/(.*)(\n[^\n]*?' . preg_quote($after, '/') . '[^\n]*?\n)/su'; + $replace = '$1$2' . $line . "\n"; + + return $this->add($content, $line, $pattern, $replace); + } + + /** + * Add line before the line with the string + * + * @param string $content Whole content. + * @param string $line Line to add. + * @param string $before String to search. + * + * @return bool|string true: already updated, false: regexp error. + */ + public function addBefore(string $content, string $line, string $before) + { + $pattern = '/(\n)([^\n]*?' . preg_quote($before, '/') . ')(.*)/su'; + $replace = '$1' . $line . "\n" . '$2$3'; + + return $this->add($content, $line, $pattern, $replace); + } +} diff --git a/tests/system/Publisher/ContentReplacerTest.php b/tests/system/Publisher/ContentReplacerTest.php new file mode 100644 index 000000000000..00875c59825d --- /dev/null +++ b/tests/system/Publisher/ContentReplacerTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Publisher; + +use CodeIgniter\Test\CIUnitTestCase; + +/** + * @internal + */ +final class ContentReplacerTest extends CIUnitTestCase +{ + public function testReplace(): void + { + $replacer = new ContentReplacer(); + $content = <<<'FILE' + 'namespace Config', + "use CodeIgniter\\Config\\BaseConfig;\n" => '', + 'extends BaseConfig' => 'extends \\CodeIgniter\\Shield\\Config\\Auth', + ]; + $output = $replacer->replace($content, $replaces); + + $expected = <<<'FILE' + assertSame($expected, $output); + } + + public function testAddAddingAfter(): void + { + $replacer = new ContentReplacer(); + $content = <<<'FILE' + get('/', 'Home::index'); + + /** + * You will have access to the $routes object within that file without + * needing to reload it. + */ + + FILE; + + $text = 'service(\'auth\')->routes($routes);'; + $pattern = '/(.*)(\n' . preg_quote('$routes->', '/') . '[^\n]+?\n)/su'; + $replace = '$1$2' . "\n" . $text . "\n"; + $output = $replacer->add($content, $text, $pattern, $replace); + + $expected = <<<'FILE' + get('/', 'Home::index'); + + service('auth')->routes($routes); + + /** + * You will have access to the $routes object within that file without + * needing to reload it. + */ + + FILE; + $this->assertSame($expected, $output); + } + + public function testAddAddingBefore(): void + { + $replacer = new ContentReplacer(); + $content = <<<'FILE' + helpers = array_merge($this->helpers, [\'auth\', \'setting\']);'; + $pattern = '/(' . preg_quote('// Do Not Edit This Line', '/') . ')/u'; + $replace = $text . "\n\n " . '$1'; + $output = $replacer->add($content, $text, $pattern, $replace); + + $expected = <<<'FILE' + helpers = array_merge($this->helpers, ['auth', 'setting']); + + // Do Not Edit This Line + parent::initController($request, $response, $logger); + } + } + FILE; + $this->assertSame($expected, $output); + } + + public function testAddAfter(): void + { + $replacer = new ContentReplacer(); + $content = <<<'FILE' + $routes->get('/', 'Home::index'); + $routes->get('/login', 'Login::index'); + + FILE; + + $line = "\n" . 'service(\'auth\')->routes($routes);'; + $after = '$routes->'; + $output = $replacer->addAfter($content, $line, $after); + + $expected = <<<'FILE' + $routes->get('/', 'Home::index'); + $routes->get('/login', 'Login::index'); + + service('auth')->routes($routes); + + FILE; + $this->assertSame($expected, $output); + } + + public function testAddBefore(): void + { + $replacer = new ContentReplacer(); + $content = <<<'FILE' + helpers = array_merge($this->helpers, [\'auth\', \'setting\']);'; + $before = '// Do Not Edit This Line'; + $output = $replacer->addBefore($content, $line, $before); + + $expected = <<<'FILE' + helpers = array_merge($this->helpers, ['auth', 'setting']); + // Do Not Edit This Line + parent::initController($request, $response, $logger); + // Do Not Edit This Line + + FILE; + $this->assertSame($expected, $output); + } +} From 362a56262e46df31578a23fe43e87f39f68910f9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 16 Jun 2022 18:03:34 +0900 Subject: [PATCH 0793/8282] feat: add Publisher::replace(), addLineAfter(), addLineBefore() --- system/Publisher/Publisher.php | 85 +++++++++++++++- .../Publisher/PublisherContentReplaceTest.php | 97 +++++++++++++++++++ 2 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 tests/system/Publisher/PublisherContentReplaceTest.php diff --git a/system/Publisher/Publisher.php b/system/Publisher/Publisher.php index 01a3c62db657..6ab147b4f8b7 100644 --- a/system/Publisher/Publisher.php +++ b/system/Publisher/Publisher.php @@ -71,6 +71,8 @@ class Publisher extends FileCollection */ private array $restrictions; + private ContentReplacer $replacer; + /** * Base path to use for the source. * @@ -157,6 +159,8 @@ public function __construct(?string $source = null, ?string $destination = null) $this->source = self::resolveDirectory($source ?? $this->source); $this->destination = self::resolveDirectory($destination ?? $this->destination); + $this->replacer = new ContentReplacer(); + // Restrictions are intentionally not injected to prevent overriding $this->restrictions = config('Publisher')->restrictions; @@ -397,12 +401,73 @@ final public function merge(bool $replace = true): bool } /** - * Copies a file with directory creation and identical file awareness. - * Intentionally allows errors. + * Replace content * - * @throws PublisherException For collisions and restriction violations + * @param array $replaces [search => replace] + * + * @return bool */ - private function safeCopyFile(string $from, string $to, bool $replace): void + public function replace(string $file, array $replaces) + { + $this->verifyAllowed($file, $file); + + $content = file_get_contents($file); + + $newContent = $this->replacer->replace($content, $replaces); + + $return = file_put_contents($file, $newContent); + + return $return !== false; + } + + /** + * Add line after the line with the string + * + * @param string $after String to search. + */ + public function addLineAfter(string $file, string $line, string $after): bool + { + $this->verifyAllowed($file, $file); + + $content = file_get_contents($file); + + $newContent = $this->replacer->addAfter($content, $line, $after); + + if (is_bool($newContent)) { + return $newContent; + } + + $return = file_put_contents($file, $newContent); + + return $return !== false; + } + + /** + * Add line before the line with the string + * + * @param string $before String to search. + */ + public function addLineBefore(string $file, string $line, string $before): bool + { + $this->verifyAllowed($file, $file); + + $content = file_get_contents($file); + + $newContent = $this->replacer->addBefore($content, $line, $before); + + if (is_bool($newContent)) { + return $newContent; + } + + $return = file_put_contents($file, $newContent); + + return $return !== false; + } + + /** + * Verify this is an allowed file for its destination. + */ + private function verifyAllowed(string $from, string $to) { // Verify this is an allowed file for its destination foreach ($this->restrictions as $directory => $pattern) { @@ -410,6 +475,18 @@ private function safeCopyFile(string $from, string $to, bool $replace): void throw PublisherException::forFileNotAllowed($from, $directory, $pattern); } } + } + + /** + * Copies a file with directory creation and identical file awareness. + * Intentionally allows errors. + * + * @throws PublisherException For collisions and restriction violations + */ + private function safeCopyFile(string $from, string $to, bool $replace): void + { + // Verify this is an allowed file for its destination + $this->verifyAllowed($from, $to); // Check for an existing file if (file_exists($to)) { diff --git a/tests/system/Publisher/PublisherContentReplaceTest.php b/tests/system/Publisher/PublisherContentReplaceTest.php new file mode 100644 index 000000000000..f265bc4c14d4 --- /dev/null +++ b/tests/system/Publisher/PublisherContentReplaceTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Publisher; + +use CodeIgniter\Test\CIUnitTestCase; + +/** + * @internal + */ +final class PublisherContentReplaceTest extends CIUnitTestCase +{ + private string $file; + private Publisher $publisher; + + protected function setUp(): void + { + parent::setUp(); + + $this->file = __DIR__ . '/App.php'; + copy(APPPATH . 'Config/App.php', $this->file); + + $this->publisher = new Publisher(__DIR__, __DIR__); + } + + protected function tearDown(): void + { + parent::tearDown(); + + unlink($this->file); + } + + public function testAddLineAfter() + { + $result = $this->publisher->addLineAfter( + $this->file, + ' public $myOwnConfig = 1000;', + 'public $CSPEnabled = false;' + ); + + $this->assertTrue($result); + $this->assertStringContainsString( + ' public $CSPEnabled = false; + public $myOwnConfig = 1000;', + file_get_contents($this->file) + ); + } + + public function testAddLineBefore() + { + $result = $this->publisher->addLineBefore( + $this->file, + ' public $myOwnConfig = 1000;', + 'public $CSPEnabled = false;' + ); + + $this->assertTrue($result); + $this->assertStringContainsString( + ' public $myOwnConfig = 1000; + public $CSPEnabled = false;', + file_get_contents($this->file) + ); + } + + public function testReplace() + { + $result = $this->publisher->replace( + $this->file, + [ + 'use CodeIgniter\Config\BaseConfig;' . "\n" => '', + 'class App extends BaseConfig' => 'class App extends \Some\Package\SomeConfig', + ] + ); + + $this->assertTrue($result); + $this->assertStringNotContainsString( + 'use CodeIgniter\Config\BaseConfig;', + file_get_contents($this->file) + ); + $this->assertStringContainsString( + 'class App extends \Some\Package\SomeConfig', + file_get_contents($this->file) + ); + $this->assertStringNotContainsString( + 'class App extends BaseConfig', + file_get_contents($this->file) + ); + } +} From 003e64d68d798319107be13ae69b619881ec6471 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 19 Jun 2022 07:46:46 +0900 Subject: [PATCH 0794/8282] refactor: change visibility Co-authored-by: MGatner --- system/Publisher/ContentReplacer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Publisher/ContentReplacer.php b/system/Publisher/ContentReplacer.php index 4c31b604c89e..367d901a76b5 100644 --- a/system/Publisher/ContentReplacer.php +++ b/system/Publisher/ContentReplacer.php @@ -32,7 +32,7 @@ public function replace(string $content, array $replaces): string * * @return bool|string true: already updated, false: regexp error. */ - public function add(string $content, string $text, string $pattern, string $replace) + private function add(string $content, string $text, string $pattern, string $replace) { $return = preg_match('/' . preg_quote($text, '/') . '/u', $content); From 1dc06ec87b6910c7db6017d5e3a3e238f84ef021 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 19 Jun 2022 08:43:21 +0900 Subject: [PATCH 0795/8282] feat: change return type of ContentReplacer::add() --- system/Publisher/ContentReplacer.php | 38 ++++-- system/Publisher/Publisher.php | 24 ++-- .../system/Publisher/ContentReplacerTest.php | 118 ++++++------------ 3 files changed, 80 insertions(+), 100 deletions(-) diff --git a/system/Publisher/ContentReplacer.php b/system/Publisher/ContentReplacer.php index 367d901a76b5..05d75457ca0a 100644 --- a/system/Publisher/ContentReplacer.php +++ b/system/Publisher/ContentReplacer.php @@ -11,6 +11,9 @@ namespace CodeIgniter\Publisher; +use RuntimeException; +use stdClass; + class ContentReplacer { /** @@ -30,25 +33,38 @@ public function replace(string $content, array $replaces): string * @param string $pattern Regexp search pattern. * @param string $replace Regexp replacement including text to add. * - * @return bool|string true: already updated, false: regexp error. + * @return stdClass {updated: bool, content: string} */ - private function add(string $content, string $text, string $pattern, string $replace) + private function add(string $content, string $text, string $pattern, string $replace): stdClass { + $result = new stdClass(); + $return = preg_match('/' . preg_quote($text, '/') . '/u', $content); + if ($return === false) { + // Regexp error. + throw new RuntimeException(preg_last_error_msg()); + } + if ($return === 1) { // It has already been updated. + $result->updated = false; + $result->content = $content; - return true; + return $result; } - if ($return === false) { - // Regexp error. + $return = preg_replace($pattern, $replace, $content); - return false; + if ($return === null) { + // Regexp error. + throw new RuntimeException(preg_last_error_msg()); } - return preg_replace($pattern, $replace, $content); + $result->updated = true; + $result->content = $return; + + return $result; } /** @@ -58,9 +74,9 @@ private function add(string $content, string $text, string $pattern, string $rep * @param string $line Line to add. * @param string $after String to search. * - * @return bool|string true: already updated, false: regexp error. + * @return stdClass {updated: bool, content: string} */ - public function addAfter(string $content, string $line, string $after) + public function addAfter(string $content, string $line, string $after): stdClass { $pattern = '/(.*)(\n[^\n]*?' . preg_quote($after, '/') . '[^\n]*?\n)/su'; $replace = '$1$2' . $line . "\n"; @@ -75,9 +91,9 @@ public function addAfter(string $content, string $line, string $after) * @param string $line Line to add. * @param string $before String to search. * - * @return bool|string true: already updated, false: regexp error. + * @return stdClass {updated: bool, content: string} */ - public function addBefore(string $content, string $line, string $before) + public function addBefore(string $content, string $line, string $before): stdClass { $pattern = '/(\n)([^\n]*?' . preg_quote($before, '/') . ')(.*)/su'; $replace = '$1' . $line . "\n" . '$2$3'; diff --git a/system/Publisher/Publisher.php b/system/Publisher/Publisher.php index 6ab147b4f8b7..23179da9abd2 100644 --- a/system/Publisher/Publisher.php +++ b/system/Publisher/Publisher.php @@ -431,15 +431,15 @@ public function addLineAfter(string $file, string $line, string $after): bool $content = file_get_contents($file); - $newContent = $this->replacer->addAfter($content, $line, $after); + $result = $this->replacer->addAfter($content, $line, $after); - if (is_bool($newContent)) { - return $newContent; - } + if ($result->updated) { + $return = file_put_contents($file, $result->content); - $return = file_put_contents($file, $newContent); + return $return !== false; + } - return $return !== false; + return false; } /** @@ -453,15 +453,15 @@ public function addLineBefore(string $file, string $line, string $before): bool $content = file_get_contents($file); - $newContent = $this->replacer->addBefore($content, $line, $before); + $result = $this->replacer->addBefore($content, $line, $before); - if (is_bool($newContent)) { - return $newContent; - } + if ($result->updated) { + $return = file_put_contents($file, $result->content); - $return = file_put_contents($file, $newContent); + return $return !== false; + } - return $return !== false; + return false; } /** diff --git a/tests/system/Publisher/ContentReplacerTest.php b/tests/system/Publisher/ContentReplacerTest.php index 00875c59825d..0274eb56a4a3 100644 --- a/tests/system/Publisher/ContentReplacerTest.php +++ b/tests/system/Publisher/ContentReplacerTest.php @@ -53,129 +53,92 @@ class Auth extends \CodeIgniter\Shield\Config\Auth $this->assertSame($expected, $output); } - public function testAddAddingAfter(): void + public function testAddAfter(): void { $replacer = new ContentReplacer(); $content = <<<'FILE' - get('/', 'Home::index'); - - /** - * You will have access to the $routes object within that file without - * needing to reload it. - */ + $routes->get('/login', 'Login::index'); FILE; - $text = 'service(\'auth\')->routes($routes);'; - $pattern = '/(.*)(\n' . preg_quote('$routes->', '/') . '[^\n]+?\n)/su'; - $replace = '$1$2' . "\n" . $text . "\n"; - $output = $replacer->add($content, $text, $pattern, $replace); + $line = "\n" . 'service(\'auth\')->routes($routes);'; + $after = '$routes->'; + $result = $replacer->addAfter($content, $line, $after); $expected = <<<'FILE' - get('/', 'Home::index'); + $routes->get('/login', 'Login::index'); service('auth')->routes($routes); - /** - * You will have access to the $routes object within that file without - * needing to reload it. - */ - FILE; - $this->assertSame($expected, $output); + $this->assertSame($expected, $result->content); + $this->assertTrue($result->updated); } - public function testAddAddingBefore(): void + public function testAddAfterAlreadyUpdated(): void { $replacer = new ContentReplacer(); $content = <<<'FILE' - get('/', 'Home::index'); + $routes->get('/login', 'Login::index'); - namespace App\Controllers; + service('auth')->routes($routes); - abstract class BaseController extends Controller - { - public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) - { - // Do Not Edit This Line - parent::initController($request, $response, $logger); - } - } FILE; - $text = '$this->helpers = array_merge($this->helpers, [\'auth\', \'setting\']);'; - $pattern = '/(' . preg_quote('// Do Not Edit This Line', '/') . ')/u'; - $replace = $text . "\n\n " . '$1'; - $output = $replacer->add($content, $text, $pattern, $replace); + $line = "\n" . 'service(\'auth\')->routes($routes);'; + $after = '$routes->'; + $result = $replacer->addAfter($content, $line, $after); $expected = <<<'FILE' - get('/', 'Home::index'); + $routes->get('/login', 'Login::index'); - namespace App\Controllers; + service('auth')->routes($routes); - abstract class BaseController extends Controller - { - public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) - { - $this->helpers = array_merge($this->helpers, ['auth', 'setting']); - - // Do Not Edit This Line - parent::initController($request, $response, $logger); - } - } FILE; - $this->assertSame($expected, $output); + $this->assertSame($expected, $result->content); + $this->assertFalse($result->updated); } - public function testAddAfter(): void + public function testAddBefore(): void { $replacer = new ContentReplacer(); $content = <<<'FILE' - $routes->get('/', 'Home::index'); - $routes->get('/login', 'Login::index'); + routes($routes);'; - $after = '$routes->'; - $output = $replacer->addAfter($content, $line, $after); + $line = '$this->helpers = array_merge($this->helpers, [\'auth\', \'setting\']);'; + $before = '// Do Not Edit This Line'; + $result = $replacer->addBefore($content, $line, $before); $expected = <<<'FILE' - $routes->get('/', 'Home::index'); - $routes->get('/login', 'Login::index'); + routes($routes); + $this->helpers = array_merge($this->helpers, ['auth', 'setting']); + // Do Not Edit This Line + parent::initController($request, $response, $logger); + // Do Not Edit This Line FILE; - $this->assertSame($expected, $output); + $this->assertSame($expected, $result->content); + $this->assertTrue($result->updated); } - public function testAddBefore(): void + public function testAddBeforeAlreadyUpdated(): void { $replacer = new ContentReplacer(); $content = <<<'FILE' helpers = array_merge($this->helpers, ['auth', 'setting']); // Do Not Edit This Line parent::initController($request, $response, $logger); // Do Not Edit This Line @@ -184,7 +147,7 @@ public function testAddBefore(): void $line = '$this->helpers = array_merge($this->helpers, [\'auth\', \'setting\']);'; $before = '// Do Not Edit This Line'; - $output = $replacer->addBefore($content, $line, $before); + $result = $replacer->addBefore($content, $line, $before); $expected = <<<'FILE' assertSame($expected, $output); + $this->assertSame($expected, $result->content); + $this->assertFalse($result->updated); } } From 830d0c0003a700742778ff9118f97751a762cc6c Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 10 Jul 2022 11:35:20 +0900 Subject: [PATCH 0796/8282] refactor: preg_last_error_msg() requires PHP8 or later --- system/Publisher/ContentReplacer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Publisher/ContentReplacer.php b/system/Publisher/ContentReplacer.php index 05d75457ca0a..eca0c7ac55a6 100644 --- a/system/Publisher/ContentReplacer.php +++ b/system/Publisher/ContentReplacer.php @@ -43,7 +43,7 @@ private function add(string $content, string $text, string $pattern, string $rep if ($return === false) { // Regexp error. - throw new RuntimeException(preg_last_error_msg()); + throw new RuntimeException('Regex error. PCRE error code: ' . preg_last_error()); } if ($return === 1) { @@ -58,7 +58,7 @@ private function add(string $content, string $text, string $pattern, string $rep if ($return === null) { // Regexp error. - throw new RuntimeException(preg_last_error_msg()); + throw new RuntimeException('Regex error. PCRE error code: ' . preg_last_error()); } $result->updated = true; From ca91e22929040d9e7721b023cc968f2ecb44b2a3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 10 Jul 2022 11:58:26 +0900 Subject: [PATCH 0797/8282] test: update assertions Config\App has been changed. --- .../Publisher/PublisherContentReplaceTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/system/Publisher/PublisherContentReplaceTest.php b/tests/system/Publisher/PublisherContentReplaceTest.php index f265bc4c14d4..ab6748027f17 100644 --- a/tests/system/Publisher/PublisherContentReplaceTest.php +++ b/tests/system/Publisher/PublisherContentReplaceTest.php @@ -42,14 +42,14 @@ public function testAddLineAfter() { $result = $this->publisher->addLineAfter( $this->file, - ' public $myOwnConfig = 1000;', - 'public $CSPEnabled = false;' + ' public int $myOwnConfig = 1000;', + 'public bool $CSPEnabled = false;' ); $this->assertTrue($result); $this->assertStringContainsString( - ' public $CSPEnabled = false; - public $myOwnConfig = 1000;', + ' public bool $CSPEnabled = false; + public int $myOwnConfig = 1000;', file_get_contents($this->file) ); } @@ -58,14 +58,14 @@ public function testAddLineBefore() { $result = $this->publisher->addLineBefore( $this->file, - ' public $myOwnConfig = 1000;', - 'public $CSPEnabled = false;' + ' public int $myOwnConfig = 1000;', + 'public bool $CSPEnabled = false;' ); $this->assertTrue($result); $this->assertStringContainsString( - ' public $myOwnConfig = 1000; - public $CSPEnabled = false;', + ' public int $myOwnConfig = 1000; + public bool $CSPEnabled = false;', file_get_contents($this->file) ); } From 6ae1dbcca1c15a15c934183ebd7c25d3548cf604 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 11 Jul 2022 11:10:59 +0900 Subject: [PATCH 0798/8282] refactor: remove stdClass result object Using stdClass is not good practice. --- system/Publisher/ContentReplacer.php | 25 +++++----------- system/Publisher/Publisher.php | 8 ++--- .../system/Publisher/ContentReplacerTest.php | 30 +++---------------- 3 files changed, 16 insertions(+), 47 deletions(-) diff --git a/system/Publisher/ContentReplacer.php b/system/Publisher/ContentReplacer.php index eca0c7ac55a6..2372e850b080 100644 --- a/system/Publisher/ContentReplacer.php +++ b/system/Publisher/ContentReplacer.php @@ -12,7 +12,6 @@ namespace CodeIgniter\Publisher; use RuntimeException; -use stdClass; class ContentReplacer { @@ -33,12 +32,10 @@ public function replace(string $content, array $replaces): string * @param string $pattern Regexp search pattern. * @param string $replace Regexp replacement including text to add. * - * @return stdClass {updated: bool, content: string} + * @return string|null Updated content, or null if not updated. */ - private function add(string $content, string $text, string $pattern, string $replace): stdClass + private function add(string $content, string $text, string $pattern, string $replace): ?string { - $result = new stdClass(); - $return = preg_match('/' . preg_quote($text, '/') . '/u', $content); if ($return === false) { @@ -48,10 +45,7 @@ private function add(string $content, string $text, string $pattern, string $rep if ($return === 1) { // It has already been updated. - $result->updated = false; - $result->content = $content; - - return $result; + return null; } $return = preg_replace($pattern, $replace, $content); @@ -61,10 +55,7 @@ private function add(string $content, string $text, string $pattern, string $rep throw new RuntimeException('Regex error. PCRE error code: ' . preg_last_error()); } - $result->updated = true; - $result->content = $return; - - return $result; + return $return; } /** @@ -74,9 +65,9 @@ private function add(string $content, string $text, string $pattern, string $rep * @param string $line Line to add. * @param string $after String to search. * - * @return stdClass {updated: bool, content: string} + * @return string|null Updated content, or null if not updated. */ - public function addAfter(string $content, string $line, string $after): stdClass + public function addAfter(string $content, string $line, string $after): ?string { $pattern = '/(.*)(\n[^\n]*?' . preg_quote($after, '/') . '[^\n]*?\n)/su'; $replace = '$1$2' . $line . "\n"; @@ -91,9 +82,9 @@ public function addAfter(string $content, string $line, string $after): stdClass * @param string $line Line to add. * @param string $before String to search. * - * @return stdClass {updated: bool, content: string} + * @return string|null Updated content, or null if not updated. */ - public function addBefore(string $content, string $line, string $before): stdClass + public function addBefore(string $content, string $line, string $before): ?string { $pattern = '/(\n)([^\n]*?' . preg_quote($before, '/') . ')(.*)/su'; $replace = '$1' . $line . "\n" . '$2$3'; diff --git a/system/Publisher/Publisher.php b/system/Publisher/Publisher.php index 23179da9abd2..920650fe9351 100644 --- a/system/Publisher/Publisher.php +++ b/system/Publisher/Publisher.php @@ -433,8 +433,8 @@ public function addLineAfter(string $file, string $line, string $after): bool $result = $this->replacer->addAfter($content, $line, $after); - if ($result->updated) { - $return = file_put_contents($file, $result->content); + if ($result !== null) { + $return = file_put_contents($file, $result); return $return !== false; } @@ -455,8 +455,8 @@ public function addLineBefore(string $file, string $line, string $before): bool $result = $this->replacer->addBefore($content, $line, $before); - if ($result->updated) { - $return = file_put_contents($file, $result->content); + if ($result !== null) { + $return = file_put_contents($file, $result); return $return !== false; } diff --git a/tests/system/Publisher/ContentReplacerTest.php b/tests/system/Publisher/ContentReplacerTest.php index 0274eb56a4a3..dbfc6d80109b 100644 --- a/tests/system/Publisher/ContentReplacerTest.php +++ b/tests/system/Publisher/ContentReplacerTest.php @@ -73,8 +73,7 @@ public function testAddAfter(): void service('auth')->routes($routes); FILE; - $this->assertSame($expected, $result->content); - $this->assertTrue($result->updated); + $this->assertSame($expected, $result); } public function testAddAfterAlreadyUpdated(): void @@ -91,16 +90,7 @@ public function testAddAfterAlreadyUpdated(): void $line = "\n" . 'service(\'auth\')->routes($routes);'; $after = '$routes->'; $result = $replacer->addAfter($content, $line, $after); - - $expected = <<<'FILE' - $routes->get('/', 'Home::index'); - $routes->get('/login', 'Login::index'); - - service('auth')->routes($routes); - - FILE; - $this->assertSame($expected, $result->content); - $this->assertFalse($result->updated); + $this->assertNull($result); } public function testAddBefore(): void @@ -128,8 +118,7 @@ public function testAddBefore(): void // Do Not Edit This Line FILE; - $this->assertSame($expected, $result->content); - $this->assertTrue($result->updated); + $this->assertSame($expected, $result); } public function testAddBeforeAlreadyUpdated(): void @@ -148,17 +137,6 @@ public function testAddBeforeAlreadyUpdated(): void $line = '$this->helpers = array_merge($this->helpers, [\'auth\', \'setting\']);'; $before = '// Do Not Edit This Line'; $result = $replacer->addBefore($content, $line, $before); - - $expected = <<<'FILE' - helpers = array_merge($this->helpers, ['auth', 'setting']); - // Do Not Edit This Line - parent::initController($request, $response, $logger); - // Do Not Edit This Line - - FILE; - $this->assertSame($expected, $result->content); - $this->assertFalse($result->updated); + $this->assertNull($result); } } From 615400147b5e8cbc38d43446d6eceea7ad0937cc Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 11 Jul 2022 11:28:33 +0900 Subject: [PATCH 0799/8282] docs: add doc comment --- system/Publisher/ContentReplacer.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/Publisher/ContentReplacer.php b/system/Publisher/ContentReplacer.php index 2372e850b080..76cef11336f1 100644 --- a/system/Publisher/ContentReplacer.php +++ b/system/Publisher/ContentReplacer.php @@ -13,6 +13,9 @@ use RuntimeException; +/** + * Replace Text Content + */ class ContentReplacer { /** From 40ee9c8db826a5f66f6a47cf0f05f5c9eccb2bee Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 4 Jul 2022 11:56:53 +0900 Subject: [PATCH 0800/8282] style: align comments --- app/Config/Constants.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/Config/Constants.php b/app/Config/Constants.php index 0c1b53487cd9..47b92f832935 100644 --- a/app/Config/Constants.php +++ b/app/Config/Constants.php @@ -67,16 +67,16 @@ | http://tldp.org/LDP/abs/html/exitcodes.html | */ -defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0); // no errors -defined('EXIT_ERROR') || define('EXIT_ERROR', 1); // generic error -defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3); // configuration error -defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4); // file not found -defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5); // unknown class +defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0); // no errors +defined('EXIT_ERROR') || define('EXIT_ERROR', 1); // generic error +defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3); // configuration error +defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4); // file not found +defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5); // unknown class defined('EXIT_UNKNOWN_METHOD') || define('EXIT_UNKNOWN_METHOD', 6); // unknown class member -defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user input -defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error -defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code -defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code +defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user input +defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error +defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code +defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code /** * @deprecated Use \CodeIgniter\Events\Events::PRIORITY_LOW instead. From b0ed2d511c79a9289a689880e9929d546678f68e Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 11 Jul 2022 14:00:24 +0900 Subject: [PATCH 0801/8282] fix: invalid INSERT query when builder uses table alias --- system/Database/BaseBuilder.php | 17 ++++++++++-- tests/system/Database/Builder/InsertTest.php | 29 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 4f835730d176..a5903c49625b 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -1863,7 +1863,7 @@ public function getCompiledInsert(bool $reset = true) } $sql = $this->_insert( - $this->db->protectIdentifiers($this->QBFrom[0], true, null, false), + $this->db->protectIdentifiers($this->removeAlias($this->QBFrom[0]), true, null, false), array_keys($this->QBSet), array_values($this->QBSet) ); @@ -1895,7 +1895,7 @@ public function insert($set = null, ?bool $escape = null) } $sql = $this->_insert( - $this->db->protectIdentifiers($this->QBFrom[0], true, $escape, false), + $this->db->protectIdentifiers($this->removeAlias($this->QBFrom[0]), true, $escape, false), array_keys($this->QBSet), array_values($this->QBSet) ); @@ -1914,6 +1914,19 @@ public function insert($set = null, ?bool $escape = null) return false; } + protected function removeAlias(string $from): string + { + if (strpos($from, ' ') !== false) { + // if the alias is written with the AS keyword, remove it + $from = preg_replace('/\s+AS\s+/i', ' ', $from); + + $parts = explode(' ', $from); + $from = $parts[0]; + } + + return $from; + } + /** * This method is used by both insert() and getCompiledInsert() to * validate that the there data is actually being set and that table diff --git a/tests/system/Database/Builder/InsertTest.php b/tests/system/Database/Builder/InsertTest.php index daa605be40f9..774dd1082ed0 100644 --- a/tests/system/Database/Builder/InsertTest.php +++ b/tests/system/Database/Builder/InsertTest.php @@ -85,6 +85,35 @@ public function testInsertObject() $this->assertSame($expectedBinds, $builder->getBinds()); } + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/5365 + */ + public function testInsertWithTableAlias() + { + $builder = $this->db->table('jobs as j'); + + $insertData = [ + 'id' => 1, + 'name' => 'Grocery Sales', + ]; + $builder->testMode()->insert($insertData, true); + + $expectedSQL = 'INSERT INTO "jobs" ("id", "name") VALUES (1, \'Grocery Sales\')'; + $expectedBinds = [ + 'id' => [ + 1, + true, + ], + 'name' => [ + 'Grocery Sales', + true, + ], + ]; + + $this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledInsert())); + $this->assertSame($expectedBinds, $builder->getBinds()); + } + public function testThrowsExceptionOnNoValuesSet() { $builder = $this->db->table('jobs'); From dd279db1103223bc1bd63efff013b7c29613fd0d Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Nov 2021 17:36:43 +0900 Subject: [PATCH 0802/8282] style: break long lines --- system/Database/BaseBuilder.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index a5903c49625b..958292b1a820 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -1863,7 +1863,12 @@ public function getCompiledInsert(bool $reset = true) } $sql = $this->_insert( - $this->db->protectIdentifiers($this->removeAlias($this->QBFrom[0]), true, null, false), + $this->db->protectIdentifiers( + $this->removeAlias($this->QBFrom[0]), + true, + null, + false + ), array_keys($this->QBSet), array_values($this->QBSet) ); @@ -1895,7 +1900,12 @@ public function insert($set = null, ?bool $escape = null) } $sql = $this->_insert( - $this->db->protectIdentifiers($this->removeAlias($this->QBFrom[0]), true, $escape, false), + $this->db->protectIdentifiers( + $this->removeAlias($this->QBFrom[0]), + true, + $escape, + false + ), array_keys($this->QBSet), array_values($this->QBSet) ); From bd5ac2475816493abaa954ee106bfc0ffeeaba5e Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Nov 2021 20:26:31 +0900 Subject: [PATCH 0803/8282] fix: invalid DELETE query when builder uses table alias --- system/Database/BaseBuilder.php | 2 +- tests/system/Database/Builder/DeleteTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 958292b1a820..e4b399f5fbee 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -2363,7 +2363,7 @@ public function delete($where = '', ?int $limit = null, bool $resetData = true) return false; // @codeCoverageIgnore } - $sql = $this->_delete($table); + $sql = $this->_delete($this->removeAlias($table)); if (! empty($limit)) { $this->QBLimit = $limit; diff --git a/tests/system/Database/Builder/DeleteTest.php b/tests/system/Database/Builder/DeleteTest.php index 777a646992c4..ab27a2ec9266 100644 --- a/tests/system/Database/Builder/DeleteTest.php +++ b/tests/system/Database/Builder/DeleteTest.php @@ -60,6 +60,20 @@ public function testGetCompiledDelete() $this->assertSame($expectedSQL, $sql); } + public function testGetCompiledDeleteWithTableAlias() + { + $builder = $this->db->table('jobs j'); + + $builder->where('id', 1); + $sql = $builder->getCompiledDelete(); + + $expectedSQL = <<<'EOL' + DELETE FROM "jobs" + WHERE "id" = 1 + EOL; + $this->assertSame($expectedSQL, $sql); + } + public function testGetCompiledDeleteWithLimit() { $builder = $this->db->table('jobs'); From bfcbde3d7e49d7deee66d0585f705daa6380aebc Mon Sep 17 00:00:00 2001 From: nalakapws <106789904+nalakapws@users.noreply.github.com> Date: Mon, 11 Jul 2022 11:05:48 +0530 Subject: [PATCH 0804/8282] Add db port entry into env file. --- env | 2 ++ 1 file changed, 2 insertions(+) diff --git a/env b/env index 67faaee5b57a..cc0681c67711 100644 --- a/env +++ b/env @@ -45,6 +45,7 @@ # database.default.password = root # database.default.DBDriver = MySQLi # database.default.DBPrefix = +# database.default.port = 3306 # database.tests.hostname = localhost # database.tests.database = ci4 @@ -52,6 +53,7 @@ # database.tests.password = root # database.tests.DBDriver = MySQLi # database.tests.DBPrefix = +# database.tests.port = 3306 #-------------------------------------------------------------------- # CONTENT SECURITY POLICY From cdd7278948da89eccbbcaf759b134fd779ca012a Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 11 Jul 2022 14:47:27 +0900 Subject: [PATCH 0805/8282] fix: Call to undefined method CodeIgniter\Pager\PagerRenderer::getDetails() --- system/Pager/Pager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index 32843ba9e98d..80861646efdc 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -122,7 +122,8 @@ protected function displayLinks(string $group, string $template): string $pager = new PagerRenderer($this->getDetails($group)); - return $this->view->setVar('pager', $pager)->render($this->config->templates[$template]); + return $this->view->setVar('pager', $pager) + ->render($this->config->templates[$template], null, false); } /** From 5383d46679c24b15cdbe2cd5dedab738e41c83c7 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Jul 2022 10:07:21 +0900 Subject: [PATCH 0806/8282] fix: incorrect HTTP status code may return --- system/Debug/Exceptions.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index 736a67fa9711..fb2dfc6d6002 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -14,6 +14,7 @@ use CodeIgniter\API\ResponseTrait; use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\HTTP\CLIRequest; +use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\Response; use Config\Exceptions as ExceptionsConfig; @@ -115,7 +116,14 @@ public function exceptionHandler(Throwable $exception) } if (! is_cli()) { - $this->response->setStatusCode($statusCode); + try { + $this->response->setStatusCode($statusCode); + } catch (HTTPException $e) { + // Workaround for invalid HTTP status code. + $statusCoden = 500; + $this->response->setStatusCode($statusCoden); + } + if (! headers_sent()) { header(sprintf('HTTP/%s %s %s', $this->request->getProtocolVersion(), $this->response->getStatusCode(), $this->response->getReasonPhrase()), true, $statusCode); } From 50fa7ada5aa71cbcaf29d48f030c8bbc8757dda2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Jul 2022 15:11:22 +0900 Subject: [PATCH 0807/8282] refactor: fix type in variable name --- system/Debug/Exceptions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index fb2dfc6d6002..3ec1230291ea 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -120,8 +120,8 @@ public function exceptionHandler(Throwable $exception) $this->response->setStatusCode($statusCode); } catch (HTTPException $e) { // Workaround for invalid HTTP status code. - $statusCoden = 500; - $this->response->setStatusCode($statusCoden); + $statusCode = 500; + $this->response->setStatusCode($statusCode); } if (! headers_sent()) { From 2258c955e004aca1a3c03c196f7ed59605828c4b Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Jul 2022 17:06:55 +0900 Subject: [PATCH 0808/8282] refactor: add return type --- system/Publisher/Publisher.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/Publisher/Publisher.php b/system/Publisher/Publisher.php index 920650fe9351..502d3670ff12 100644 --- a/system/Publisher/Publisher.php +++ b/system/Publisher/Publisher.php @@ -404,10 +404,8 @@ final public function merge(bool $replace = true): bool * Replace content * * @param array $replaces [search => replace] - * - * @return bool */ - public function replace(string $file, array $replaces) + public function replace(string $file, array $replaces): bool { $this->verifyAllowed($file, $file); From 7c028b718270c3fde17f9ea0b84be0b6f626a9e6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Jul 2022 17:07:44 +0900 Subject: [PATCH 0809/8282] docs: fix existing sample code --- user_guide_src/source/libraries/publisher/010.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/libraries/publisher/010.php b/user_guide_src/source/libraries/publisher/010.php index 652322fd0bcb..61b75287e51f 100644 --- a/user_guide_src/source/libraries/publisher/010.php +++ b/user_guide_src/source/libraries/publisher/010.php @@ -15,9 +15,9 @@ class AuthPublish extends BaseCommand public function run(array $params) { // Use the Autoloader to figure out the module path - $source = service('autoloader')->getNamespace('Math\\Auth'); + $source = service('autoloader')->getNamespace('Math\\Auth')[0]; - $publisher = new Publisher($source, APPATH); + $publisher = new Publisher($source, APPPATH); try { // Add only the desired components From a4f5e8c69834d09b408ed4b9febcd21fbcb98e75 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Jul 2022 17:21:13 +0900 Subject: [PATCH 0810/8282] docs: add replace(), addLineAfter(), addLineBefore() --- user_guide_src/source/libraries/publisher.rst | 24 +++++++++++++++++++ .../source/libraries/publisher/013.php | 16 +++++++++++++ .../source/libraries/publisher/014.php | 14 +++++++++++ .../source/libraries/publisher/015.php | 14 +++++++++++ 4 files changed, 68 insertions(+) create mode 100644 user_guide_src/source/libraries/publisher/013.php create mode 100644 user_guide_src/source/libraries/publisher/014.php create mode 100644 user_guide_src/source/libraries/publisher/015.php diff --git a/user_guide_src/source/libraries/publisher.rst b/user_guide_src/source/libraries/publisher.rst index 5c437c3715e8..9da4e6c9eb9d 100644 --- a/user_guide_src/source/libraries/publisher.rst +++ b/user_guide_src/source/libraries/publisher.rst @@ -264,3 +264,27 @@ affect other files in the destination. Returns success or failure, use ``getPubl Example: .. literalinclude:: publisher/012.php + +Modifying Files +=============== + +replace(string $file, array $replaces): bool +-------------------------------------------- + +Replaces the ``$file`` contents. The second parameter ``$replaces`` array specifies the search strings as keys and the replacements as values. + +.. literalinclude:: publisher/013.php + +addLineAfter(string $file, string $line, string $after): bool +------------------------------------------------------------- + +Adds ``$line`` after a line with specific string ``$after``. + +.. literalinclude:: publisher/014.php + +addLineBefore(string $file, string $line, string $after): bool +-------------------------------------------------------------- + +Adds ``$line`` before a line with specific string ``$after``. + +.. literalinclude:: publisher/015.php diff --git a/user_guide_src/source/libraries/publisher/013.php b/user_guide_src/source/libraries/publisher/013.php new file mode 100644 index 000000000000..a48935497f5e --- /dev/null +++ b/user_guide_src/source/libraries/publisher/013.php @@ -0,0 +1,16 @@ +getNamespace('CodeIgniter\\Shield')[0]; +$publisher = new Publisher($source, APPPATH); + +$file = APPPATH . 'Config/Auth.php'; + +$publisher->replace( + $file, + [ + 'use CodeIgniter\Config\BaseConfig;' . "\n" => '', + 'class App extends BaseConfig' => 'class App extends \Some\Package\SomeConfig', + ] +); diff --git a/user_guide_src/source/libraries/publisher/014.php b/user_guide_src/source/libraries/publisher/014.php new file mode 100644 index 000000000000..06acbe1313cb --- /dev/null +++ b/user_guide_src/source/libraries/publisher/014.php @@ -0,0 +1,14 @@ +getNamespace('CodeIgniter\\Shield')[0]; +$publisher = new Publisher($source, APPPATH); + +$file = APPPATH . 'Config/App.php'; + +$publisher->addLineAfter( + $file, + ' public int $myOwnConfig = 1000;', // Adds this line + 'public bool $CSPEnabled = false;' // After this line +); diff --git a/user_guide_src/source/libraries/publisher/015.php b/user_guide_src/source/libraries/publisher/015.php new file mode 100644 index 000000000000..28cb609d4d0b --- /dev/null +++ b/user_guide_src/source/libraries/publisher/015.php @@ -0,0 +1,14 @@ +getNamespace('CodeIgniter\\Shield')[0]; +$publisher = new Publisher($source, APPPATH); + +$file = APPPATH . 'Config/App.php'; + +$publisher->addLineBefore( + $file, + ' public int $myOwnConfig = 1000;', // Add this line + 'public bool $CSPEnabled = false;' // Before this line +); From d9ea9da28679cbc340739956060a06456cb95027 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Jul 2022 17:30:10 +0900 Subject: [PATCH 0811/8282] docs: add changelog --- user_guide_src/source/changelogs/v4.3.0.rst | 1 + user_guide_src/source/libraries/publisher.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index f2f311c5ce89..e753fc93ef13 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -36,6 +36,7 @@ Enhancements - Added before and after events to ``BaseModel::insertBatch()`` and ``BaseModel::updateBatch()`` methods. See :ref:`model-events-callbacks`. - Added ``Model::allowEmptyInserts()`` method to insert empty data. See :ref:`Using CodeIgniter's Model ` - Added ``$routes->useSupportedLocalesOnly(true)`` so that the Router returns 404 Not Found if the locale in the URL is not supported in ``Config\App::$supportedLocales``. See :ref:`Localization ` +- Added methods ``replace()``, ``addLineAfter()`` and ``addLineBefore()`` to modify files in Publisher. See :ref:`Publisher ` for details. - The call handler for Spark commands from the ``CodeIgniter\CodeIgniter`` class has been extracted. This will reduce the cost of console calls. Changes diff --git a/user_guide_src/source/libraries/publisher.rst b/user_guide_src/source/libraries/publisher.rst index 9da4e6c9eb9d..630eac44b3ad 100644 --- a/user_guide_src/source/libraries/publisher.rst +++ b/user_guide_src/source/libraries/publisher.rst @@ -265,6 +265,8 @@ Example: .. literalinclude:: publisher/012.php +.. _publisher-modifying-files: + Modifying Files =============== From a8b0edd5a1d8d36daeb874100314b10342a81b5a Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 12 Jul 2022 17:43:18 +0900 Subject: [PATCH 0812/8282] docs: add @internal --- system/Database/BaseBuilder.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index e4b399f5fbee..a899ffbe5628 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -1924,6 +1924,12 @@ public function insert($set = null, ?bool $escape = null) return false; } + /** + * @internal This is a temporary solution. + * + * @see https://github.com/codeigniter4/CodeIgniter4/pull/5376 + * @TODO Fix a root cause, and this method should be removed. + */ protected function removeAlias(string $from): string { if (strpos($from, ' ') !== false) { From 3d3923570ea48479c4c7d280cb99e9ca3c4f3a95 Mon Sep 17 00:00:00 2001 From: Toto Date: Tue, 12 Jul 2022 18:28:18 +0700 Subject: [PATCH 0813/8282] remove `stale.yml` --- .gitattributes | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index f1ed82db1653..6d9cd569c5a3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,7 +16,6 @@ contributing/ export-ignore CODE_OF_CONDUCT.md export-ignore CONTRIBUTING.md export-ignore PULL_REQUEST_TEMPLATE.md export-ignore -stale.yml export-ignore Vagrantfile.dist export-ignore # They don't want our test files From fe229a43d69eed9952f173200b3d164b9b3a5fa7 Mon Sep 17 00:00:00 2001 From: Toto Date: Tue, 12 Jul 2022 18:31:10 +0700 Subject: [PATCH 0814/8282] remove `PULL_REQUEST_TEMPLATE.md` --- .gitattributes | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 6d9cd569c5a3..c17992b1ff54 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,7 +15,6 @@ contributing/ export-ignore .nojekyll export-ignore export-ignore CODE_OF_CONDUCT.md export-ignore CONTRIBUTING.md export-ignore -PULL_REQUEST_TEMPLATE.md export-ignore Vagrantfile.dist export-ignore # They don't want our test files From 189a561661d1a0c4368da9c7d8929ff03453ae23 Mon Sep 17 00:00:00 2001 From: Toto Date: Tue, 12 Jul 2022 18:35:54 +0700 Subject: [PATCH 0815/8282] remove `depfile.yaml` --- .gitattributes | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index c17992b1ff54..6e2767f58b27 100644 --- a/.gitattributes +++ b/.gitattributes @@ -21,7 +21,6 @@ Vagrantfile.dist export-ignore tests/AutoReview/ export-ignore tests/system/ export-ignore utils/ export-ignore -depfile.yaml export-ignore rector.php export-ignore phpunit.xml.dist export-ignore phpstan-baseline.neon.dist export-ignore From f7b54767cfec6c5b2905cf9159af4a8c408573a6 Mon Sep 17 00:00:00 2001 From: Toto Date: Tue, 12 Jul 2022 18:40:14 +0700 Subject: [PATCH 0816/8282] add `deptrac.yaml` --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 6e2767f58b27..e81e6ce3a031 100644 --- a/.gitattributes +++ b/.gitattributes @@ -21,6 +21,7 @@ Vagrantfile.dist export-ignore tests/AutoReview/ export-ignore tests/system/ export-ignore utils/ export-ignore +deptrac.yaml export-ignore rector.php export-ignore phpunit.xml.dist export-ignore phpstan-baseline.neon.dist export-ignore From 3fc49fd5b56729fbefe703f7b49414bc2f6f36ba Mon Sep 17 00:00:00 2001 From: fcosrno Date: Tue, 12 Jul 2022 13:16:44 -0400 Subject: [PATCH 0817/8282] Update CodeIgniter.php Use getenv instead of $_SESSION in detectEnvironment --- system/CodeIgniter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index e7b14a96f170..5468133b81c3 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -528,7 +528,7 @@ protected function detectEnvironment() { // Make sure ENVIRONMENT isn't already set by other means. if (! defined('ENVIRONMENT')) { - define('ENVIRONMENT', $_SERVER['CI_ENVIRONMENT'] ?? 'production'); + define('ENVIRONMENT', getenv('CI_ENVIRONMENT') ?? 'production'); } } From 3bddc85bacf7fc1354887f8ebc3711cb56aa1627 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 10 Jul 2022 14:10:35 -0700 Subject: [PATCH 0818/8282] Fix create table if not exists when indexes exist --- phpstan-baseline.neon.dist | 2 +- system/Database/Forge.php | 27 +++++----- system/Database/OCI8/Forge.php | 4 +- system/Database/SQLSRV/Forge.php | 12 +---- system/Database/SQLite3/Forge.php | 3 +- user_guide_src/source/dbmgmt/forge.rst | 4 +- user_guide_src/source/dbmgmt/forge/015.php | 2 +- .../source/installation/upgrade_422.rst | 50 +++++++++++++++++++ 8 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 user_guide_src/source/installation/upgrade_422.rst diff --git a/phpstan-baseline.neon.dist b/phpstan-baseline.neon.dist index 713e54cb0142..da08082b3e08 100644 --- a/phpstan-baseline.neon.dist +++ b/phpstan-baseline.neon.dist @@ -337,7 +337,7 @@ parameters: - message: "#^Access to an undefined property CodeIgniter\\\\Database\\\\BaseConnection\\:\\:\\$schema\\.$#" - count: 14 + count: 13 path: system/Database/SQLSRV/Forge.php - diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 0ffd245fa7a0..8678022b5d19 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -107,9 +107,7 @@ class Forge protected $createTableStr = "%s %s (%s\n)"; /** - * CREATE TABLE IF statement - * - * @var bool|string + * @deprecated This is no longer used. */ protected $createTableIfStr = 'CREATE TABLE IF NOT EXISTS'; @@ -495,7 +493,14 @@ public function createTable(string $table, bool $ifNotExists = false, array $att throw new RuntimeException('Field information is required.'); } - $sql = $this->_createTable($table, $ifNotExists, $attributes); + // If table exists lets stop here + if ($ifNotExists === true && $this->db->tableExists($table)) { + $this->reset(); + + return true; + } + + $sql = $this->_createTable($table, false, $attributes); if (is_bool($sql)) { $this->reset(); @@ -530,20 +535,12 @@ public function createTable(string $table, bool $ifNotExists = false, array $att /** * @return bool|string + * + * @deprecated $ifNotExists is no longer used, and will be removed. */ protected function _createTable(string $table, bool $ifNotExists, array $attributes) { - // For any platforms that don't support Create If Not Exists... - if ($ifNotExists === true && $this->createTableIfStr === false) { - if ($this->db->tableExists($table)) { - return true; - } - - $ifNotExists = false; - } - - $sql = ($ifNotExists) ? sprintf($this->createTableIfStr, $this->db->escapeIdentifiers($table)) - : 'CREATE TABLE'; + $sql = 'CREATE TABLE'; $columns = $this->_processFields(true); diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 9cd5bad945cd..5fd7c5d0b8a5 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -40,9 +40,7 @@ class Forge extends BaseForge protected $createTableIfStr = false; /** - * DROP TABLE IF EXISTS statement - * - * @var false + * @deprecated This is no longer used. */ protected $dropTableIfStr = false; diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 67ef057e12de..63130fd87289 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -91,9 +91,7 @@ class Forge extends BaseForge protected $createTableIfStr; /** - * CREATE TABLE statement - * - * @var string + * @deprecated This is no longer used. */ protected $createTableStr; @@ -101,14 +99,6 @@ public function __construct(BaseConnection $db) { parent::__construct($db); - $this->createTableIfStr = 'IF NOT EXISTS' - . '(SELECT t.name, s.name as schema_name, t.type_desc ' - . 'FROM sys.tables t ' - . 'INNER JOIN sys.schemas s on s.schema_id = t.schema_id ' - . "WHERE s.name=N'" . $this->db->schema . "' " - . "AND t.name=REPLACE(N'%s', '\"', '') " - . "AND t.type_desc='USER_TABLE')\nCREATE TABLE "; - $this->createTableStr = '%s ' . $this->db->escapeIdentifiers($this->db->schema) . ".%s (%s\n) "; $this->renameTableStr = 'EXEC sp_rename [' . $this->db->escapeIdentifiers($this->db->schema) . '.%s] , %s ;'; diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index 1f48f20bd54e..154c9f97b554 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -56,8 +56,7 @@ public function __construct(BaseConnection $db) parent::__construct($db); if (version_compare($this->db->getVersion(), '3.3', '<')) { - $this->createTableIfStr = false; - $this->dropTableIfStr = false; + $this->dropTableIfStr = false; } } diff --git a/user_guide_src/source/dbmgmt/forge.rst b/user_guide_src/source/dbmgmt/forge.rst index 804a0e6ac9d1..6464af2f99a8 100644 --- a/user_guide_src/source/dbmgmt/forge.rst +++ b/user_guide_src/source/dbmgmt/forge.rst @@ -191,8 +191,8 @@ with .. literalinclude:: forge/014.php -An optional second parameter set to true adds an ``IF NOT EXISTS`` clause -into the definition +An optional second parameter set to true will only execute create table statement if +the table name is not in the array of know tables .. literalinclude:: forge/015.php diff --git a/user_guide_src/source/dbmgmt/forge/015.php b/user_guide_src/source/dbmgmt/forge/015.php index ad478406a903..ec116bf6d00d 100644 --- a/user_guide_src/source/dbmgmt/forge/015.php +++ b/user_guide_src/source/dbmgmt/forge/015.php @@ -1,4 +1,4 @@ createTable('table_name', true); -// gives CREATE TABLE IF NOT EXISTS table_name +// creates table only if table does not exist diff --git a/user_guide_src/source/installation/upgrade_422.rst b/user_guide_src/source/installation/upgrade_422.rst new file mode 100644 index 000000000000..374f7703e97b --- /dev/null +++ b/user_guide_src/source/installation/upgrade_422.rst @@ -0,0 +1,50 @@ +############################# +Upgrading from 4.2.1 to 4.2.2 +############################# + +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 + +Mandatory File Changes +********************** + + +Breaking Changes +**************** + +- The method ``Forge::createTable()`` no longer executes a ``CREATE TABLE IF NOT EXISTS``. If table is not found in ``$db->tableExists($table)`` then ``CREATE TABLE`` is executed. +- The method signature of ``Forge::_createTable()`` is deprecated. The ``bool`` ``$ifNotExists`` is no longer used and will be removed in a future release. + +Breaking Enhancements +********************* + + +Project Files +************* + +Numerous files in the **project space** (root, app, public, writable) received updates. Due to +these files being outside of the **system** scope they will not be changed without your intervention. +There are some third-party CodeIgniter modules available to assist with merging changes to +the project space: `Explore on Packagist `_. + +.. note:: Except in very rare cases for bug fixes, no changes made to files for the project space + will break your application. All changes noted here are optional until the next major version, + and any mandatory changes will be covered in the sections above. + +Content Changes +=============== + + +All Changes +=========== + +This is a list of all files in the **project space** that received changes; +many will be simple comments or formatting that have no effect on the runtime: + From fbf292f6c6f87a6b84fcf3028647cd482114cf74 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 12 Jul 2022 11:31:03 -0700 Subject: [PATCH 0819/8282] Cleanup create table if not exists when indexes exist --- system/Database/Forge.php | 4 +--- system/Database/OCI8/Forge.php | 8 ++++---- system/Database/SQLSRV/Forge.php | 8 ++++---- user_guide_src/source/dbmgmt/forge.rst | 3 +-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 8678022b5d19..a30c0f388ffb 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -540,8 +540,6 @@ public function createTable(string $table, bool $ifNotExists = false, array $att */ protected function _createTable(string $table, bool $ifNotExists, array $attributes) { - $sql = 'CREATE TABLE'; - $columns = $this->_processFields(true); for ($i = 0, $c = count($columns); $i < $c; $i++) { @@ -563,7 +561,7 @@ protected function _createTable(string $table, bool $ifNotExists, array $attribu return sprintf( $this->createTableStr . '%s', - $sql, + 'CREATE TABLE', $this->db->escapeIdentifiers($table), $columns, $this->_createTableAttributes($attributes) diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 5fd7c5d0b8a5..5dfc1cf77838 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -33,14 +33,14 @@ class Forge extends BaseForge protected $createDatabaseStr = false; /** - * CREATE TABLE IF statement - * - * @var false + * @deprecated This is no longer used. */ protected $createTableIfStr = false; /** - * @deprecated This is no longer used. + * DROP TABLE IF EXISTS statement + * + * @var false */ protected $dropTableIfStr = false; diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 63130fd87289..f83fdc869494 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -84,14 +84,14 @@ class Forge extends BaseForge ]; /** - * CREATE TABLE IF statement - * - * @var string + * @deprecated This is no longer used. */ protected $createTableIfStr; /** - * @deprecated This is no longer used. + * CREATE TABLE statement + * + * @var string */ protected $createTableStr; diff --git a/user_guide_src/source/dbmgmt/forge.rst b/user_guide_src/source/dbmgmt/forge.rst index 6464af2f99a8..2fb13c72d3e1 100644 --- a/user_guide_src/source/dbmgmt/forge.rst +++ b/user_guide_src/source/dbmgmt/forge.rst @@ -191,8 +191,7 @@ with .. literalinclude:: forge/014.php -An optional second parameter set to true will only execute create table statement if -the table name is not in the array of know tables +An optional second parameter set to true will create the table only if it doesn't already exist. .. literalinclude:: forge/015.php From 01689104f8934a168bfd14dd786154b10e7911dd Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 12 Jul 2022 13:46:59 -0700 Subject: [PATCH 0820/8282] Added change log and source guide notes --- user_guide_src/source/changelogs/v4.3.0.rst | 1 + user_guide_src/source/database/metadata.rst | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index f2f311c5ce89..1730eb026d63 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -37,6 +37,7 @@ Enhancements - Added ``Model::allowEmptyInserts()`` method to insert empty data. See :ref:`Using CodeIgniter's Model ` - Added ``$routes->useSupportedLocalesOnly(true)`` so that the Router returns 404 Not Found if the locale in the URL is not supported in ``Config\App::$supportedLocales``. See :ref:`Localization ` - The call handler for Spark commands from the ``CodeIgniter\CodeIgniter`` class has been extracted. This will reduce the cost of console calls. +- Added a psuedo index named 'PRIMARY' for SQLite `AUTOINCREMENT` column. SQLite treats these in a special way which was excluding them from being retrieved by calling ``BaseConnection::getIndexData()``. Changes ******* diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index 458f70004102..8e3c71458d90 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -121,6 +121,10 @@ The key types may be unique to the database you are using. For instance, MySQL will return one of primary, fulltext, spatial, index or unique for each key associated with a table. +SQLite3 includes a psuedo index named 'PRIMARY'. `AUTOINCREMENT` columns are treated +in special way and wouldn't otherwise be returned. A drop command on this index will +cause an error. + $db->getForeignKeyData() ------------------------ From 0e9335a0bfac45c259ece5e8673ba8bcbe83a369 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 12 Jul 2022 15:09:20 -0700 Subject: [PATCH 0821/8282] Fix Comments --- system/Database/Forge.php | 4 ++++ system/Database/OCI8/Forge.php | 4 ++++ system/Database/SQLSRV/Forge.php | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index a30c0f388ffb..6f65f676a17c 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -107,6 +107,10 @@ class Forge protected $createTableStr = "%s %s (%s\n)"; /** + * CREATE TABLE IF statement + * + * @var bool|string + * * @deprecated This is no longer used. */ protected $createTableIfStr = 'CREATE TABLE IF NOT EXISTS'; diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 5dfc1cf77838..42393cd63aa9 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -33,6 +33,10 @@ class Forge extends BaseForge protected $createDatabaseStr = false; /** + * CREATE TABLE IF statement + * + * @var false + * * @deprecated This is no longer used. */ protected $createTableIfStr = false; diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index f83fdc869494..95ec9ff0b601 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -84,6 +84,10 @@ class Forge extends BaseForge ]; /** + * CREATE TABLE IF statement + * + * @var string + * * @deprecated This is no longer used. */ protected $createTableIfStr; From 3370dd632373f5962b7e96bcdcfef147fbe2cc59 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 12 Jul 2022 15:11:25 -0700 Subject: [PATCH 0822/8282] Update user_guide_src/source/installation/upgrade_422.rst Co-authored-by: kenjis --- user_guide_src/source/installation/upgrade_422.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/installation/upgrade_422.rst b/user_guide_src/source/installation/upgrade_422.rst index 374f7703e97b..b16b52675d4a 100644 --- a/user_guide_src/source/installation/upgrade_422.rst +++ b/user_guide_src/source/installation/upgrade_422.rst @@ -20,7 +20,7 @@ Breaking Changes **************** - The method ``Forge::createTable()`` no longer executes a ``CREATE TABLE IF NOT EXISTS``. If table is not found in ``$db->tableExists($table)`` then ``CREATE TABLE`` is executed. -- The method signature of ``Forge::_createTable()`` is deprecated. The ``bool`` ``$ifNotExists`` is no longer used and will be removed in a future release. +- The second parameter ``$ifNotExists`` of ``Forge::_createTable()`` is deprecated. It is no longer used and will be removed in a future release. Breaking Enhancements ********************* From 6c8e8b09f0db442f6baebdebad807ca8afff8293 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 11:22:07 +0900 Subject: [PATCH 0823/8282] docs: fix section title marks --- user_guide_src/source/testing/benchmark.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/testing/benchmark.rst b/user_guide_src/source/testing/benchmark.rst index 1e7a5c9607b7..896ad7b53026 100644 --- a/user_guide_src/source/testing/benchmark.rst +++ b/user_guide_src/source/testing/benchmark.rst @@ -14,9 +14,9 @@ sending the output to the user, enabling a very accurate timing of the entire sy :local: :depth: 2 -=============== +*************** Using the Timer -=============== +*************** With the Timer, you can measure the time between two moments in the execution of your application. This makes it simple to measure the performance of different aspects of your application. All measurement is done using @@ -63,9 +63,9 @@ the timer to display. The second is the number of decimal places to display. Thi .. literalinclude:: benchmark/006.php -================== +****************** Using the Iterator -================== +****************** The Iterator is a simple tool that is designed to allow you to try out multiple variations on a solution to see the speed differences and different memory usage patterns. You can add any number of "tasks" for it to From 1b60a8efcc88a501cb59dd183eb9c4ad93a532af Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 11:26:38 +0900 Subject: [PATCH 0824/8282] docs: fix text decoration --- user_guide_src/source/testing/benchmark.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/testing/benchmark.rst b/user_guide_src/source/testing/benchmark.rst index 896ad7b53026..6bc59c7826d1 100644 --- a/user_guide_src/source/testing/benchmark.rst +++ b/user_guide_src/source/testing/benchmark.rst @@ -3,7 +3,7 @@ Benchmarking ############ CodeIgniter provides two separate tools to help you benchmark your code and test different options: -the Timer and the Iterator. The Timer allows you to easily calculate the time between two points in the +the **Timer** and the **Iterator**. The Timer allows you to easily calculate the time between two points in the execution of your script. The Iterator allows you to set up several variations and runs those tests, recording performance and memory statistics to help you decide which version is the best. @@ -58,7 +58,7 @@ Displaying Execution Time ========================= While the ``getTimers()`` method will give you the raw data for all of the timers in your project, you can retrieve -the duration of a single timer, in seconds, with the `getElapsedTime()` method. The first parameter is the name of +the duration of a single timer, in seconds, with the ``getElapsedTime()`` method. The first parameter is the name of the timer to display. The second is the number of decimal places to display. This defaults to 4: .. literalinclude:: benchmark/006.php @@ -76,7 +76,7 @@ Creating Tasks To Run ===================== Tasks are defined within Closures. Any output the task creates will be discarded automatically. They are -added to the Iterator class through the `add()` method. The first parameter is a name you want to refer to +added to the Iterator class through the ``add()`` method. The first parameter is a name you want to refer to this test by. The second parameter is the Closure, itself: .. literalinclude:: benchmark/007.php @@ -91,6 +91,6 @@ to run the tests more times than that, you can pass the number as the first para .. literalinclude:: benchmark/008.php Once it has run, it will return an HTML table with the results of the test. If you don't want the results -displayed, you can pass in `false` as the second parameter: +displayed, you can pass in ``false`` as the second parameter: .. literalinclude:: benchmark/009.php From 57a8cc973238430f9fd6e5ab5215b82523332d51 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 11:35:47 +0900 Subject: [PATCH 0825/8282] docs: fix incorrect namespace --- user_guide_src/source/testing/benchmark/007.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/testing/benchmark/007.php b/user_guide_src/source/testing/benchmark/007.php index 13a95ec66e29..00c1b7aa2b8f 100644 --- a/user_guide_src/source/testing/benchmark/007.php +++ b/user_guide_src/source/testing/benchmark/007.php @@ -1,6 +1,6 @@ add('single_concat', static function () { From 3ad2dce923d8002e0766d1d0a96ed37a169d66a2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 11:36:21 +0900 Subject: [PATCH 0826/8282] docs: improve explanation --- user_guide_src/source/testing/benchmark.rst | 4 ++-- user_guide_src/source/testing/benchmark/008.php | 2 +- user_guide_src/source/testing/benchmark/009.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/testing/benchmark.rst b/user_guide_src/source/testing/benchmark.rst index 6bc59c7826d1..fb92eb19dfec 100644 --- a/user_guide_src/source/testing/benchmark.rst +++ b/user_guide_src/source/testing/benchmark.rst @@ -90,7 +90,7 @@ to run the tests more times than that, you can pass the number as the first para .. literalinclude:: benchmark/008.php -Once it has run, it will return an HTML table with the results of the test. If you don't want the results -displayed, you can pass in ``false`` as the second parameter: +Once it has run, it will return an HTML table with the results of the test. +If you don't want the results, you can pass in ``false`` as the second parameter: .. literalinclude:: benchmark/009.php diff --git a/user_guide_src/source/testing/benchmark/008.php b/user_guide_src/source/testing/benchmark/008.php index da0f6a83b5fa..931250447f6c 100644 --- a/user_guide_src/source/testing/benchmark/008.php +++ b/user_guide_src/source/testing/benchmark/008.php @@ -1,4 +1,4 @@ run(3000); +$htmlTable = $iterator->run(3000); diff --git a/user_guide_src/source/testing/benchmark/009.php b/user_guide_src/source/testing/benchmark/009.php index 1f30692559b2..0e966c638e70 100644 --- a/user_guide_src/source/testing/benchmark/009.php +++ b/user_guide_src/source/testing/benchmark/009.php @@ -1,4 +1,4 @@ run(1000, false); From 1f732552d20f2b63f4a6e12d72a99d96996e0e0d Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 13:38:32 +0900 Subject: [PATCH 0827/8282] chore: update Kint to 4.1.3 in ThirdParty --- system/ThirdParty/Kint/Parser/Parser.php | 7 ++++++- system/ThirdParty/Kint/resources/compiled/rich.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/system/ThirdParty/Kint/Parser/Parser.php b/system/ThirdParty/Kint/Parser/Parser.php index d658b092479d..75781206bf01 100644 --- a/system/ThirdParty/Kint/Parser/Parser.php +++ b/system/ThirdParty/Kint/Parser/Parser.php @@ -34,6 +34,7 @@ use Kint\Zval\Value; use ReflectionObject; use stdClass; +use TypeError; class Parser { @@ -524,7 +525,11 @@ private function parseObject(&$var, Value $o) } $stash = $val; - $copy[$i] = $refmarker; + try { + $copy[$i] = $refmarker; + } catch (TypeError $e) { + $child->reference = true; + } if ($val === $refmarker) { $child->reference = true; $val = $stash; diff --git a/system/ThirdParty/Kint/resources/compiled/rich.js b/system/ThirdParty/Kint/resources/compiled/rich.js index 7ab616cf62c1..f15d38ff0616 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)},each:function(e,t){Array.prototype.slice.call(document.querySelectorAll(e),0).forEach(t)},hasClass:function(e,t){return!!e.classList&&e.classList.contains(t=void 0===t?"kint-show":t)},addClass:function(e,t){e.classList.add(t=void 0===t?"kint-show":t)},removeClass:function(e,t){return e.classList.remove(t=void 0===t?"kint-show":t),e},toggle:function(e,t){var a=l.getChildren(e);a&&((t=void 0===t?l.hasClass(e):t)?l.removeClass(e):l.addClass(e),1===a.childNodes.length&&(a=a.childNodes[0].childNodes[0])&&l.hasClass(a,"kint-parent")&&l.toggle(a,t))},toggleChildren:function(e,t){var a=l.getChildren(e);if(a){var o=a.getElementsByClassName("kint-parent"),r=o.length;for(void 0===t&&(t=!l.hasClass(e));r--;)l.toggle(o[r],t)}},toggleAll:function(e){for(var t=document.getElementsByClassName("kint-parent"),a=t.length,o=!l.hasClass(e.parentNode);a--;)l.toggle(t[a],o)},switchTab:function(e){var t=e.previousSibling,a=0;for(l.removeClass(e.parentNode.getElementsByClassName("kint-active-tab")[0],"kint-active-tab"),l.addClass(e,"kint-active-tab");t;)1===t.nodeType&&a++,t=t.previousSibling;for(var o=e.parentNode.nextSibling.childNodes,r=0;r"},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)",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&&l.removeClass(t,"kint-focused"),l.keyboardNav.active&&(t=l.keyboardNav.targets[l.keyboardNav.target],l.addClass(t,"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: Wed, 13 Jul 2022 14:19:45 +0900 Subject: [PATCH 0828/8282] docs: add text decoration --- user_guide_src/source/libraries/uploaded_files.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/libraries/uploaded_files.rst b/user_guide_src/source/libraries/uploaded_files.rst index 57951041e216..968cc4b68020 100644 --- a/user_guide_src/source/libraries/uploaded_files.rst +++ b/user_guide_src/source/libraries/uploaded_files.rst @@ -32,7 +32,7 @@ find reference information. Creating the Upload Form ======================== -Using a text editor, create a form called upload_form.php. In it, place +Using a text editor, create a form called **upload_form.php**. In it, place this code and save it to your **app/Views/** directory: .. literalinclude:: uploaded_files/001.php @@ -46,7 +46,7 @@ wrong. The Success Page ================ -Using a text editor, create a form called upload_success.php. In it, +Using a text editor, create a form called **upload_success.php**. In it, place this code and save it to your **app/Views/** directory:: @@ -72,7 +72,7 @@ place this code and save it to your **app/Views/** directory:: The Controller ============== -Using a text editor, create a controller called Upload.php. In it, place +Using a text editor, create a controller called **Upload.php**. In it, place this code and save it to your **app/Controllers/** directory: .. literalinclude:: uploaded_files/002.php From 8332b3b73b4a255e1c462b0e6428b1930f14f362 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 14:20:48 +0900 Subject: [PATCH 0829/8282] docs: add routing for Upload sample code --- .../source/libraries/uploaded_files.rst | 7 +++++++ .../source/libraries/uploaded_files/021.php | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 user_guide_src/source/libraries/uploaded_files/021.php diff --git a/user_guide_src/source/libraries/uploaded_files.rst b/user_guide_src/source/libraries/uploaded_files.rst index 968cc4b68020..3695b5d5873e 100644 --- a/user_guide_src/source/libraries/uploaded_files.rst +++ b/user_guide_src/source/libraries/uploaded_files.rst @@ -81,6 +81,13 @@ this code and save it to your **app/Controllers/** directory: only :ref:`rules-for-file-uploads` can be used to validate upload file with :doc:`validation`. The rule ``required`` also can't be used, so use ``uploaded`` instead. +The Routes +========== + +Using a text editor, open **app/Config/Routes.php**. In it, add the following two routes: + +.. literalinclude:: uploaded_files/021.php + The Upload Directory ==================== diff --git a/user_guide_src/source/libraries/uploaded_files/021.php b/user_guide_src/source/libraries/uploaded_files/021.php new file mode 100644 index 000000000000..c7cfcddc76f7 --- /dev/null +++ b/user_guide_src/source/libraries/uploaded_files/021.php @@ -0,0 +1,18 @@ +get('/', 'Home::index'); + +$routes->get('upload', 'Upload::index'); // Add this line. +$routes->post('upload/upload', 'Upload::upload'); // Add this line. + +// ... From 708454cab1103e6e259fd62a11d6a6cc8582cb43 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 14:36:54 +0900 Subject: [PATCH 0830/8282] fix: add parameter checking --- system/Validation/FileRules.php | 10 +++++++--- tests/system/Validation/FileRulesTest.php | 10 ++++++++++ tests/system/Validation/StrictRules/FileRulesTest.php | 10 ++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/system/Validation/FileRules.php b/system/Validation/FileRules.php index 2becb34b1950..89b91a7fdbf8 100644 --- a/system/Validation/FileRules.php +++ b/system/Validation/FileRules.php @@ -14,6 +14,7 @@ use CodeIgniter\HTTP\RequestInterface; use Config\Mimes; use Config\Services; +use InvalidArgumentException; /** * File validation rules @@ -79,8 +80,11 @@ public function max_size(?string $blank, string $params): bool { // Grab the file name off the top of the $params // after we split it. - $params = explode(',', $params); - $name = array_shift($params); + $paramArray = explode(',', $params); + if (count($paramArray) !== 2) { + throw new InvalidArgumentException('Invalid max_size parameter: "' . $params . '"'); + } + $name = array_shift($paramArray); if (! ($files = $this->request->getFileMultiple($name))) { $files = [$this->request->getFile($name)]; @@ -99,7 +103,7 @@ public function max_size(?string $blank, string $params): bool return false; } - if ($file->getSize() / 1024 > $params[0]) { + if ($file->getSize() / 1024 > $paramArray[0]) { return false; } } diff --git a/tests/system/Validation/FileRulesTest.php b/tests/system/Validation/FileRulesTest.php index 8c5ce6e142ac..574e1d4ec50f 100644 --- a/tests/system/Validation/FileRulesTest.php +++ b/tests/system/Validation/FileRulesTest.php @@ -13,6 +13,7 @@ use CodeIgniter\Test\CIUnitTestCase; use Config\Services; +use InvalidArgumentException; use Tests\Support\Validation\TestRules; /** @@ -198,6 +199,15 @@ public function testMaxSizeBad(): void $this->assertFalse($this->validation->run([])); } + public function testMaxSizeInvalidParam(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid max_size parameter: "avatar.100"'); + + $this->validation->setRules(['avatar' => 'max_size[avatar.100]']); + $this->validation->run([]); + } + public function testMaxDims(): void { $this->validation->setRules(['avatar' => 'max_dims[avatar,640,480]']); diff --git a/tests/system/Validation/StrictRules/FileRulesTest.php b/tests/system/Validation/StrictRules/FileRulesTest.php index 3446d9f62010..949339755761 100644 --- a/tests/system/Validation/StrictRules/FileRulesTest.php +++ b/tests/system/Validation/StrictRules/FileRulesTest.php @@ -14,6 +14,7 @@ use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Validation\Validation; use Config\Services; +use InvalidArgumentException; use Tests\Support\Validation\TestRules; /** @@ -199,6 +200,15 @@ public function testMaxSizeBad(): void $this->assertFalse($this->validation->run([])); } + public function testMaxSizeInvalidParam(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid max_size parameter: "avatar.100"'); + + $this->validation->setRules(['avatar' => 'max_size[avatar.100]']); + $this->validation->run([]); + } + public function testMaxDims(): void { $this->validation->setRules(['avatar' => 'max_dims[avatar,640,480]']); From 80bd30afccfe8d6fe59cd86fa289679713807720 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 17:16:56 +0900 Subject: [PATCH 0831/8282] refactor: replace locale_set_default() with Locale::setDefault() We are using Locale::getDefault(). --- system/CodeIgniter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index e7b14a96f170..35589102a2ca 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -35,6 +35,7 @@ use Kint; use Kint\Renderer\CliRenderer; use Kint\Renderer\RichRenderer; +use Locale; use LogicException; /** @@ -190,7 +191,7 @@ public function initialize() } // Set default locale on the server - locale_set_default($this->config->defaultLocale ?? 'en'); + Locale::setDefault($this->config->defaultLocale ?? 'en'); // Set default timezone on the server date_default_timezone_set($this->config->appTimezone ?? 'UTC'); From ad7e9e1429f635d7f347682a6539eae2894f35e8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Jul 2022 17:15:00 +0900 Subject: [PATCH 0832/8282] fix: format_number() depends on IncommingRequest --- system/Helpers/number_helper.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/system/Helpers/number_helper.php b/system/Helpers/number_helper.php index 0a1e16cedc29..56a2c39e71dd 100644 --- a/system/Helpers/number_helper.php +++ b/system/Helpers/number_helper.php @@ -9,8 +9,6 @@ * the LICENSE file that was distributed with this source code. */ -use Config\Services; - // CodeIgniter Number Helpers if (! function_exists('number_to_size')) { @@ -129,8 +127,9 @@ function number_to_currency(float $num, string $currency, ?string $locale = null */ function format_number(float $num, int $precision = 1, ?string $locale = null, array $options = []): string { - // Locale is either passed in here, negotiated with client, or grabbed from our config file. - $locale ??= Services::request()->getLocale(); + // If locate is not passed, get from the default locale that is set from our config file + // or set by HTTP content negotiation. + $locale ??= Locale::getDefault(); // Type can be any of the NumberFormatter options, but provide a default. $type = (int) ($options['type'] ?? NumberFormatter::DECIMAL); From cdaadd34103615dab10864f0cba0f1f809bc8849 Mon Sep 17 00:00:00 2001 From: Toto Prayogo Date: Wed, 13 Jul 2022 17:20:47 +0700 Subject: [PATCH 0833/8282] use SVG logo grab from codeigniter.com --- app/Views/welcome_message.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php index c66a9615c6be..3193b5a98ecf 100644 --- a/app/Views/welcome_message.php +++ b/app/Views/welcome_message.php @@ -200,9 +200,10 @@