Skip to content

Fixed issues with debugging #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "okapi/code-transformer",
"description": "PHP Code Transformer is a PHP library that allows you to modify and transform the source code of a loaded PHP class.",
"version": "1.3.5",
"version": "1.3.6",
"type": "library",
"homepage": "https://github.com/okapi-web/php-code-transformer",
"license": "MIT",
Expand Down
5 changes: 5 additions & 0 deletions src/CodeTransformerKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use DI\Attribute\Inject;
use Okapi\CodeTransformer\Core\AutoloadInterceptor;
use Okapi\CodeTransformer\Core\Cache\CacheStateManager;
use Okapi\CodeTransformer\Core\CachedStreamFilter;
use Okapi\CodeTransformer\Core\Container\TransformerManager;
use Okapi\CodeTransformer\Core\DI;
use Okapi\CodeTransformer\Core\Exception\Kernel\DirectKernelInitializationException;
Expand Down Expand Up @@ -46,6 +47,9 @@ abstract class CodeTransformerKernel
#[Inject]
private StreamFilter $streamFilter;

#[Inject]
private CachedStreamFilter $cachedStreamFilter;

#[Inject]
private AutoloadInterceptor $autoloadInterceptor;

Expand Down Expand Up @@ -226,6 +230,7 @@ protected function registerServices(): void
$this->cacheStateManager->register();

$this->streamFilter->register();
$this->cachedStreamFilter->register();
}

/**
Expand Down
26 changes: 19 additions & 7 deletions src/Core/AutoloadInterceptor/ClassContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,28 @@ class ClassContainer
/**
* The class paths.
*
* @var array<string, string>
* @var array<string, array{namespacedClass: class-string, cachedFilePath: string|null}>
*/
private array $namespacedClassPaths = [];
private array $classContext = [];

/**
* Add a class path.
*
* @param string $path
* @param string $class
* @param class-string $namespacedClass
* @param string|null $cachedFilePath
*
* @return void
*/
public function addNamespacedClassPath(string $path, string $class): void
{
$this->namespacedClassPaths[$path] = $class;
public function addClassContext(
string $path,
string $namespacedClass,
?string $cachedFilePath = null,
): void {
$this->classContext[$path] = [
'namespacedClass' => $namespacedClass,
'cachedFilePath' => $cachedFilePath,
];
}

/**
Expand All @@ -39,6 +46,11 @@ public function addNamespacedClassPath(string $path, string $class): void
*/
public function getNamespacedClassByPath(string $path): string
{
return $this->namespacedClassPaths[$path];
return $this->classContext[$path]['namespacedClass'];
}

public function getCachedFilePath(string $filePath): string
{
return $this->classContext[$filePath]['cachedFilePath'];
}
}
33 changes: 29 additions & 4 deletions src/Core/AutoloadInterceptor/ClassLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use DI\Attribute\Inject;
use Okapi\CodeTransformer\Core\AutoloadInterceptor;
use Okapi\CodeTransformer\Core\Cache\CacheStateManager;
use Okapi\CodeTransformer\Core\CachedStreamFilter;
use Okapi\CodeTransformer\Core\Matcher\TransformerMatcher;
use Okapi\CodeTransformer\Core\Options;
use Okapi\CodeTransformer\Core\Options\Environment;
Expand Down Expand Up @@ -120,26 +121,50 @@ public function findFile($namespacedClass): false|string
&& $cacheState
) {
// Use the cached file if transformations have been applied
if ($cacheFilePath = $cacheState->getFilePath()) {
$this->classContainer->addClassContext(
$filePath,
$namespacedClass,
$cacheFilePath,
);

// For cached files, the debugger will have trouble finding the
// original file, that's why we rewrite the file path with a PHP
// stream filter
/** @see CachedStreamFilter::filter() */
return $this->filterInjector->rewriteCached($filePath);
}

// Or return the original file if no transformations have been applied
return $cacheState->getFilePath() ?? $filePath;
return $filePath;
}

// In development mode, check if the cache is fresh
elseif ($this->options->getEnvironment() === Environment::DEVELOPMENT
&& $cacheState
&& $cacheState->isFresh()
) {
return $cacheState->getFilePath() ?? $filePath;
if ($cacheFilePath = $cacheState->getFilePath()) {
$this->classContainer->addClassContext(
$filePath,
$namespacedClass,
$cacheFilePath,
);

return $this->filterInjector->rewriteCached($filePath);
}

return $filePath;
}


