Skip to content

Commit

Permalink
Allow specific argument type checks for factories (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan authored Nov 14, 2023
1 parent 4b44060 commit dd65314
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 4 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ This extension provides the following features:

### Rules

* Checks if the string argument passed to `config()` or `model()` function is a valid class string extending `CodeIgniter\Config\BaseConfig` or `CodeIgniter\Model`, respectively. This can be turned off by setting `codeigniter.checkArgumentTypeOfFactories: false` in your `phpstan.neon`.
* Checks if the string argument passed to `config()` or `model()` function is a valid class string extending
`CodeIgniter\Config\BaseConfig` or `CodeIgniter\Model`, respectively. This can be turned off by setting
`codeigniter.checkArgumentTypeOfFactories: false` in your `phpstan.neon`. For fine-grained control, you can
individually choose which factory function to disable using `codeigniter.checkArgumentTypeOfConfig` and
`codeigniter.checkArgumentTypeOfModel`. **NOTE:** Setting `codeigniter.checkArgumentTypeOfFactories: false` will effectively
bypass the two specific options.
* Checks if the string argument passed to `service()` or `single_service()` function is a valid service name. This can be turned off by setting `codeigniter.checkArgumentTypeOfServices: false` in your `phpstan.neon`.
* Disallows instantiating cache handlers using `new` and suggests to use the `CacheFactory` class instead.
* Disallows instantiating `FrameworkException` classes using `new`.
Expand Down
7 changes: 7 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ parameters:
additionalServices: []
notStringFormattedFields: []
checkArgumentTypeOfFactories: true
checkArgumentTypeOfConfig: true
checkArgumentTypeOfModel: true
checkArgumentTypeOfServices: true

parametersSchema:
Expand All @@ -21,6 +23,8 @@ parametersSchema:
additionalServices: listOf(string())
notStringFormattedFields: arrayOf(string())
checkArgumentTypeOfFactories: bool()
checkArgumentTypeOfConfig: bool()
checkArgumentTypeOfModel: bool()
checkArgumentTypeOfServices: bool()
])

Expand Down Expand Up @@ -76,6 +80,9 @@ services:
# conditional rules
-
class: CodeIgniter\PHPStan\Rules\Functions\FactoriesFunctionArgumentTypeRule
arguments:
checkArgumentTypeOfConfig: %codeigniter.checkArgumentTypeOfConfig%
checkArgumentTypeOfModel: %codeigniter.checkArgumentTypeOfModel%

-
class: CodeIgniter\PHPStan\Rules\Functions\ServicesFunctionArgumentTypeRule
Expand Down
20 changes: 18 additions & 2 deletions src/Rules/Functions/FactoriesFunctionArgumentTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,22 @@ final class FactoriesFunctionArgumentTypeRule implements Rule
'model' => Model::class,
];

/**
* @var array<string, bool>
*/
private array $argumentTypeCheck = [];

public function __construct(
private readonly ReflectionProvider $reflectionProvider,
private readonly FactoriesReturnTypeHelper $factoriesReturnTypeHelper
) {}
private readonly FactoriesReturnTypeHelper $factoriesReturnTypeHelper,
bool $checkArgumentTypeOfConfig,
bool $checkArgumentTypeOfModel
) {
$this->argumentTypeCheck = [
'config' => $checkArgumentTypeOfConfig,
'model' => $checkArgumentTypeOfModel,
];
}

public function getNodeType(): string
{
Expand Down Expand Up @@ -104,6 +116,10 @@ public function processNode(Node $node, Scope $scope): array
}

if (! (new ObjectType($this->instanceofMap[$function]))->isSuperTypeOf($returnType)->yes()) {
if (! $this->argumentTypeCheck[$function]) {
return [];
}

return [RuleErrorBuilder::message(sprintf(
'Argument #1 $%s (%s) passed to function %s does not extend %s.',
$firstParameter->getName(),
Expand Down
19 changes: 19 additions & 0 deletions tests/Fixtures/Rules/Functions/bug-8.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) 2023 CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

class NotificationModel
{
public string $returnType = 'object';
}

model(NotificationModel::class);
21 changes: 20 additions & 1 deletion tests/Rules/Functions/FactoriesFunctionArgumentTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,24 @@ final class FactoriesFunctionArgumentTypeRuleTest extends RuleTestCase
{
use AdditionalConfigFilesTrait;

private bool $checkArgumentTypeOfConfig;
private bool $checkArgumentTypeOfModel;

protected function setUp(): void
{
parent::setUp();

$this->checkArgumentTypeOfConfig = true;
$this->checkArgumentTypeOfModel = true;
}

protected function getRule(): Rule
{
return new FactoriesFunctionArgumentTypeRule(
self::createReflectionProvider(),
self::getContainer()->getByType(FactoriesReturnTypeHelper::class)
self::getContainer()->getByType(FactoriesReturnTypeHelper::class),
$this->checkArgumentTypeOfConfig,
$this->checkArgumentTypeOfModel
);
}

Expand Down Expand Up @@ -90,4 +103,10 @@ public function testRule(): void
],
]);
}

public function testAllowNonModelClassesOnModelCall(): void
{
$this->checkArgumentTypeOfModel = false;
$this->analyse([__DIR__ . '/../../Fixtures/Rules/Functions/bug-8.php'], []);
}
}

0 comments on commit dd65314

Please sign in to comment.