Skip to content

Commit 7df37b7

Browse files
committed
removes keyword 'readonly' [Closes #39]
1 parent 757586f commit 7df37b7

File tree

5 files changed

+62
-11
lines changed

5 files changed

+62
-11
lines changed

.github/workflows/coding-style.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
- uses: actions/checkout@v2
1111
- uses: shivammathur/setup-php@v2
1212
with:
13-
php-version: 7.4
13+
php-version: 8.2
1414
coverage: none
1515

1616
- run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress
@@ -24,7 +24,7 @@ jobs:
2424
- uses: actions/checkout@v2
2525
- uses: shivammathur/setup-php@v2
2626
with:
27-
php-version: 7.4
27+
php-version: 8.2
2828
coverage: none
2929

3030
- run: composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress

readme.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Bypass Finals
1010
Introduction
1111
------------
1212

13-
Removes final keywords from source code on-the-fly and allows mocking of final methods and classes.
13+
Removes `final` and `readonly` keywords from source code on-the-fly and allows mocking of final methods and classes.
1414
It can be used together with any test tool such as PHPUnit, Mockery or [Nette Tester](https://tester.nette.org).
1515

1616

@@ -35,12 +35,12 @@ Simply call this:
3535
DG\BypassFinals::enable();
3636
```
3737

38-
You need to enable it before the classes you want to remove the final are loaded. So call it as soon as possible,
38+
You need to enable it before the classes you want to remove the keywords from are loaded. So call it as soon as possible,
3939
preferably right after `vendor/autoload.php` is loaded.
4040

4141
Note that final internal PHP classes like `Closure` cannot be mocked.
4242

43-
You can choose to only bypass finals in specific files or directories:
43+
You can choose to only bypass keywords in specific files or directories:
4444

4545
```php
4646
DG\BypassFinals::setWhitelist([

src/BypassFinals.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ class BypassFinals
2727
/** @var ?string */
2828
private static $cacheDir;
2929

30+
/** @var array */
31+
private static $tokens = [
32+
T_FINAL => 'final',
33+
];
34+
3035

3136
public static function enable(): void
3237
{
38+
if (PHP_VERSION_ID >= 80100) {
39+
self::$tokens[T_READONLY] = 'readonly';
40+
}
41+
3342
$wrapper = stream_get_meta_data(fopen(__FILE__, 'r'))['wrapper_data'] ?? null;
3443
if ($wrapper instanceof self) {
3544
return;
@@ -98,13 +107,16 @@ public function dir_opendir(string $path, int $options): bool
98107

99108
private static function modifyCode(string $code): string
100109
{
101-
if (stripos($code, 'final') === false) {
102-
return $code;
110+
foreach (self::$tokens as $text) {
111+
if (stripos($code, $text) !== false) {
112+
return self::$cacheDir
113+
? self::removeTokensCached($code)
114+
: self::removeTokens($code);
115+
116+
}
103117
}
104118

105-
return self::$cacheDir
106-
? self::removeTokensCached($code)
107-
: self::removeTokens($code);
119+
return $code;
108120
}
109121

110122

@@ -141,7 +153,7 @@ private static function removeTokens(string $code): string
141153
$code = '';
142154
foreach ($tokens as $token) {
143155
$code .= is_array($token)
144-
? ($token[0] === T_FINAL ? '' : $token[1])
156+
? (isset(self::$tokens[$token[0]]) ? '' : $token[1])
145157
: $token;
146158
}
147159

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/** @phpVersion 8.2 */
4+
5+
declare(strict_types=1);
6+
7+
use Tester\Assert;
8+
9+
require __DIR__ . '/../../vendor/autoload.php';
10+
11+
Tester\Environment::setup();
12+
13+
14+
DG\BypassFinals::enable();
15+
16+
require __DIR__ . '/fixtures/final.readonly.class.php';
17+
18+
$rc = new ReflectionClass('FinalReadonlyClass');
19+
Assert::false($rc->isReadOnly());
20+
Assert::false($rc->isFinal());
21+
Assert::false($rc->getMethod('finalMethod')->isFinal());
22+
Assert::same(123, FinalReadonlyClass::FINAL);
23+
Assert::same(456, (new FinalReadonlyClass)->final());
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
final readonly class FinalReadonlyClass
5+
{
6+
const FINAL = 123;
7+
8+
final function finalMethod()
9+
{
10+
}
11+
12+
function final ()
13+
{
14+
return 456;
15+
}
16+
}

0 commit comments

Comments
 (0)