Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue/fix phpstan has method check #31

Merged
merged 5 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Cake\ORM\Query\SelectQuery;
use Cake\ORM\Table;
use CakeDC\PHPStan\Rule\Traits\ParseClassNameFromArgTrait;
use InvalidArgumentException;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\MethodCall;
Expand All @@ -22,6 +23,8 @@
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
Expand Down Expand Up @@ -102,7 +105,11 @@ public function processNode(Node $node, Scope $scope): array
if (empty($referenceClasses)) {
return [];
}
$details = $this->getDetails($referenceClasses, $node->name->name, $args);
try {
$details = $this->getDetails($referenceClasses, $node->name->name, $args);
} catch (InvalidArgumentException) {
return [];
}
if ($details === null) {
return [];
}
Expand Down Expand Up @@ -273,6 +280,8 @@ protected function getOptionsFromArray(Array_ $source, array $options): array
foreach ($source->items as $item) {
if (isset($item->key) && $item->key instanceof String_) {
$options[$item->key->value] = $item->value;
} else {
throw new InvalidArgumentException('Rule is ignored because one option key is not string');
}
}

Expand Down Expand Up @@ -337,18 +346,32 @@ protected function getSpecificFinderOptions(array $details, Scope $scope): array
}
$object = new ObjectType($tableClass);
$finderMethod = 'find' . $finder;
if ($object->hasMethod($finderMethod)->no()) {
if (!$object->hasMethod($finderMethod)->yes()) {
return [];
}
$method = $object->getMethod($finderMethod, $scope);
$parameters = $method->getVariants()[0]->getParameters();

if (!isset($parameters[1])) {
return [];
}
if (count($parameters) === 2) {
//Backward compatibility with CakePHP 4 finder structure, findSomething($query, array $options)
$secondParam = $parameters[1];
$paramType = $secondParam->getType();
if (
$parameters[1]->getName() === 'options'
&& !$secondParam->isVariadic()
&& ($paramType instanceof MixedType || $paramType instanceof ArrayType)
) {
return [];
}
}
foreach ($parameters as $key => $param) {
if ($key === 0) {
if ($key === 0 || $param->isVariadic()) {
continue;
}

$specificFinderOptions[$param->getName()] = $param;
}

Expand Down
72 changes: 72 additions & 0 deletions tests/TestCase/Rule/Model/Fake/FailingOrmFindRuleItemsLogic.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,77 @@ public function process()
$Table->find(
'featured', //custom finder is known but required options are missing
);
$Table->find(
'unkonwn',
);
$Table->find('legacy');//Legacy should ignore params check
$Table->find('legacy', [//Legacy should ignore params check
'sort' => ['Notes.note' => 'ASC'],
]);
$Table->find('legacy', [//Legacy should ignore params check
'sort' => ['Notes.note' => 'ASC'],
'type' => 'featured',
'active' => false,
]);
$Table->find('optionsPacked');
$Table->find('optionsPacked', [//Legacy should ignore params check
'sort' => ['Notes.note' => 'ASC'],
]);
$Table->find('optionsPacked', [//Legacy should ignore params check
'sort' => ['Notes.note' => 'ASC'],
'labelField' => 'id',
]);
$Table->find('optionsPacked', [//Legacy should ignore params check
'sort' => ['Notes.note' => 'ASC'],
'labelField' => 'id',
]);
$Table->find(
'optionsPacked',
sort: ['Notes.note' => 'ASC'],
labelField: 'id'
);
$Table->find(
'optionsPacked',
sort: ['Notes.note' => 'ASC'],
labelField: 'id'
);
$Table->find('argsPacked');
$Table->find(
'argsPacked',
sort: ['Notes.note' => 'ASC'],
groupLabel: 'type'
);
$Table->find('argsPacked', [
'sort' => ['Notes.note' => 'ASC'],
'groupLabel' => 'id',
]);
$Table->find('twoArgsButNotLegacy', [
'sort' => ['Notes.note' => 'ASC'],
'myType' => 'featured',
]);
$Table->find('twoArgsButNotLegacy', [
'sort' => ['Notes.note' => 'ASC'],
]);
$Table->find(
'twoArgsButNotLegacy',
sort: ['Notes.note' => 'ASC'],
myType: 'featured'
);
$Table->find('twoArgsButNotLegacy');
$Table->find(
'twoArgsButNotLegacy',
sort: ['Notes.note' => 'ASC'],
myType: 19
);
$field = $Table->getTypeTestTwoArgsButNotLegacy();
$value = 'featured';
$Table->find('twoArgsButNotLegacy', [$field => $value]);
$Table->find('twoArgsButNotLegacy', [$field => 'test']);
$Table->find('twoArgsButNotLegacy', [$Table->getTypeTestTwoArgsButNotLegacy() => $value]);
$Table->find('twoArgsButNotLegacy', [$Table->getTypeTestTwoArgsButNotLegacy() => 'sample']);
$Table->find('twoArgsButNotLegacy', ...[$field => $value]);
$Table->find('twoArgsButNotLegacy', ...[$field => 'test']);
$Table->find('twoArgsButNotLegacy', ...[$Table->getTypeTestTwoArgsButNotLegacy() => $value]);
$Table->find('twoArgsButNotLegacy', ...[$Table->getTypeTestTwoArgsButNotLegacy() => 'sample']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ public function testRule(): void
['Call to App\Model\Table\NotesTable::find is missing required finder option "fun".', 134],
['Call to App\Model\Table\NotesTable::find is missing required finder option "year".', 138],
['Call to App\Model\Table\NotesTable::find is missing required finder option "fun".', 138],
['Call to App\Model\Table\NotesTable::find is missing required finder option "myType".', 189],
['Call to App\Model\Table\NotesTable::find is missing required finder option "myType".', 197],
['Call to App\Model\Table\NotesTable::find with option "myType" (string) does not accept int.', 198],
]);
}

