Skip to content

Commit

Permalink
removes keyword 'readonly' [Closes #39]
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 23, 2023
1 parent 757586f commit 7df37b7
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 11 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/coding-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: 7.4
php-version: 8.2
coverage: none

- run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress
Expand All @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: 7.4
php-version: 8.2
coverage: none

- run: composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress
Expand Down
6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Bypass Finals
Introduction
------------

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


Expand All @@ -35,12 +35,12 @@ Simply call this:
DG\BypassFinals::enable();
```

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

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

You can choose to only bypass finals in specific files or directories:
You can choose to only bypass keywords in specific files or directories:

```php
DG\BypassFinals::setWhitelist([
Expand Down
24 changes: 18 additions & 6 deletions src/BypassFinals.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ class BypassFinals
/** @var ?string */
private static $cacheDir;

/** @var array */
private static $tokens = [
T_FINAL => 'final',
];


public static function enable(): void
{
if (PHP_VERSION_ID >= 80100) {
self::$tokens[T_READONLY] = 'readonly';
}

$wrapper = stream_get_meta_data(fopen(__FILE__, 'r'))['wrapper_data'] ?? null;
if ($wrapper instanceof self) {
return;
Expand Down Expand Up @@ -98,13 +107,16 @@ public function dir_opendir(string $path, int $options): bool

private static function modifyCode(string $code): string
{
if (stripos($code, 'final') === false) {
return $code;
foreach (self::$tokens as $text) {
if (stripos($code, $text) !== false) {
return self::$cacheDir
? self::removeTokensCached($code)
: self::removeTokens($code);

}
}

return self::$cacheDir
? self::removeTokensCached($code)
: self::removeTokens($code);
return $code;
}


Expand Down Expand Up @@ -141,7 +153,7 @@ private static function removeTokens(string $code): string
$code = '';
foreach ($tokens as $token) {
$code .= is_array($token)
? ($token[0] === T_FINAL ? '' : $token[1])
? (isset(self::$tokens[$token[0]]) ? '' : $token[1])
: $token;
}

Expand Down
23 changes: 23 additions & 0 deletions tests/BypassFinals/BypassFinals.readonly+final.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/** @phpVersion 8.2 */

declare(strict_types=1);

use Tester\Assert;

require __DIR__ . '/../../vendor/autoload.php';

Tester\Environment::setup();


DG\BypassFinals::enable();

require __DIR__ . '/fixtures/final.readonly.class.php';

$rc = new ReflectionClass('FinalReadonlyClass');
Assert::false($rc->isReadOnly());
Assert::false($rc->isFinal());
Assert::false($rc->getMethod('finalMethod')->isFinal());
Assert::same(123, FinalReadonlyClass::FINAL);
Assert::same(456, (new FinalReadonlyClass)->final());
16 changes: 16 additions & 0 deletions tests/BypassFinals/fixtures/final.readonly.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);

final readonly class FinalReadonlyClass
{
const FINAL = 123;

final function finalMethod()
{
}

function final ()
{
return 456;
}
}

0 comments on commit 7df37b7

Please sign in to comment.