Skip to content

Commit 57b2cc6

Browse files
eiriksmmglaman
andauthored
Make it possible to opt out of loading api.php files (#641)
* Make it possible to opt out of loading api.php files * Do not use empty * Refactor and rename logic * add bleedingEdge config * Rename bleeding edge toggle, hardcoded core deprecated hooks * clean up logic for hardcoded hooks when miss on hook function reflection * only check deprecated_field_widget_hooks on 9.x --------- Co-authored-by: Matt Glaman <[email protected]>
1 parent fa79d1f commit 57b2cc6

File tree

7 files changed

+80
-10
lines changed

7 files changed

+80
-10
lines changed

bleedingEdge.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
parameters:
2+
drupal:
3+
bleedingEdge:
4+
checkDeprecatedHooksInApiFiles: true

extension.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ parameters:
1717
- stubs/Twig/functions.stub
1818
drupal:
1919
drupal_root: '%currentWorkingDirectory%'
20+
bleedingEdge:
21+
checkDeprecatedHooksInApiFiles: false
2022
entityMapping:
2123
aggregator_feed:
2224
class: Drupal\aggregator\Entity\Feed
@@ -233,6 +235,9 @@ parameters:
233235
parametersSchema:
234236
drupal: structure([
235237
drupal_root: string()
238+
bleedingEdge: structure([
239+
checkDeprecatedHooksInApiFiles: boolean()
240+
])
236241
entityMapping: arrayOf(anyOf(
237242
structure([
238243
class: string()

src/Drupal/DrupalAutoloader.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class DrupalAutoloader
5757

5858
public function register(Container $container): void
5959
{
60+
/**
61+
* @var array{drupal_root: string, bleedingEdge: array{checkDeprecatedHooksInApiFiles: bool}} $drupalParams
62+
*/
6063
$drupalParams = $container->getParameter('drupal');
6164
$drupalRoot = realpath($drupalParams['drupal_root']);
6265
$finder = new DrupalFinder();
@@ -94,6 +97,7 @@ public function register(Container $container): void
9497
$this->addThemeNamespaces();
9598
$this->registerPs4Namespaces($this->namespaces);
9699
$this->loadLegacyIncludes();
100+
$checkDeprecatedHooksInApiFiles = $drupalParams['bleedingEdge']['checkDeprecatedHooksInApiFiles'];
97101

98102
foreach ($this->moduleData as $extension) {
99103
$this->loadExtension($extension);
@@ -112,7 +116,7 @@ public function register(Container $container): void
112116
$this->loadAndCatchErrors($module_dir . '/' . $module_name . '.post_update.php');
113117
}
114118
// Add .api.php
115-
if (file_exists($module_dir . '/' . $module_name . '.api.php')) {
119+
if ($checkDeprecatedHooksInApiFiles && file_exists($module_dir . '/' . $module_name . '.api.php')) {
116120
$this->loadAndCatchErrors($module_dir . '/' . $module_name . '.api.php');
117121
}
118122
// Add misc .inc that are magically allowed via hook_hook_info.

src/Rules/Deprecations/DeprecatedHookImplementation.php

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@
1313
class DeprecatedHookImplementation implements Rule
1414
{
1515

16-
/**
17-
* @var \PHPStan\Reflection\ReflectionProvider
18-
*/
19-
protected $reflectionProvider;
16+
protected ReflectionProvider $reflectionProvider;
2017

2118
public function __construct(ReflectionProvider $reflectionProvider)
2219
{
@@ -48,6 +45,36 @@ public function processNode(Node $node, Scope $scope) : array
4845

4946
$hook_name_node = new Name($hook_name);
5047
if (!$this->reflectionProvider->hasFunction($hook_name_node, $scope)) {
48+
// @todo replace this hardcoded logic with something more intelligent and extensible.
49+
if ($hook_name === 'hook_field_widget_form_alter') {
50+
return $this->buildError(
51+
$function_name,
52+
$hook_name,
53+
'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_single_element_form_alter instead.'
54+
);
55+
}
56+
if (str_starts_with($hook_name, 'hook_field_widget_') && str_ends_with($hook_name, '_form_alter')) {
57+
return $this->buildError(
58+
$function_name,
59+
'hook_field_widget_WIDGET_TYPE_form_alter',
60+
'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_single_element_WIDGET_TYPE_form_alter instead.'
61+
);
62+
}
63+
if ($hook_name === 'hook_field_widget_multivalue_form_alter') {
64+
return $this->buildError(
65+
$function_name,
66+
$hook_name,
67+
'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_complete_form_alter instead.'
68+
);
69+
}
70+
if (str_starts_with($hook_name, 'hook_field_widget_multivalue_') && str_ends_with($hook_name, '_form_alter')) {
71+
return $this->buildError(
72+
$function_name,
73+
'hook_field_widget_multivalue_WIDGET_TYPE_form_alter',
74+
'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_complete_WIDGET_TYPE_form_alter instead.'
75+
);
76+
}
77+
5178
return [];
5279
}
5380

@@ -56,12 +83,15 @@ public function processNode(Node $node, Scope $scope) : array
5683
return [];
5784
}
5885

59-
$deprecation_description = $reflection->getDeprecatedDescription();
60-
$deprecation_message = $deprecation_description !== null ? " $deprecation_description" : ".";
86+
return $this->buildError($function_name, $hook_name, $reflection->getDeprecatedDescription());
87+
}
6188

89+
private function buildError(string $function_name, string $hook_name, ?string $deprecated_description): array
90+
{
91+
$deprecated_description = $deprecated_description !== null ? " $deprecated_description" : ".";
6292
return [
6393
RuleErrorBuilder::message(
64-
"Function $function_name implements $hook_name which is deprecated$deprecation_message",
94+
"Function $function_name implements $hook_name which is deprecated$deprecated_description",
6595
)->build()
6696
];
6797
}

tests/fixtures/config/phpunit-drupal-phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ parameters:
1616
includes:
1717
- ../../../extension.neon
1818
- ../../../rules.neon
19+
- ../../../bleedingEdge.neon
1920
- ../../../vendor/phpstan/phpstan-deprecation-rules/rules.neon

tests/src/Rules/DeprecatedHookImplementationTest.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
namespace mglaman\PHPStanDrupal\Tests\Rules;
44

55
use mglaman\PHPStanDrupal\Rules\Deprecations\DeprecatedHookImplementation;
6+
use mglaman\PHPStanDrupal\Tests\DrupalRuleTestCase;
67
use PHPStan\Reflection\ReflectionProvider;
78
use PHPStan\Rules\Rule;
8-
use PHPStan\Testing\RuleTestCase;
99

1010
/**
1111
* Test the rule to detected deprecated hook implementations.
1212
*/
13-
class DeprecatedHookImplementationTest extends RuleTestCase {
13+
class DeprecatedHookImplementationTest extends DrupalRuleTestCase {
1414

1515
/**
1616
* {@inheritdoc}
@@ -35,6 +35,21 @@ public function testRule() : void {
3535
5,
3636
],
3737
]);
38+
[$version] = explode('.', \Drupal::VERSION, 2);
39+
if ($version === '9') {
40+
$this->analyse([__DIR__ . '/data/deprecated_field_widget_hooks.module'],
41+
[
42+
[
43+
'Function deprecated_field_widget_hooks_field_widget_form_alter implements hook_field_widget_form_alter which is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. Use
44+
hook_field_widget_single_element_form_alter instead.',
45+
5
46+
],
47+
[
48+
'Function deprecated_field_widget_hooks_field_widget_textfield_form_alter implements hook_field_widget_WIDGET_TYPE_form_alter which is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_single_element_WIDGET_TYPE_form_alter instead.',
49+
9
50+
]
51+
]);
52+
}
3853
}
3954

4055
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace DerecatedFieldWidgetHooks;
4+
5+
function deprecated_field_widget_hooks_field_widget_form_alter() {
6+
7+
}
8+
9+
function deprecated_field_widget_hooks_field_widget_textfield_form_alter() {
10+
11+
}

0 commit comments

Comments
 (0)