From a87ed764c21193c54c0da53913aee0e6084fa594 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 24 Aug 2026 19:46:19 +0800 Subject: [PATCH] refactor: fix phpstan errors in `View` --- system/Config/Factories.php | 1 + system/Traits/PropertiesTrait.php | 4 + system/View/Cell.php | 16 ++-- system/View/Cells/Cell.php | 6 ++ system/View/Parser.php | 6 +- tests/system/View/ParserPluginTest.php | 7 +- tests/system/View/ParserTest.php | 9 +- tests/system/View/TableTest.php | 35 +++++--- utils/phpstan-baseline/argument.type.neon | 27 +----- .../phpstan-baseline/assign.propertyType.neon | 7 +- utils/phpstan-baseline/loader.neon | 2 +- utils/phpstan-baseline/method.notFound.neon | 27 +----- .../missingType.iterableValue.neon | 89 +------------------ .../staticMethod.notFound.neon | 12 +-- 14 files changed, 66 insertions(+), 182 deletions(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index ab5343d15fb8..2d11632f7c05 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -26,6 +26,7 @@ * large performance boost and helps keep code clean of lengthy * instantiation checks. * + * @method static object|null cells(string $alias, array $options = []) * @method static BaseConfig|null config(...$arguments) * @method static Model|null models(string $alias, array $options = [], ?ConnectionInterface &$conn = null) * @see \CodeIgniter\Config\FactoriesTest diff --git a/system/Traits/PropertiesTrait.php b/system/Traits/PropertiesTrait.php index bf0571b2f05b..dd14e6c68d3a 100644 --- a/system/Traits/PropertiesTrait.php +++ b/system/Traits/PropertiesTrait.php @@ -27,6 +27,8 @@ trait PropertiesTrait { /** * Attempts to set the values of public class properties. + * + * @param array $params */ final public function fill(array $params): self { @@ -61,6 +63,8 @@ public function getProperties(object $obj): array /** * Get the protected and private properties of the class and return as an array. + * + * @return list */ final public function getNonPublicProperties(): array { diff --git a/system/View/Cell.php b/system/View/Cell.php index 3a3e9595e3f5..5e0eda26255b 100644 --- a/system/View/Cell.php +++ b/system/View/Cell.php @@ -78,9 +78,7 @@ public function render(string $library, $params = null, int $ttl = 0, ?string $c { [$instance, $method] = $this->determineClass($library); - $class = is_object($instance) - ? $instance::class - : null; + $class = $instance::class; $params = $this->prepareParams($params); @@ -160,6 +158,8 @@ public function prepareParams($params) /** * Given the library string, attempts to determine the class and method * to call. + * + * @return array{object, string} */ protected function determineClass(string $library): array { @@ -198,6 +198,8 @@ protected function determineClass(string $library): array /** * Renders a cell that extends the BaseCell class. + * + * @param array $params */ final protected function renderCell(BaseCell $instance, string $method, array $params): string { @@ -234,6 +236,10 @@ final protected function renderCell(BaseCell $instance, string $method, array $p * Returns the values from $params that match the parameters * for a method, in the order they are defined. This allows * them to be passed directly into the method. + * + * @param array $params + * + * @return list */ private function getMethodParams(BaseCell $instance, string $method, array $params): array { @@ -262,9 +268,9 @@ private function getMethodParams(BaseCell $instance, string $method, array $para * * @todo Determine if this can be refactored to use $this-getMethodParams(). * - * @param object $instance + * @param array $params */ - final protected function renderSimpleClass($instance, string $method, array $params, string $class): string + final protected function renderSimpleClass(object $instance, string $method, array $params, string $class): string { // Try to match up the parameter list we were provided // with the parameter name in the callback method. diff --git a/system/View/Cells/Cell.php b/system/View/Cells/Cell.php index 581499028e4b..ae1586dbcbcb 100644 --- a/system/View/Cells/Cell.php +++ b/system/View/Cells/Cell.php @@ -71,6 +71,8 @@ public function setView(string $view) * current scope and captures the output buffer instead of * relying on the view service. * + * @param array $data + * * @throws LogicException */ final protected function view(?string $view, array $data = []): string @@ -129,6 +131,10 @@ public function __toString(): string /** * Allows the developer to define computed properties * as methods with `get` prefixed to the protected/private property name. + * + * @param array $properties + * + * @return array */ private function includeComputedProperties(array $properties): array { diff --git a/system/View/Parser.php b/system/View/Parser.php index af4ebea346ac..56c3b093bc5d 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -543,9 +543,9 @@ public function setConditionalDelimiters($leftDelimiter = '{', $rightDelimiter = * Handles replacing a pseudo-variable with the actual content. Will double-check * for escaping brackets. * - * @param array|string $pattern - * @param string $content - * @param string $template + * @param array|string $pattern + * @param string $content + * @param string $template */ protected function replaceSingle($pattern, $content, $template, bool $escape = false): string { diff --git a/tests/system/View/ParserPluginTest.php b/tests/system/View/ParserPluginTest.php index bb458a85ad04..a9312e91168c 100644 --- a/tests/system/View/ParserPluginTest.php +++ b/tests/system/View/ParserPluginTest.php @@ -36,8 +36,11 @@ protected function setUp(): void Services::reset(true); - $this->parser = service('parser'); - $this->validator = service('validation'); + $this->parser = service('parser'); + + $validator = service('validation'); + $this->assertInstanceOf(Validation::class, $validator); + $this->validator = $validator; } public function testCurrentURL(): void diff --git a/tests/system/View/ParserTest.php b/tests/system/View/ParserTest.php index fa6784445e8e..730acb6cf8c1 100644 --- a/tests/system/View/ParserTest.php +++ b/tests/system/View/ParserTest.php @@ -438,6 +438,9 @@ public function testEscHandling($value, $expected = null): void $this->assertSame($expected, \esc($value)); } + /** + * @return iterable> + */ public static function provideEscHandling(): iterable { return [ @@ -543,20 +546,20 @@ public function testNoEscapingSetData(): void { $template = '{ foo | noescape}'; - $this->parser->setData(['foo' => 'http://foo.com'], 'unknown'); + $this->parser->setData(['foo' => 'http://foo.com'], 'unknown'); // @phpstan-ignore argument.type (deliberately invalid context) $this->assertSame('http://foo.com', $this->parser->renderString($template)); } public function testAutoEscaping(): void { - $this->parser->setData(['foo' => 'http://foo.com'], 'unknown'); + $this->parser->setData(['foo' => 'http://foo.com'], 'unknown'); // @phpstan-ignore argument.type (deliberately invalid context) $this->assertSame('html', $this->parser->shouldAddEscaping('{ foo | this | that }')); } public function testAutoEscapingNot(): void { - $this->parser->setData(['foo' => 'http://foo.com'], 'unknown'); + $this->parser->setData(['foo' => 'http://foo.com'], 'unknown'); // @phpstan-ignore argument.type (deliberately invalid context) $this->assertFalse($this->parser->shouldAddEscaping('{ foo | noescape }')); } diff --git a/tests/system/View/TableTest.php b/tests/system/View/TableTest.php index b7946b059138..e09cb9e6631f 100644 --- a/tests/system/View/TableTest.php +++ b/tests/system/View/TableTest.php @@ -201,7 +201,7 @@ public function testPrepArgs(): void $this->assertSame( $expected, - $this->table->prepArgs(['name', 'color', 'size']), + $this->table->prepArgs(['name', 'color', 'size']), // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) ); // with cell attributes @@ -213,7 +213,7 @@ public function testPrepArgs(): void $this->assertSame( $expected, - $this->table->prepArgs(['name', 'color', 'size', ['data' => 'weight', 'class' => 'awesome']]), + $this->table->prepArgs(['name', 'color', 'size', ['data' => 'weight', 'class' => 'awesome']]), // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) ); } @@ -241,7 +241,7 @@ public function testDefaultTemplateKeys(): void ]; foreach ($keys as $key) { - $this->assertArrayHasKey($key, $this->table->defaultTemplate()); + $this->assertArrayHasKey($key, $this->table->defaultTemplate()); // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) } } @@ -251,14 +251,14 @@ public function testCompileTemplate(): void // non default key $this->table->setTemplate(['nonsense' => 'foo']); - $this->table->compileTemplate(); + $this->table->compileTemplate(); // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) $this->assertArrayHasKey('nonsense', $this->table->template); $this->assertSame('foo', $this->table->template['nonsense']); // override default $this->table->setTemplate(['table_close' => '']); - $this->table->compileTemplate(); + $this->table->compileTemplate(); // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) $this->assertArrayHasKey('table_close', $this->table->template); $this->assertSame('', $this->table->template['table_close']); @@ -267,9 +267,9 @@ public function testCompileTemplate(): void public function testMakeColumns(): void { // Test bogus parameters - $this->assertFalse($this->table->makeColumns('invalid_junk')); + $this->assertFalse($this->table->makeColumns('invalid_junk')); // @phpstan-ignore argument.type (deliberately wrong type) $this->assertFalse($this->table->makeColumns([])); - $this->assertFalse($this->table->makeColumns(['one', 'two'], '2.5')); + $this->assertFalse($this->table->makeColumns(['one', 'two'], '2.5')); // @phpstan-ignore argument.type (deliberately wrong type) // Now on to the actual column creation @@ -345,12 +345,12 @@ public function testSetFromArray(): void ]; $this->table->autoHeading = false; - $this->table->setFromArray($data); + $this->table->setFromArray($data); // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) $this->assertEmpty($this->table->heading); $this->table->clear(); - $this->table->setFromArray($data); + $this->table->setFromArray($data); // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) $this->assertCount(2, $this->table->rows); $expected = [ @@ -376,9 +376,9 @@ public function testSetFromObject(): void $dummy->connID = null; $dummy->resultID = null; - $DBResult = new DBResultDummy($dummy->connID, $dummy->resultID); + $DBResult = new DBResultDummy($dummy->connID, $dummy->resultID); // @phpstan-ignore argument.type, argument.type (deliberately null, exercising the fallback path) - $this->table->setFromDBResult($DBResult); + $this->table->setFromDBResult($DBResult); // @phpstan-ignore method.notFound (Testing MockTable::__call() fallback) $expected = [ ['data' => 'name'], @@ -733,7 +733,7 @@ public function testGenerateFromDBResult(): void $dummy = new stdClass(); $dummy->connID = null; $dummy->resultID = null; - $DBResult = new DBResultDummy($dummy->connID, $dummy->resultID); + $DBResult = new DBResultDummy($dummy->connID, $dummy->resultID); // @phpstan-ignore argument.type, argument.type (deliberately null, exercising the fallback path) $table = $this->table->generate($DBResult); @@ -780,6 +780,10 @@ public function testInvalidCallback(): void $this->assertStringContainsString('FredBlueSmall', $generated); } + /** + * @param array $heading + * @param array $row + */ #[DataProvider('orderedColumnUsecases')] public function testAddRowAndGenerateOrderedColumns(array $heading, array $row, string $expectContainsString): void { @@ -792,6 +796,10 @@ public function testAddRowAndGenerateOrderedColumns(array $heading, array $row, $this->assertStringContainsString($expectContainsString, $generated); } + /** + * @param array $heading + * @param array $row + */ #[DataProvider('orderedColumnUsecases')] public function testGenerateOrderedColumns(array $heading, array $row, string $expectContainsString): void { @@ -803,6 +811,9 @@ public function testGenerateOrderedColumns(array $heading, array $row, string $e $this->assertStringContainsString($expectContainsString, $generated); } + /** + * @return iterable, row: array, expectContainsString: string}> + */ public static function orderedColumnUsecases(): iterable { yield from [ diff --git a/utils/phpstan-baseline/argument.type.neon b/utils/phpstan-baseline/argument.type.neon index 4451b62f6419..7b4703df4747 100644 --- a/utils/phpstan-baseline/argument.type.neon +++ b/utils/phpstan-baseline/argument.type.neon @@ -1,4 +1,4 @@ -# total 25 errors +# total 16 errors parameters: ignoreErrors: @@ -66,28 +66,3 @@ parameters: message: '#^Parameter \#1 \$format of method CodeIgniter\\RESTful\\ResourceController\:\:setFormat\(\) expects ''json''\|''xml'', ''Nonsense'' given\.$#' count: 1 path: ../../tests/system/RESTful/ResourceControllerTest.php - - - - message: '#^Parameter \#2 \$context of method CodeIgniter\\View\\Parser\:\:setData\(\) expects ''attr''\|''css''\|''html''\|''js''\|''raw''\|''url''\|null, ''unknown'' given\.$#' - count: 3 - path: ../../tests/system/View/ParserTest.php - - - - message: '#^Parameter \#1 \$array of method CodeIgniter\\View\\Table\:\:makeColumns\(\) expects list\, ''invalid_junk'' given\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Parameter \#1 \$connID of class CodeIgniter\\View\\DBResultDummy constructor expects mysqli, null given\.$#' - count: 2 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Parameter \#2 \$columnLimit of method CodeIgniter\\View\\Table\:\:makeColumns\(\) expects int, string given\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Parameter \#2 \$resultID of class CodeIgniter\\View\\DBResultDummy constructor expects mysqli_result, null given\.$#' - count: 2 - path: ../../tests/system/View/TableTest.php diff --git a/utils/phpstan-baseline/assign.propertyType.neon b/utils/phpstan-baseline/assign.propertyType.neon index a2a5ca34cc63..b00f0bef614d 100644 --- a/utils/phpstan-baseline/assign.propertyType.neon +++ b/utils/phpstan-baseline/assign.propertyType.neon @@ -1,4 +1,4 @@ -# total 20 errors +# total 19 errors parameters: ignoreErrors: @@ -61,8 +61,3 @@ parameters: message: '#^Property Config\\Cookie\:\:\$samesite \(''''\|''Lax''\|''None''\|''Strict''\) does not accept ''Invalid''\.$#' count: 1 path: ../../tests/system/Session/SessionTest.php - - - - message: '#^Property CodeIgniter\\View\\ParserPluginTest\:\:\$validator \(CodeIgniter\\Validation\\Validation\) does not accept CodeIgniter\\Validation\\ValidationInterface\.$#' - count: 1 - path: ../../tests/system/View/ParserPluginTest.php diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index 61ebc04982b4..ea807b354912 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 419 errors +# total 381 errors includes: - argument.type.neon diff --git a/utils/phpstan-baseline/method.notFound.neon b/utils/phpstan-baseline/method.notFound.neon index 67fac4e02b49..675235232552 100644 --- a/utils/phpstan-baseline/method.notFound.neon +++ b/utils/phpstan-baseline/method.notFound.neon @@ -1,4 +1,4 @@ -# total 20 errors +# total 12 errors parameters: ignoreErrors: @@ -41,28 +41,3 @@ parameters: message: '#^Call to an undefined method CodeIgniter\\HTTP\\ResponseInterface\:\:pretend\(\)\.$#' count: 1 path: ../../tests/system/RESTful/ResourceControllerTest.php - - - - message: '#^Call to an undefined method CodeIgniter\\View\\Table\:\:compileTemplate\(\)\.$#' - count: 2 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Call to an undefined method CodeIgniter\\View\\Table\:\:defaultTemplate\(\)\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Call to an undefined method CodeIgniter\\View\\Table\:\:prepArgs\(\)\.$#' - count: 2 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Call to an undefined method CodeIgniter\\View\\Table\:\:setFromArray\(\)\.$#' - count: 2 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Call to an undefined method CodeIgniter\\View\\Table\:\:setFromDBResult\(\)\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon index 333eaa92ae2e..800c217b6610 100644 --- a/utils/phpstan-baseline/missingType.iterableValue.neon +++ b/utils/phpstan-baseline/missingType.iterableValue.neon @@ -1,4 +1,4 @@ -# total 282 errors +# total 265 errors parameters: ignoreErrors: @@ -628,65 +628,10 @@ parameters: path: ../../system/Typography/Typography.php - - message: '#^Method CodeIgniter\\View\\Cell\:\:determineClass\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cell\:\:getMethodParams\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cell\:\:getMethodParams\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cell\:\:renderCell\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cell\:\:renderSimpleClass\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cells\\Cell\:\:fill\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cells/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cells\\Cell\:\:getNonPublicProperties\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cells/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cells\\Cell\:\:includeComputedProperties\(\) has parameter \$properties with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cells/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cells\\Cell\:\:includeComputedProperties\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cells/Cell.php - - - - message: '#^Method CodeIgniter\\View\\Cells\\Cell\:\:view\(\) has parameter \$data with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Cells/Cell.php - - - - message: '#^Method class@anonymous/system/Traits/PropertiesTrait\.php\:49\:\:getProperties\(\) return type has no value type specified in iterable type array\.$#' + message: '#^Method class@anonymous/system/Traits/PropertiesTrait\.php\:51\:\:getProperties\(\) return type has no value type specified in iterable type array\.$#' count: 1 path: ../../system/View/Cells/Cell.php - - - message: '#^Method CodeIgniter\\View\\Parser\:\:replaceSingle\(\) has parameter \$pattern with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/View/Parser.php - - message: '#^Method CodeIgniter\\AutoReview\\ComposerJsonTest\:\:checkConfig\(\) has parameter \$fromComponent with no value type specified in iterable type array\.$#' count: 1 @@ -1381,33 +1326,3 @@ parameters: message: '#^Method CodeIgniter\\Throttle\\ThrottleTest\:\:testTokenTimeCalculationUCs\(\) has parameter \$checkInputs with no value type specified in iterable type array\.$#' count: 1 path: ../../tests/system/Throttle/ThrottleTest.php - - - - message: '#^Method CodeIgniter\\View\\ParserTest\:\:provideEscHandling\(\) return type has no value type specified in iterable type iterable\.$#' - count: 1 - path: ../../tests/system/View/ParserTest.php - - - - message: '#^Method CodeIgniter\\View\\TableTest\:\:orderedColumnUsecases\(\) return type has no value type specified in iterable type iterable\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Method CodeIgniter\\View\\TableTest\:\:testAddRowAndGenerateOrderedColumns\(\) has parameter \$heading with no value type specified in iterable type array\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Method CodeIgniter\\View\\TableTest\:\:testAddRowAndGenerateOrderedColumns\(\) has parameter \$row with no value type specified in iterable type array\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Method CodeIgniter\\View\\TableTest\:\:testGenerateOrderedColumns\(\) has parameter \$heading with no value type specified in iterable type array\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php - - - - message: '#^Method CodeIgniter\\View\\TableTest\:\:testGenerateOrderedColumns\(\) has parameter \$row with no value type specified in iterable type array\.$#' - count: 1 - path: ../../tests/system/View/TableTest.php diff --git a/utils/phpstan-baseline/staticMethod.notFound.neon b/utils/phpstan-baseline/staticMethod.notFound.neon index 3ad7dfbbd356..9dfe25e54b97 100644 --- a/utils/phpstan-baseline/staticMethod.notFound.neon +++ b/utils/phpstan-baseline/staticMethod.notFound.neon @@ -1,17 +1,7 @@ -# total 18 errors +# total 15 errors parameters: ignoreErrors: - - - message: '#^Call to an undefined static method CodeIgniter\\Config\\Factories\:\:cells\(\)\.$#' - count: 1 - path: ../../system/View/Cell.php - - - - message: '#^Call to an undefined static method CodeIgniter\\Config\\Factories\:\:cells\(\)\.$#' - count: 2 - path: ../../tests/system/Config/FactoriesTest.php - - message: '#^Call to an undefined static method CodeIgniter\\Config\\Factories\:\:tedwigs\(\)\.$#' count: 1