Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ parameters:
addBehaviorExistsClassRule: true
tableGetMatchOptionsTypesRule: true
ormSelectQueryFindMatchOptionsTypesRule: true
disallowEntityArrayAccessRule: false
disallowEntityArrayAccessRule: true
getMailerExistsClassRule: true
loadComponentExistsClassRule: true
controllerMethodMustBeUsedRule: true
Expand Down
16 changes: 15 additions & 1 deletion src/Rule/Model/DisallowEntityArrayAccessRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@
use Cake\Datasource\EntityInterface;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Scalar\String_;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

class DisallowEntityArrayAccessRule implements Rule
{
/**
* @var list<string>
*/
protected array $allowedKeys = [
'_matchingData',
'_joinData',
'_ids',
];

/**
* @inheritDoc
*/
Expand All @@ -38,9 +48,13 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

if ($node->dim instanceof String_ && in_array($node->dim->value, $this->allowedKeys, true)) {
return [];
}

return [
RuleErrorBuilder::message(sprintf(
'Array access to entity to %s is not allowed, access as object instead',
'Array access to entity %s is not allowed, access as object instead',
$reflection->getName(),
))
->identifier('cake.entity.arrayAccess')
Expand Down
24 changes: 14 additions & 10 deletions tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,28 @@ public function testRule(): void
// each error consists of the asserted error message, and the asserted error file line
$this->analyse([__DIR__ . '/Fake/FailingEntityUseLogic.php'], [
[
'Array access to entity to App\Model\Entity\Note is not allowed, access as object instead',
23, // asserted error line
'Array access to entity App\Model\Entity\Note is not allowed, access as object instead',
24, // asserted error line
],
[
'Array access to entity to App\Model\Entity\Note is not allowed, access as object instead',
24, // asserted error line
'Array access to entity App\Model\Entity\Note is not allowed, access as object instead',
25, // asserted error line
],
[
'Array access to entity App\Model\Entity\Note is not allowed, access as object instead',
27, // asserted error line
],
[
'Array access to entity to Cake\Datasource\EntityInterface is not allowed, access as object instead',
29,
'Array access to entity Cake\Datasource\EntityInterface is not allowed, access as object instead',
36,
],
[
'Array access to entity to App\Model\Entity\User is not allowed, access as object instead',
31, // asserted error line
'Array access to entity App\Model\Entity\User is not allowed, access as object instead',
38, // asserted error line
],
[
'Array access to entity to App\Model\Entity\Note is not allowed, access as object instead',
37, // asserted error line
'Array access to entity App\Model\Entity\Note is not allowed, access as object instead',
44, // asserted error line
],
]);
}
Expand Down
13 changes: 13 additions & 0 deletions tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace CakeDC\PHPStan\Test\TestCase\Rule\Model\Fake;

use App\Model\Entity\Note;
use Cake\ORM\Locator\LocatorAwareTrait;
use SplFixedArray;

Expand All @@ -23,6 +24,12 @@ public function execute(): array
$note = $entity['note'];
$notable = 'Notable ' . $entity['note'];
$noted = $entity->note;
$note2 = $entity[Note::FIELD_NOTE];
$matching = $entity['_matchingData'];//allowed access to _matchingData
$matchingUser = $entity['_matchingData']['Users'];//allowed access to _matchingData
$joinData = $entity['_joinData'];//allowed access to _matchingData
$joinDataPosts = $entity['_joinData']['Posts'];//allowed access to _matchingData
$ids = $entity['_ids'];//allowed access to _matchingData

//Unknown entity
$unknown = $this->fetchTable('UnknownRecords')->get(20);
Expand All @@ -39,6 +46,12 @@ public function execute(): array
'noted' => $noted,
'notable' => $notable,
'date' => $date,
'note2' => $note2,
'matchingData' => $matching,
'matchingUser' => $matchingUser,
'joinData' => $joinData,
'joinDataPosts' => $joinDataPosts,
'ids' => $ids,
];
}
}
1 change: 1 addition & 0 deletions tests/test_app/Model/Entity/Note.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
*/
class Note extends Entity
{
public const FIELD_NOTE = 'note';
}