Skip to content

Commit

Permalink
Add setWhitelist method to allow controlling which files get replaced. (
Browse files Browse the repository at this point in the history
  • Loading branch information
afilina authored and dg committed May 8, 2020
1 parent 8406995 commit 1623686
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 1 deletion.
10 changes: 10 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@ 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,
preferably right after `vendor/autoload.php` in loaded.

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

```php
DG\BypassFinals::setWhitelist([
'*/Nette/*',
]);
```

This gives you finer control and can solve issues with certain frameworks and libraries.

If you like it, **[please make a donation now](https://nette.org/make-donation?to=bypass-finals)**. Thank you!
26 changes: 25 additions & 1 deletion src/BypassFinals.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class BypassFinals
/** @var resource|null */
private $handle;

/** @var array */
private static $pathWhitelist = ['*'];


public static function enable(): void
{
Expand All @@ -26,6 +29,15 @@ public static function enable(): void
}


public static function setWhitelist(array $whitelist): void
{
foreach ($whitelist as &$mask) {
$mask = strtr($mask, '\\', '/');
}
self::$pathWhitelist = $whitelist;
}


public function dir_closedir(): void
{
closedir($this->handle);
Expand Down Expand Up @@ -126,7 +138,7 @@ public function stream_metadata(string $path, int $option, $value): bool
public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool
{
$usePath = (bool) ($options & STREAM_USE_PATH);
if ($mode === 'rb' && pathinfo($path, PATHINFO_EXTENSION) === 'php') {
if ($mode === 'rb' && pathinfo($path, PATHINFO_EXTENSION) === 'php' && self::isPathInWhiteList($path)) {
$content = $this->native('file_get_contents', $path, $usePath, $this->context);
if ($content === false) {
return false;
Expand Down Expand Up @@ -226,4 +238,16 @@ public static function removeFinals(string $code): string
}
return $code;
}


private static function isPathInWhiteList(string $path): bool
{
$path = strtr($path, '\\', '/');
foreach (self::$pathWhitelist as $mask) {
if (fnmatch($mask, $path)) {
return true;
}
}
return false;
}
}
23 changes: 23 additions & 0 deletions tests/BypassFinals/BypassFinals.excluded.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);

use Tester\Assert;

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

Tester\Environment::setup();


DG\BypassFinals::enable();
DG\BypassFinals::setWhitelist([
'*/fixtures/final.class.php',
]);

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

$rc = new ReflectionClass('FinalClass');
Assert::false($rc->isFinal());

$rc = new ReflectionClass('FinalClassExcluded');
Assert::true($rc->isFinal());
29 changes: 29 additions & 0 deletions tests/BypassFinals/BypassFinals.whitelist.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);

use DG\BypassFinals;
use Tester\Assert;

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

Tester\Environment::setup();

Assert::with(BypassFinals::class, function () {
Assert::true(BypassFinals::isPathInWhiteList(__DIR__ . '/fixtures/final.class.php'));


DG\BypassFinals::setWhitelist([
__DIR__ . '/fixtures/final.class.php',
]);

Assert::true(BypassFinals::isPathInWhiteList(__DIR__ . '/fixtures/final.class.php'));
Assert::false(BypassFinals::isPathInWhiteList(__DIR__ . '/fixtures/other.class.php'));


DG\BypassFinals::setWhitelist([
__DIR__ . '/fixtures/*',
]);

Assert::true(BypassFinals::isPathInWhiteList(__DIR__ . '/fixtures/class.php'));
Assert::false(BypassFinals::isPathInWhiteList(__DIR__ . '/other/class.php'));
});
9 changes: 9 additions & 0 deletions tests/BypassFinals/fixtures/final.excluded.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);

final class FinalClassExcluded
{
final function finalMethod()
{
}
}

0 comments on commit 1623686

Please sign in to comment.