Expand Down
69 changes: 69 additions & 0 deletions tests/test_app/Model/Table/NotesTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ public function warning(): array
$this->Users->find('all', order: ['Users.id' => 'DESC'], limit: 12);
$entity = $this->saveOrFail($entity);
}
$this->find('optionsPacked');
$this->find(
'twoArgsButNotLegacy',
sort: ['Notes.note' => 'ASC'],
myType: 'featured'
);
$this->find('argsPacked');

return [
'type' => 'warning',
Expand Down Expand Up @@ -88,4 +95,66 @@ public function findFeatured(SelectQuery $query, string|int $year, bool $fun): S
return $query->where($where)
->orderBy(['Notes.created' => 'DESC']);
}

/**
* @param \Cake\ORM\Query\SelectQuery $query
* @param string $myType
* @return \Cake\ORM\Query\SelectQuery
*/
public function findTwoArgsButNotLegacy(SelectQuery $query, string $myType): SelectQuery
{
$where = [
'year <=' => 2010,
'type' => $myType,
];

return $query->where($where)
->orderBy(['Notes.created' => 'DESC']);
}

/**
* @param \Cake\ORM\Query\SelectQuery $query
* @param array<string, mixed> $options
* @return \Cake\ORM\Query\SelectQuery
*/
public function findLegacy(SelectQuery $query, array $options): SelectQuery
{
return $query
->where([
'type' => $options['type'] ?? 'normal',
'active' => $options['active'] ?? false,
]);
}

/**
* @param \Cake\ORM\Query\SelectQuery $query
* @param mixed ...$options
* @return \Cake\ORM\Query\SelectQuery
*/
public function findOptionsPacked(SelectQuery $query, mixed ...$options): SelectQuery
{
return $query->select([
$options['labelField'] ?? 'note',
]);
}

/**
* @param \Cake\ORM\Query\SelectQuery $query
* @param mixed ...$args
* @return \Cake\ORM\Query\SelectQuery
*/
public function findArgsPacked(SelectQuery $query, mixed ...$args): SelectQuery
{
return $query->select([
$args['groupLabel'] ?? 'note',
]);
}

/**
* @return string
*/
public function getTypeTestTwoArgsButNotLegacy(): string
{
return 'myType';
}
}
Loading