// Check if the class should be transformed
if (!$this->transformerMatcher->match($namespacedClass, $filePath)) {
if (!$this->transformerMatcher->matchAndStore($namespacedClass, $filePath)) {
return $filePath;
}

// Add the class to store the file path
$this->classContainer->addNamespacedClassPath($filePath, $namespacedClass);
$this->classContainer->addClassContext($filePath, $namespacedClass);

// Replace the file path with a PHP stream filter
/** @see StreamFilter::filter() */
Expand Down
60 changes: 60 additions & 0 deletions src/Core/CachedStreamFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace Okapi\CodeTransformer\Core;

use Okapi\CodeTransformer\Core\AutoloadInterceptor\ClassContainer;
use Okapi\CodeTransformer\Core\StreamFilter\Metadata;
use Okapi\Filesystem\Filesystem;
use php_user_filter as PhpStreamFilter;

/**
* # Cached Stream Filter
*
* This class is used to register the cached stream filter.
*
* Because the PHP debugger has trouble finding the original file, we always
* rewrite the file path with a PHP stream filter.
*/
class CachedStreamFilter extends PhpStreamFilter implements ServiceInterface
{
public const CACHED_FILTER_ID = 'okapi.code-transformer.cached';

private string $data = '';

public function register(): void
{
stream_filter_register(static::CACHED_FILTER_ID, static::class);
}

public function filter($in, $out, &$consumed, bool $closing): int
{
// Read stream until EOF
while ($bucket = stream_bucket_make_writeable($in)) {
$this->data .= $bucket->data;
}

// If stream is closed, return the cached file
if ($closing || feof($this->stream)) {
$consumed = strlen($this->data);

$metadata = DI::make(Metadata::class, [
'stream' => $this->stream,
'originalSource' => $this->data,
]);

$classContainer = DI::get(ClassContainer::class);
$cachedFilePath = $classContainer->getCachedFilePath($metadata->uri);

$source = Filesystem::readFile($cachedFilePath);

$bucket = stream_bucket_new($this->stream, $source);
stream_bucket_append($out, $bucket);

// Pass the (cached) source code to the next filter
return PSFS_PASS_ON;
}

// No data has been consumed
return PSFS_PASS_ON;
}
}
36 changes: 24 additions & 12 deletions src/Core/Matcher/TransformerMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Okapi\CodeTransformer\Core\Container\TransformerManager;
use Okapi\CodeTransformer\Core\DI;
use Okapi\CodeTransformer\Transformer;
use Okapi\Path\Path;
use Okapi\Wildcards\Regex;

/**
Expand Down Expand Up @@ -44,7 +45,7 @@ class TransformerMatcher
*
* @return bool
*/
public function match(string $namespacedClass, string $filePath): bool
public function matchAndStore(string $namespacedClass, string $filePath): bool
{
// Get the transformers
$transformerContainers = $this->transformerContainer->getTransformerContainers();
Expand Down Expand Up @@ -80,24 +81,35 @@ function (bool $carry, TransformerContainer $container) use ($transformerContain

// Cache the result
if (!$matchedTransformerContainers) {
$cacheState = DI::make(EmptyResultCacheState::class, [
CacheState::DATA => [
CacheState::ORIGINAL_FILE_PATH_KEY => $filePath,
CacheState::NAMESPACED_CLASS_KEY => $namespacedClass,
CacheState::MODIFICATION_TIME_KEY => filemtime($filePath),
],
]);

// Set the cache state
$this->cacheStateManager->setCacheState(
$this->cacheEmptyResult(
$namespacedClass,
$filePath,
$cacheState,
);
}

return (bool)$matchedTransformerContainers;
}

private function cacheEmptyResult(
string $namespacedClass,
string $filePath,
): void {
$filePath = Path::resolve($filePath);
$cacheState = DI::make(EmptyResultCacheState::class, [
CacheState::DATA => [
CacheState::ORIGINAL_FILE_PATH_KEY => $filePath,
CacheState::NAMESPACED_CLASS_KEY => $namespacedClass,
CacheState::MODIFICATION_TIME_KEY => filemtime($filePath),
],
]);

// Set the cache state
$this->cacheStateManager->setCacheState(
$filePath,
$cacheState,
);
}

/**
* Get the matched transformers for the given class.
*
Expand Down
11 changes: 11 additions & 0 deletions src/Core/StreamFilter/FilterInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Okapi\CodeTransformer\Core\StreamFilter;

use Okapi\CodeTransformer\Core\CachedStreamFilter;
use Okapi\CodeTransformer\Core\StreamFilter;

/**
Expand Down Expand Up @@ -41,4 +42,14 @@ public function rewrite(string $filePath): string
$filePath
);
}

public function rewriteCached(string $filePath): string
{
return sprintf(
"%s%s/resource=%s",
static::PHP_FILTER_READ,
CachedStreamFilter::CACHED_FILTER_ID,
$filePath,
);
}
}
13 changes: 8 additions & 5 deletions tests/ClassLoaderMockTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
namespace Okapi\CodeTransformer\Tests;

use Okapi\CodeTransformer\Core\AutoloadInterceptor\ClassLoader;
use Okapi\CodeTransformer\Core\Cache\CachePaths;
use Okapi\CodeTransformer\Core\DI;
use Okapi\CodeTransformer\Core\CachedStreamFilter;
use Okapi\CodeTransformer\Core\StreamFilter;
use Okapi\CodeTransformer\Core\StreamFilter\FilterInjector;
use Okapi\Path\Path;
Expand Down Expand Up @@ -65,9 +64,13 @@ public function assertWillBeTransformed(string $className): void

public function assertTransformerLoadedFromCache(string $className): void
{
$filePath = $this->findOriginalClassMock($className);
$cachePaths = DI::get(CachePaths::class);
$cachePath = $cachePaths->getTransformedCachePath($filePath);
$filePath = Path::resolve($this->findOriginalClassMock($className));

$cachePath =
FilterInjector::PHP_FILTER_READ .
CachedStreamFilter::CACHED_FILTER_ID . '/resource=' .
$filePath;

$filePathMock = $this->findClassMock($className);

Assert::assertEquals(
Expand Down