Skip to content

Commit

Permalink
Support concatenated callables in RenderCallbackRule (#544)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Glaman <nmd.matt@gmail.com>
  • Loading branch information
Boegie and mglaman authored May 10, 2023
1 parent 266474c commit 7340cbc
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 4 deletions.
16 changes: 12 additions & 4 deletions src/Rules/Drupal/RenderCallbackRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,18 @@ private function doProcessNode(Node\Expr $node, Scope $scope, string $keyChecked
)->line($errorLine)
->tip('Change record: https://www.drupal.org/node/2966725.')
->build();
} elseif (!$trustedCallbackType->isSuperTypeOf($type)->yes()) {
$errors[] = RuleErrorBuilder::message(
sprintf("%s callback class %s at key '%s' does not implement Drupal\Core\Security\TrustedCallbackInterface.", $keyChecked, $constantStringType->describe(VerbosityLevel::value()), $pos)
)->line($errorLine)->tip('Change record: https://www.drupal.org/node/2966725.')->build();
} else {
// @see \PHPStan\Type\Constant\ConstantStringType::isCallable
preg_match('#^([a-zA-Z_\\x7f-\\xff\\\\][a-zA-Z0-9_\\x7f-\\xff\\\\]*)::([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)\\z#', $constantStringType->getValue(), $matches);
if (count($matches) === 0) {
$errors[] = RuleErrorBuilder::message(
sprintf("%s callback %s at key '%s' is not callable.", $keyChecked, $constantStringType->describe(VerbosityLevel::value()), $pos)
)->line($errorLine)->build();
} elseif (!$trustedCallbackType->isSuperTypeOf(new ObjectType($matches[1]))->yes()) {
$errors[] = RuleErrorBuilder::message(
sprintf("%s callback class %s at key '%s' does not implement Drupal\Core\Security\TrustedCallbackInterface.", $keyChecked, $constantStringType->describe(VerbosityLevel::value()), $pos)
)->line($errorLine)->tip('Change record: https://www.drupal.org/node/2966725.')->build();
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions tests/src/Rules/RenderCallbackRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ public static function fileData(): \Generator
],
];
}

yield [
__DIR__ . '/data/bug-543.php',
[]
];
}


Expand Down
105 changes: 105 additions & 0 deletions tests/src/Rules/data/bug-543.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

namespace Bug543;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Security\TrustedCallbackInterface;

/**
* Code snippets from \Drupal\Tests\Core\Render\RendererTest.
*/
class TestClass {

/**
* Provides a list of both booleans.
*
* @return array
*/
public function providerAccessValues() {
return [
[FALSE],
[TRUE],
[AccessResult::forbidden()],
[AccessResult::allowed()],
];
}

/**
* @dataProvider providerAccessValues
*/
public function testRenderWithAccessControllerResolved($access) {

switch ($access) {
case AccessResult::allowed():
$method = 'accessResultAllowed';
break;

case AccessResult::forbidden():
$method = 'accessResultForbidden';
break;

case FALSE:
$method = 'accessFalse';
break;

case TRUE:
$method = 'accessTrue';
break;
}

$build = [
'#access_callback' => TestAccessClass::class . '::' . $method,
];
}

public function bug543AccessResultAllowed(): void {
$build = [
'#access_callback' => TestAccessClass::class . '::accessResultAllowed',
];
}

public function bug543AccessResultForbidden(): void {
$build = [
'#access_callback' => TestAccessClass::class . '::accessResultForbidden',
];
}

public function bug543AccessFalse(): void {
$build = [
'#access_callback' => TestAccessClass::class . '::accessFalse',
];
}

public function bug543AccessTrue(): void {
$build = [
'#access_callback' => TestAccessClass::class . '::accessTrue',
];
}
}

class TestAccessClass implements TrustedCallbackInterface {

public static function accessTrue() {
return TRUE;
}

public static function accessFalse() {
return FALSE;
}

public static function accessResultAllowed() {
return AccessResult::allowed();
}

public static function accessResultForbidden() {
return AccessResult::forbidden();
}

/**
* {@inheritdoc}
*/
public static function trustedCallbacks() {
return ['accessTrue', 'accessFalse', 'accessResultAllowed', 'accessResultForbidden'];
}

}

0 comments on commit 7340cbc

Please sign in to comment.