Skip to content

Commit 33c1b2e

Browse files
authored
Allow to whitelist constants (#214)
1 parent df9bcf5 commit 33c1b2e

26 files changed

+617
-63
lines changed

README.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ potentially very difficult to debug due to dissimilar or unsupported package ver
3838
- [Finders and paths](#finders-and-paths)
3939
- [Patchers](#patchers)
4040
- [Whitelist][whitelist]
41-
- [Class Whitelisting](#class-whitelisting)
41+
- [Class & Constant Whitelisting](#class-constant-whitelisting)
4242
- [Namespace Whitelisting](#namespace-whitelisting)
4343
- [Building A Scoped PHAR](#building-a-scoped-phar)
4444
- [With Box](#with-box)
@@ -261,9 +261,9 @@ a PHPUnit PHAR with isolated code, you still want the PHAR to be able to
261261
understand the `PHPUnit\Framework\TestCase` class.
262262

263263

264-
### Class whitelisting
264+
### Class & Constant whitelisting
265265

266-
You can whitelist classes and interfaces like so:
266+
You can whitelist classes, interfaces and constants like so like so:
267267

268268
```php
269269
<?php declare(strict_types=1);
@@ -273,15 +273,26 @@ You can whitelist classes and interfaces like so:
273273
return [
274274
'whitelist' => [
275275
'PHPUnit\Framework\TestCase',
276+
'PHPUNIT_VERSION',
276277
],
277278
];
278279
```
279280

280-
Note that only classes are whitelisted, this does not affect constants,
281-
functions or traits. This whitelisting will actually not prevent the
282-
scoping to operate, i.e. the class or interface will still be prefixed,
283-
but a `class_alias()` statement will be registered pointing the prefixed
284-
class to the non-prefixed one.
281+
This will _not_ work on traits or functions.
282+
283+
The class aliasing mechanism is done like follows:
284+
- Prefix the class or interface as usual
285+
- Append a `class_alias()` statement at the end of the class/interface declaration to link the prefixed symbol to the
286+
non prefixed one
287+
- Append a `class_exists()` statement right after the autoloader is registered to trigger the loading of the method
288+
which will ensure the `class_alias()` statement is executed
289+
290+
It is done this way to ensure prefixed and whitelisted classes can co-exist together without breaking the autoloading.
291+
292+
The constant aliasing mechanism is done by transforming the constant declaration into a `define()` statement when this
293+
is not already the case. Note that there is a difference here since `define()` defines a constant at runtime whereas
294+
`const` defines it at compile time. You have a more details post regarding the differences
295+
[here](https://stackoverflow.com/a/3193704/3902761)
285296

286297

287298
### Namespace whitelisting

specs/const/const-declaration.php

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the humbug/php-scoper package.
7+
*
8+
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
9+
* Pádraic Brady <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
return [
16+
'meta' => [
17+
'title' => 'Global constant usage in the global scope',
18+
// Default values. If not specified will be the one used
19+
'prefix' => 'Humbug',
20+
'whitelist' => [],
21+
],
22+
23+
'Constants declaration in the global namespace' => [
24+
'payload' => <<<'PHP'
25+
<?php
26+
27+
const FOO_CONST = foo();
28+
define('BAR_CONST', foo());
29+
define(FOO_CONST, foo());
30+
define(\FOO_CONST, foo());
31+
----
32+
<?php
33+
34+
namespace Humbug;
35+
36+
const FOO_CONST = \Humbug\foo();
37+
\define('BAR_CONST', \Humbug\foo());
38+
\define(\Humbug\FOO_CONST, \Humbug\foo());
39+
\define(\Humbug\FOO_CONST, \Humbug\foo());
40+
41+
PHP
42+
],
43+
44+
'Constants declaration in the global namespace which is whitelisted' => [
45+
'whitelist' => ['*'],
46+
'payload' => <<<'PHP'
47+
<?php
48+
49+
const FOO_CONST = foo();
50+
define('BAR_CONST', foo());
51+
define(FOO_CONST, foo());
52+
define(\FOO_CONST, foo());
53+
----
54+
<?php
55+
56+
namespace {
57+
const FOO_CONST = \foo();
58+
\define('BAR_CONST', \foo());
59+
\define(\FOO_CONST, \foo());
60+
\define(\FOO_CONST, \foo());
61+
}
62+
63+
PHP
64+
],
65+
66+
'Whitelisted constants declaration in the global namespace' => [
67+
'whitelist' => ['FOO_CONST', 'BAR_CONST'],
68+
'payload' => <<<'PHP'
69+
<?php
70+
71+
const FOO_CONST = foo();
72+
define('BAR_CONST', foo());
73+
define(FOO_CONST, foo());
74+
define(\FOO_CONST, foo());
75+
----
76+
<?php
77+
78+
namespace Humbug;
79+
80+
\define('FOO_CONST', \Humbug\foo());
81+
\define('BAR_CONST', \Humbug\foo());
82+
\define(\FOO_CONST, \Humbug\foo());
83+
\define(\FOO_CONST, \Humbug\foo());
84+
85+
PHP
86+
],
87+
88+
'Constants declaration in a namespace' => [
89+
'payload' => <<<'PHP'
90+
<?php
91+
92+
namespace Acme;
93+
94+
const FOO_CONST = foo();
95+
define('BAR_CONST', foo());
96+
define(FOO_CONST, foo());
97+
define(\FOO_CONST, foo());
98+
define(\Acme\FOO_CONST, foo());
99+
----
100+
<?php
101+
102+
namespace Humbug\Acme;
103+
104+
const FOO_CONST = foo();
105+
\define('BAR_CONST', foo());
106+
\define(FOO_CONST, foo());
107+
\define(\Humbug\FOO_CONST, foo());
108+
\define(\Humbug\Acme\FOO_CONST, foo());
109+
110+
PHP
111+
],
112+
113+
'Constants declaration in a whitelisted namespace' => [
114+
'whitelist' => ['Acme\*'],
115+
'payload' => <<<'PHP'
116+
<?php
117+
118+
namespace Acme;
119+
120+
const FOO_CONST = foo();
121+
define('BAR_CONST', foo());
122+
define(FOO_CONST, foo());
123+
define(\FOO_CONST, foo());
124+
define(\Acme\FOO_CONST, foo());
125+
----
126+
<?php
127+
128+
namespace Acme;
129+
130+
const FOO_CONST = foo();
131+
\define('BAR_CONST', foo());
132+
\define(FOO_CONST, foo());
133+
\define(\Humbug\FOO_CONST, foo());
134+
\define(\Acme\FOO_CONST, foo());
135+
136+
PHP
137+
],
138+
139+
'Whitelisted constants declaration in a namespace' => [
140+
'whitelist' => ['Acme\FOO_CONST'],
141+
'payload' => <<<'PHP'
142+
<?php
143+
144+
namespace Acme;
145+
146+
const FOO_CONST = foo();
147+
define('BAR_CONST', foo());
148+
define(FOO_CONST, foo());
149+
define(\FOO_CONST, foo());
150+
define(\Acme\FOO_CONST, foo());
151+
----
152+
<?php
153+
154+
namespace Humbug\Acme;
155+
156+
\define('Acme\\FOO_CONST', foo());
157+
\define('BAR_CONST', foo());
158+
\define(FOO_CONST, foo());
159+
\define(\Humbug\FOO_CONST, foo());
160+
\define(\Acme\FOO_CONST, foo());
161+
162+
PHP
163+
],
164+
];

specs/const/global-scope-global-with-single-level-use-statement-and-alias.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@
2020
'whitelist' => [],
2121
],
2222

23-
// As it is extremely rare to use a `use const` statement for a built-in constant from the
24-
// global scope, we can relatively safely assume it is a user-land declared constant which should
25-
// be prefixed.
26-
2723
[
2824
'spec' => <<<'SPEC'
2925
Constant call imported with an aliased use statement:
@@ -46,6 +42,31 @@
4642
use const Humbug\DUMMY_CONST as FOO;
4743
\Humbug\DUMMY_CONST;
4844

45+
PHP
46+
],
47+
48+
[
49+
'spec' => <<<'SPEC'
50+
Whitelisted constant call imported with an aliased use statement:
51+
- add prefixed namespace
52+
- transforms the call into a FQ call
53+
SPEC
54+
,
55+
'whitelist' => ['DUMMY_CONST'],
56+
'payload' => <<<'PHP'
57+
<?php
58+
59+
use const DUMMY_CONST as FOO;
60+
61+
FOO;
62+
----
63+
<?php
64+
65+
namespace Humbug;
66+
67+
use const DUMMY_CONST as FOO;
68+
\DUMMY_CONST;
69+
4970
PHP
5071
],
5172

specs/const/global-scope-global-with-single-level-use-statement.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@
2020
'whitelist' => [],
2121
],
2222

23-
// As it is extremely rare to use a `use const` statement for a built-in constant from the
24-
// global scope, we can relatively safely assume it is a user-land declared constant which should
25-
// be prefixed.
26-
2723
[
2824
'spec' => <<<'SPEC'
2925
Constant call imported with a use statement:
@@ -46,6 +42,31 @@
4642
use const Humbug\DUMMY_CONST;
4743
\Humbug\DUMMY_CONST;
4844

45+
PHP
46+
],
47+
48+
[
49+
'spec' => <<<'SPEC'
50+
Whitelisted constant call imported with a use statement:
51+
- add prefixed namespace
52+
- transforms the call into a FQ call
53+
SPEC
54+
,
55+
'whitelist' => ['DUMMY_CONST'],
56+
'payload' => <<<'PHP'
57+
<?php
58+
59+
use const DUMMY_CONST;
60+
61+
DUMMY_CONST;
62+
----
63+
<?php
64+
65+
namespace Humbug;
66+
67+
use const DUMMY_CONST;
68+
\DUMMY_CONST;
69+
4970
PHP
5071
],
5172

specs/const/global-scope-global.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,28 @@
3838
3939
\Humbug\DUMMY_CONST;
4040

41+
PHP
42+
],
43+
44+
[
45+
'spec' => <<<'SPEC'
46+
Whitelisted constant call in the global namespace:
47+
- add prefixed namespace
48+
- transforms the call into a FQ call
49+
SPEC
50+
,
51+
'whitelist' => ['DUMMY_CONST'],
52+
'payload' => <<<'PHP'
53+
<?php
54+
55+
DUMMY_CONST;
56+
----
57+
<?php
58+
59+
namespace Humbug;
60+
61+
\DUMMY_CONST;
62+
4163
PHP
4264
],
4365

specs/const/global-scope-single-part-namespaced-with-single-level-use-alias.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,8 @@ class Foo
107107

108108
[
109109
'spec' => <<<'SPEC'
110-
Constant call on an imported single-level namespace
110+
Whitelisted onstant call on an imported single-level namespace
111111
- do not prefix the use statement (see tests related to single-level classes)
112-
- prefix the constant call: the whitelist only works on classes
113112
- transform the call into a FQ call
114113
SPEC
115114
,
@@ -140,11 +139,11 @@ class Foo
140139
}
141140
namespace Humbug\Foo;
142141
143-
const DUMMY_CONST = '';
142+
\define('Foo\\DUMMY_CONST', '');
144143
namespace Humbug;
145144
146145
use Humbug\Foo as A;
147-
\Humbug\Foo\DUMMY_CONST;
146+
\Foo\DUMMY_CONST;
148147

149148
PHP
150149
],

specs/const/global-scope-single-part-namespaced-with-single-level-use.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
[
2424
'spec' => <<<'SPEC'
25-
Constant call on an imported single-level namespace
25+
Constant call on an imported single-level namespace:
26+
- add prefixed namespace
2627
- do not prefix the use statement (see tests related to single-level classes)
2728
- prefix the constant call
2829
- transform the call into a FQ call
@@ -94,7 +95,7 @@ class Foo
9495

9596
[
9697
'spec' => <<<'SPEC'
97-
Constant call on an imported single-level namespace
98+
Whitelisted constant call on an imported single-level namespace
9899
- do not prefix the use statement (see tests related to single-level classes)
99100
- prefix the constant call: the whitelist only works on classes
100101
- transform the call into a FQ call
@@ -127,11 +128,11 @@ class Foo
127128
}
128129
namespace Humbug\Foo;
129130
130-
const DUMMY_CONST = '';
131+
\define('Foo\\DUMMY_CONST', '');
131132
namespace Humbug;
132133
133134
use Humbug\Foo;
134-
\Humbug\Foo\DUMMY_CONST;
135+
\Foo\DUMMY_CONST;
135136

136137
PHP
137138
],

0 commit comments

Comments
 (0)