Skip to content

Commit 4f91030

Browse files
committed
Properly handle lazy builder callbacks
1 parent 652a9d2 commit 4f91030

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

src/Rules/Drupal/RenderCallbackRule.php

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public function processNode(Node $node, Scope $scope): array
4545
return [];
4646
}
4747
// @see https://www.drupal.org/node/2966725
48-
$keysToCheck = ['#pre_render', '#post_render', '#lazy_builder', '#access_callback'];
48+
49+
$keysToCheck = ['#pre_render', '#post_render', '#access_callback', '#lazy_builder'];
4950
$keySearch = array_search($key->value, $keysToCheck, true);
5051
if ($keySearch === false) {
5152
return [];
@@ -72,6 +73,25 @@ public function processNode(Node $node, Scope $scope): array
7273
if (!$item instanceof Node\Expr\ArrayItem) {
7374
continue;
7475
}
76+
// '#lazy_builder' has two items, callback and args. Others are direct callbacks.
77+
// Lazy builder in Renderer: $elements['#lazy_builder'][0], $elements['#lazy_builder'][1]
78+
if ($keyChecked === '#lazy_builder') {
79+
if (!$item->value instanceof Node\Expr\Array_) {
80+
$errors[] = RuleErrorBuilder::message(
81+
sprintf("%s callback %s at key '%s' is not callable.", $keyChecked, $scope->getType($item->value)->describe(VerbosityLevel::value()), $pos)
82+
)->line($item->value->getLine())->build();
83+
continue;
84+
}
85+
86+
if (count($item->value->items) !== 2) {
87+
$errors[] = RuleErrorBuilder::message(
88+
sprintf("%s callback %s at key '%s' is not valid. First value must be a callback and second value its arguments.", $keyChecked, $scope->getType($item->value)->describe(VerbosityLevel::value()), $pos)
89+
)->line($item->value->getLine())->build();
90+
continue;
91+
}
92+
// Replace $item with our nested callback.
93+
$item = $item->value->items[0];
94+
}
7595
$errorLine = $item->value->getLine();
7696
$type = $this->getType($item->value, $scope);
7797

@@ -141,11 +161,10 @@ public function processNode(Node $node, Scope $scope): array
141161
$errors[] = RuleErrorBuilder::message(
142162
sprintf("%s value '%s' at key '%s' is invalid.", $keyChecked, $type->describe(VerbosityLevel::value()), $pos)
143163
)->line($errorLine)->tip($tip)->build();
144-
}else {
164+
} else {
145165
$errors[] = RuleErrorBuilder::message(
146166
sprintf("%s value '%s' at key '%s' is invalid.", $keyChecked, $type->describe(VerbosityLevel::value()), $pos)
147-
)->line($errorLine)->tip('Open an issue https://github.com/mglaman/phpstan-drupal/issues/new with this error.')
148-
->build();
167+
)->line($errorLine)->build();
149168
}
150169
}
151170

tests/src/Rules/PreRenderCallbackRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@ public function fileData(): \Generator
7777
__DIR__ . '/../../fixtures/drupal/modules/pre_render_callback_rule/src/RenderCallbackInterfaceObject.php',
7878
[]
7979
];
80+
yield [
81+
__DIR__ . '/../../fixtures/drupal/modules/pre_render_callback_rule/src/LazyBuilderWithConstant.php',
82+
[
83+
[
84+
"#lazy_builder value 'non-empty-string' at key '4' is invalid.",
85+
17,
86+
"Refactor concatenation of `static::class` with method name to an array callback: [static::class, 'lazyBuilder']"
87+
]
88+
]
89+
];
8090
yield [
8191
__DIR__ . '/../../fixtures/drupal/modules/pre_render_callback_rule/src/FormWithClosure.php',
8292
[

0 commit comments

Comments
 (0)