Skip to content

Commit 22aaab0

Browse files
authored
Add rule to suggest replacing WP constants with function calls (#276)
1 parent 6686dc2 commit 22aaab0

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed

extension.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ services:
3232
rules:
3333
- SzepeViktor\PHPStan\WordPress\HookCallbackRule
3434
- SzepeViktor\PHPStan\WordPress\HookDocsRule
35+
- SzepeViktor\PHPStan\WordPress\WpConstantFetchRule
3536
parameters:
3637
bootstrapFiles:
3738
- ../../php-stubs/wordpress-stubs/wordpress-stubs.php

src/WpConstantFetchRule.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/**
4+
* Custom rule to discourage using WordPress constants fetchable via functions.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace SzepeViktor\PHPStan\WordPress;
10+
11+
use PhpParser\Node;
12+
use PHPStan\Analyser\Scope;
13+
use PHPStan\Rules\RuleErrorBuilder;
14+
15+
use function array_key_exists;
16+
use function sprintf;
17+
18+
/**
19+
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\Expr\ConstFetch>
20+
*/
21+
final class WpConstantFetchRule implements \PHPStan\Rules\Rule
22+
{
23+
protected const REPLACEMENTS = [
24+
'BACKGROUND_COLOR' => "get_theme_support('custom-background')",
25+
'BACKGROUND_IMAGE' => "get_theme_support('custom-background')",
26+
'DISALLOW_FILE_MODS' => 'wp_is_file_mod_allowed()',
27+
'DOING_AJAX' => 'wp_doing_ajax()',
28+
'DOING_CRON' => 'wp_doing_cron()',
29+
'FORCE_SSL_ADMIN' => 'force_ssl_admin()',
30+
'FORCE_SSL_LOGIN' => 'force_ssl_admin()',
31+
'FS_METHOD' => 'get_filesystem_method()',
32+
'HEADER_IMAGE' => "get_theme_support('custom-header\)",
33+
'HEADER_IMAGE_WIDTH' => "get_theme_support('custom-header')",
34+
'HEADER_IMAGE_HEIGHT' => "get_theme_support('custom-header')",
35+
'HEADER_TEXTCOLOR' => "get_theme_support('custom-header')",
36+
'MULTISITE' => 'is_multisite()',
37+
'NO_HEADER_TEXT' => "get_theme_support('custom-header')",
38+
'STYLESHEETPATH' => 'get_stylesheet_directory()',
39+
'SUBDOMAIN_INSTALL' => 'is_subdomain_install()',
40+
'TEMPLATEPATH' => 'get_template_directory()',
41+
'UPLOADS' => 'wp_get_upload_dir() or wp_upload_dir(null, false)',
42+
'VHOST' => 'is_subdomain_install()',
43+
'WP_ADMIN' => 'is_admin()',
44+
'WP_BLOG_ADMIN' => 'is_blog_admin()',
45+
'WP_CONTENT_URL' => 'content_url()',
46+
'WP_DEVELOPMENT_MODE' => 'wp_get_development_mode()',
47+
'WP_HOME' => 'home_url() or get_home_url()',
48+
'WP_INSTALLING' => 'wp_installing()',
49+
'WP_NETWORK_ADMIN' => 'is_network_admin()',
50+
'WP_PLUGIN_URL' => "plugins_url() or plugin_dir_url('')",
51+
'WP_POST_REVISIONS' => 'wp_revisions_to_keep()',
52+
'WP_SITEURL' => 'site_url() or get_site_url()',
53+
'WP_USER_ADMIN' => 'is_user_admin()',
54+
'WP_USE_THEMES' => 'wp_using_themes()',
55+
'WPMU_PLUGIN_URL' => 'plugins_url()',
56+
];
57+
58+
public function getNodeType(): string
59+
{
60+
return Node\Expr\ConstFetch::class;
61+
}
62+
63+
public function processNode(Node $node, Scope $scope): array
64+
{
65+
if (! $this->isDiscouragedConstant($node->name)) {
66+
return [];
67+
}
68+
69+
return [
70+
RuleErrorBuilder::message(
71+
sprintf(
72+
'Found usage of constant %s. Use %s instead.',
73+
(string)$node->name,
74+
$this->getReplacement($node->name)
75+
)
76+
)
77+
->identifier('phpstanWP.wpConstant.fetch')
78+
->build(),
79+
];
80+
}
81+
82+
private function isDiscouragedConstant(Node\Name $constantName): bool
83+
{
84+
return array_key_exists((string)$constantName, self::REPLACEMENTS);
85+
}
86+
87+
private function getReplacement(Node\Name $constantName): string
88+
{
89+
return self::REPLACEMENTS[(string)$constantName];
90+
}
91+
}

tests/WpConstantFetchRuleTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SzepeViktor\PHPStan\WordPress\Tests;
6+
7+
use PHPStan\Rules\Rule;
8+
use SzepeViktor\PHPStan\WordPress\WpConstantFetchRule;
9+
10+
/**
11+
* @extends \PHPStan\Testing\RuleTestCase<\SzepeViktor\PHPStan\WordPress\WpConstantFetchRule>
12+
*/
13+
class WpConstantFetchRuleTest extends \PHPStan\Testing\RuleTestCase
14+
{
15+
protected function getRule(): Rule
16+
{
17+
return new WpConstantFetchRule();
18+
}
19+
20+
public function testRule(): void
21+
{
22+
$this->analyse(
23+
[
24+
__DIR__ . '/data/wp-constants.php',
25+
__DIR__ . '/data/internal-error.php',
26+
],
27+
[
28+
["Found usage of constant BACKGROUND_COLOR. Use get_theme_support('custom-background') instead.", 9],
29+
["Found usage of constant BACKGROUND_IMAGE. Use get_theme_support('custom-background') instead.", 10],
30+
['Found usage of constant DISALLOW_FILE_MODS. Use wp_is_file_mod_allowed() instead.', 11],
31+
['Found usage of constant DOING_AJAX. Use wp_doing_ajax() instead.', 12],
32+
['Found usage of constant DOING_CRON. Use wp_doing_cron() instead.', 13],
33+
['Found usage of constant FORCE_SSL_ADMIN. Use force_ssl_admin() instead.', 14],
34+
['Found usage of constant FORCE_SSL_LOGIN. Use force_ssl_admin() instead.', 15],
35+
['Found usage of constant FS_METHOD. Use get_filesystem_method() instead.', 16],
36+
['Found usage of constant MULTISITE. Use is_multisite() instead.', 17],
37+
['Found usage of constant STYLESHEETPATH. Use get_stylesheet_directory() instead.', 18],
38+
['Found usage of constant SUBDOMAIN_INSTALL. Use is_subdomain_install() instead.', 19],
39+
['Found usage of constant TEMPLATEPATH. Use get_template_directory() instead.', 20],
40+
['Found usage of constant UPLOADS. Use wp_get_upload_dir() or wp_upload_dir(null, false) instead.', 21],
41+
['Found usage of constant VHOST. Use is_subdomain_install() instead.', 22],
42+
['Found usage of constant WP_ADMIN. Use is_admin() instead.', 23],
43+
['Found usage of constant WP_BLOG_ADMIN. Use is_blog_admin() instead.', 24],
44+
['Found usage of constant WP_CONTENT_URL. Use content_url() instead.', 25],
45+
['Found usage of constant WP_DEVELOPMENT_MODE. Use wp_get_development_mode() instead.', 26],
46+
['Found usage of constant WP_HOME. Use home_url() or get_home_url() instead.', 27],
47+
['Found usage of constant WP_INSTALLING. Use wp_installing() instead.', 28],
48+
['Found usage of constant WP_NETWORK_ADMIN. Use is_network_admin() instead.', 29],
49+
["Found usage of constant WP_PLUGIN_URL. Use plugins_url() or plugin_dir_url('') instead.", 30],
50+
['Found usage of constant WP_POST_REVISIONS. Use wp_revisions_to_keep() instead.', 31],
51+
['Found usage of constant WP_SITEURL. Use site_url() or get_site_url() instead.', 32],
52+
['Found usage of constant WP_USER_ADMIN. Use is_user_admin() instead.', 33],
53+
['Found usage of constant WP_USE_THEMES. Use wp_using_themes() instead.', 34],
54+
['Found usage of constant WPMU_PLUGIN_URL. Use plugins_url() instead.', 35],
55+
]
56+
);
57+
}
58+
59+
public static function getAdditionalConfigFiles(): array
60+
{
61+
return [dirname(__DIR__) . '/vendor/szepeviktor/phpstan-wordpress/extension.neon'];
62+
}
63+
}

tests/data/wp-constants.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SzepeViktor\PHPStan\WordPress\Tests;
6+
7+
// WpConstantFetchRule
8+
$ruleDoesNotApply = MB_IN_BYTES;
9+
$ruleApplies = BACKGROUND_COLOR;
10+
$ruleApplies = BACKGROUND_IMAGE;
11+
$ruleApplies = DISALLOW_FILE_MODS;
12+
$ruleApplies = DOING_AJAX;
13+
$ruleApplies = DOING_CRON;
14+
$ruleApplies = FORCE_SSL_ADMIN;
15+
$ruleApplies = FORCE_SSL_LOGIN;
16+
$ruleApplies = FS_METHOD;
17+
$ruleApplies = MULTISITE;
18+
$ruleApplies = STYLESHEETPATH;
19+
$ruleApplies = SUBDOMAIN_INSTALL;
20+
$ruleApplies = TEMPLATEPATH;
21+
$ruleApplies = UPLOADS;
22+
$ruleApplies = VHOST;
23+
$ruleApplies = WP_ADMIN;
24+
$ruleApplies = WP_BLOG_ADMIN;
25+
$ruleApplies = WP_CONTENT_URL;
26+
$ruleApplies = WP_DEVELOPMENT_MODE;
27+
$ruleApplies = WP_HOME;
28+
$ruleApplies = WP_INSTALLING;
29+
$ruleApplies = WP_NETWORK_ADMIN;
30+
$ruleApplies = WP_PLUGIN_URL;
31+
$ruleApplies = WP_POST_REVISIONS;
32+
$ruleApplies = WP_SITEURL;
33+
$ruleApplies = WP_USER_ADMIN;
34+
$ruleApplies = WP_USE_THEMES;
35+
$ruleApplies = WPMU_PLUGIN_URL;

0 commit comments

Comments
 (0)