Skip to content

Commit

Permalink
added BypassFinals::debugInfo()
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Dec 27, 2024
1 parent 86b00f0 commit d738898
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 4 deletions.
22 changes: 21 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ DG\BypassFinals::allowPaths([
]);
```

Or, conversely, you can specify which paths not to search using `DG\BypassFinals::denyPaths()`.
Or, conversely, you can specify which paths not to search using `DG\BypassFinals::denyPaths()`.
This gives you finer control and can solve issues with certain frameworks and libraries.

Enhance performance by caching transformed files. Make sure the cache directory already exists:
Expand All @@ -76,6 +76,26 @@ For integration with PHPUnit 10 or newer, simply add BypassFinals as an extensio

 <!---->

Troubleshooting
---------------

If you encounter issues with BypassFinals not working as expected, you can use the `debugInfo()` method to gain insights into its internal state. Calling this method will output valuable information to help diagnose the problem:

```php
DG\BypassFinals::debugInfo();
```

This will display:

- Configuration: Whether BypassFinals is enabled for removing `final` and/or `readonly` keywords.
- BypassFinals startup call stack: The sequence of function calls that led to `DG\BypassFinals::enable()`, helping you verify when and where BypassFinals was started.
- Classes loaded before BypassFinals startup: A list of classes that were already loaded in PHP before BypassFinals was started. Keywords in these classes cannot be removed, as the classes are already defined. This can help identify potential conflicts or reasons why certain classes aren't being modified.
- Modified files: A list of the files that BypassFinals has successfully modified. If the file containing a class you expect to be modified isn't in this list, it suggests a problem with path matching or the timing of the BypassFinals startup.

By examining this output, you can better understand how BypassFinals is configured and whether it's operating on the intended files and classes. This can significantly speed up the process of identifying and resolving issues.

 <!---->

Do you like this project?
---------

Expand Down
71 changes: 69 additions & 2 deletions src/BypassFinals.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,28 @@ final class BypassFinals
/** @var array Tokens that represent 'readonly' and 'final' keywords */
private static $tokens = [];

/** @var array<int, array{file: string, line: int, function: string, class?: string, type?: string, args?: array}> Call stack when enable() was called */
private static $enableCallStack = [];

/** @var array<string> List of userland classes loaded before enable() was called */
private static $classesLoadedBeforeEnable = [];

/** @var array<string> List of files that were modified */
private static $modifiedFiles = [];


/**
* Enables modification of the source code to bypass 'readonly' and 'final' restrictions.
*/
public static function enable(bool $bypassReadOnly = true, bool $bypassFinal = true): void
{
if (!self::$enableCallStack) {
self::$enableCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
self::$classesLoadedBeforeEnable = array_filter(get_declared_classes(), function (string $class): bool {
return !(new \ReflectionClass($class))->isInternal() && $class !== self::class;
});
}

if ($bypassReadOnly && PHP_VERSION_ID >= 80100) {
self::$tokens[T_READONLY] = 'readonly';
}
Expand Down Expand Up @@ -93,13 +109,18 @@ public static function setCacheDirectory(?string $dir): void
* Modifies the PHP code by removing specified tokens if they exist.
* @internal
*/
public static function modifyCode(string $code): string
public static function modifyCode(string $code, ?string $file = null): string
{
foreach (self::$tokens as $text) {
if (stripos($code, $text) !== false) {
return self::$cacheDir
$modifiedCode = self::$cacheDir
? self::removeTokensCached($code)
: self::removeTokens($code);
if ($modifiedCode !== $code) {
self::$modifiedFiles[] = $file;
return $modifiedCode;
}
return $code;
}
}

Expand Down Expand Up @@ -175,4 +196,50 @@ public static function isPathAllowed(string $path): bool

return false;
}


/**
* Returns debugging information to help diagnose issues.
*/
public static function debugInfo(): void
{
echo "<xmp>\n";
echo "BypassFinals Debug Information\n";
echo "------------------------------\n\n";
echo "Configuration:\n";
echo " Bypass 'final': " . (PHP_VERSION_ID >= 80100 && isset(self::$tokens[T_READONLY]) ? 'enabled' : 'disabled') . "\n";
echo " Bypass 'readonly': " . (isset(self::$tokens[T_FINAL]) ? 'enabled' : 'disabled') . "\n";

echo "\nFrom where BypassFinals::enable() was started:\n";
foreach (self::$enableCallStack as $index => $frame) {
echo " #$index ";
if (isset($frame['class'])) {
echo $frame['class'] . $frame['type'] . $frame['function'] . '()';
} elseif (isset($frame['function'])) {
echo $frame['function'] . '()';
}
if (isset($frame['file'])) {
echo ' in ' . $frame['file'] . ':' . $frame['line'];
}
echo "\n";
}

echo "\nClasses already loaded before BypassFinals was started:\n";
if (self::$classesLoadedBeforeEnable) {
foreach (self::$classesLoadedBeforeEnable as $class) {
echo " - $class\n";
}
} else {
echo " no classes\n";
}

echo "\nFiles where BypassFinals removed final/readonly:\n";
if (self::$modifiedFiles) {
foreach (self::$modifiedFiles as $file) {
echo " - $file\n";
}
} else {
echo " no files were modified\n";
}
}
}
2 changes: 1 addition & 1 deletion src/MutatingWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function stream_open(string $path, string $mode, int $options, ?string &$
$content .= $this->wrapper->stream_read(8192);
}

$modified = BypassFinals::modifyCode($content);
$modified = BypassFinals::modifyCode($content, $path);
if ($modified === $content) {
$this->wrapper->stream_seek(0);
} else {
Expand Down

0 comments on commit d738898

Please sign in to comment.