Skip to content
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

Add support for custom streams and paths #118

Merged
merged 18 commits into from Mar 12, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
dfc38f8
Do not use realtive paths with single dot
lisachenko Jan 23, 2014
82ac9a1
Enable checking for eof for phar archives, because there is a bug wit…
lisachenko Jan 23, 2014
dc1f2f8
More straightforward implementation of stream filter for better reada…
lisachenko Jan 23, 2014
1cd6fcc
Use strpos for absolute path checking. This check also verifies prese…
lisachenko Jan 23, 2014
5a20b31
Add prebuilCache option to disable check for writable directory and t…
lisachenko Jan 23, 2014
e392932
Fix a bug when date modification equals to cache creation date
lisachenko Jan 23, 2014
ad253ef
Use PathResolver instead of realpath() and stream_resolve_include_pat…
lisachenko Jan 23, 2014
1b884fa
Delay initialization of AOP loader to speed up loading of system clas…
lisachenko Jan 23, 2014
7edb756
Simplify configuration merging, do not require recursive replace
lisachenko Jan 23, 2014
71d4766
Improve performance of rewriting URLs by using static variables and c…
lisachenko Jan 23, 2014
34d4740
Direct call of filters for warmup command to avoid influence of prebu…
lisachenko Jan 23, 2014
f82f8a8
Add an empty line before returns and update phpDoc
lisachenko Jan 23, 2014
93b36b9
Add test case for PathResolver and fix some bugs in the logic
lisachenko Jan 23, 2014
985d00c
Use dynamic constant for cache dir instead of full path in the source…
lisachenko Jan 24, 2014
40490bc
Simplify path resolve check for normal files, this also fixes issue w…
lisachenko Feb 9, 2014
0b0e5b8
Add a failover for non-existent files to manual path calculation and …
lisachenko Mar 12, 2014
5ccb7ed
Merge branch 'master' into feature/phar-support
lisachenko Mar 12, 2014
1fe8c35
Sync header with master branch
lisachenko Mar 12, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions demos/autoload_aspect.php
Expand Up @@ -20,6 +20,7 @@
'cacheDir' => __DIR__ . '/cache',

'interceptFunctions' => true, // Enable support for function interception (Since 0.4.0)
'prebuiltCache' => false, // Cache was already prebuilt (Since 0.5.0)
));

AnnotationRegistry::registerFile(__DIR__ . '/Demo/Annotation/Cacheable.php');
7 changes: 6 additions & 1 deletion src/Go/Console/Command/WarmupCommand.php
Expand Up @@ -11,6 +11,7 @@
namespace Go\Console\Command;

use Go\Core\AspectKernel;
use Go\Instrument\ClassLoading\SourceTransformingLoader;
use Go\Instrument\Transformer\FilterInjectorTransformer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -92,7 +93,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
$isSuccess = null;
try {
// This will trigger creation of cache
file_get_contents(FilterInjectorTransformer::rewrite($file->getRealPath()));
file_get_contents(
FilterInjectorTransformer::PHP_FILTER_READ.
SourceTransformingLoader::FILTER_IDENTIFIER.
"/resource=" . $file->getRealPath()
);
$isSuccess = true;
} catch (\Exception $e) {
$isSuccess = false;
Expand Down
28 changes: 26 additions & 2 deletions src/Go/Core/AspectKernel.php
Expand Up @@ -13,6 +13,7 @@
use Go\Instrument\ClassLoading\AopComposerLoader;
use Go\Instrument\ClassLoading\SourceTransformingLoader;
use Go\Instrument\CleanableMemory;
use Go\Instrument\PathResolver;
use Go\Instrument\Transformer\SourceTransformer;
use Go\Instrument\Transformer\WeavingTransformer;
use Go\Instrument\Transformer\CachingTransformer;
Expand Down Expand Up @@ -91,8 +92,8 @@ public static function getInstance()
*/
public function init(array $options = array())
{
AopComposerLoader::init();
$this->options = array_replace_recursive($this->getDefaultOptions(), $options);
$this->options = $this->normalizeOptions($options);
define('AOP_CACHE_DIR', $this->options['cacheDir']);

/** @var $container AspectContainer */
$container = $this->container = new $this->options['containerClass'];
Expand All @@ -112,6 +113,8 @@ public function init(array $options = array())
$this->addKernelResourcesToContainer($container);
}

AopComposerLoader::init();

// Register all AOP configuration in the container
$this->configureAop($container);
}
Expand Down Expand Up @@ -156,12 +159,33 @@ protected function getDefaultOptions()
'cacheDir' => null,

'interceptFunctions' => false,
'prebuiltCache' => false,
'includePaths' => array(),
'excludePaths' => array(),
'containerClass' => static::$containerClass,
);
}


/**
* Normalizes options for the kernel
*
* @param array $options List of options
*
* @return array
*/
protected function normalizeOptions(array $options)
{
$options = array_replace($this->getDefaultOptions(), $options);

$options['appDir'] = PathResolver::realpath($options['appDir']);
$options['cacheDir'] = PathResolver::realpath($options['cacheDir']);
$options['includePaths'] = PathResolver::realpath($options['includePaths']);
$options['excludePaths'] = PathResolver::realpath($options['excludePaths']);

return $options;
}

/**
* Configure an AspectContainer with advisors, aspects and pointcuts
*
Expand Down
2 changes: 1 addition & 1 deletion src/Go/Core/GoAspectContainer.php
Expand Up @@ -194,6 +194,6 @@ public function isFresh($timestamp)
$this->maxTimestamp = max(array_map('filemtime', $this->resources));
}

return $this->maxTimestamp < $timestamp;
return $this->maxTimestamp <= $timestamp;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave a line break before the return

}
}
25 changes: 6 additions & 19 deletions src/Go/Instrument/ClassLoading/SourceTransformingLoader.php
Expand Up @@ -38,13 +38,6 @@ class SourceTransformingLoader extends PhpStreamFilter implements LoadTimeWeaver
*/
protected $data = '';

/**
* Filter bucket resource
*
* @var null|resource
*/
protected $bucket = null;

/**
* List of transformers
*
Expand Down Expand Up @@ -100,23 +93,17 @@ public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
$this->data .= $bucket->data;
$this->bucket = $bucket;
$consumed = 0;
}

if ($closing) {
$consumed += strlen($this->data);
if ($closing || feof($this->stream)) {
$consumed = strlen($this->data);

// $this->stream contains pointer to the source
$metadata = new StreamMetaData($this->stream);
$metadata->source = $this->data;

$metadata = new StreamMetaData($this->stream, $this->data);
$this->transformCode($metadata);
$this->bucket->data = $metadata->source;
$this->bucket->datalen = strlen($this->bucket->data);
if (!empty($this->bucket->data)) {
stream_bucket_append($out, $this->bucket);
}

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

return PSFS_PASS_ON;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be more readable to leave a line before the return

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good, need to clean the source code and make a single line before returns.

}
Expand Down
77 changes: 77 additions & 0 deletions src/Go/Instrument/PathResolver.php
@@ -0,0 +1,77 @@
<?php
/**
* Go! AOP framework
*
* @copyright Copyright 2014, Lisachenko Alexander <lisachenko.it@gmail.com>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/

namespace Go\Instrument;

/**
* Special class for resolving path for different file systems, wrappers, etc
*
* @see http://stackoverflow.com/questions/4049856/replace-phps-realpath/4050444
* @see http://bugs.php.net/bug.php?id=52769
*/
class PathResolver
{

/**
* Custom replacement for realpath() and stream_resolve_include_path()
*
* @param string|array $somePath Path without normalization or array of paths
* @param bool $shouldCheckExistence Flag for checking existence of resolved filename
*
* @return array|bool|string
*/
public static function realpath($somePath, $shouldCheckExistence = false)
{
$normalized = null;
if (is_array($somePath)) {
return array_map(array(__CLASS__, __FUNCTION__), $somePath);
}
// Trick to get scheme name and path in one action. If no scheme, then there will be only one part
$components = explode('://', $somePath, 2);
list ($pathScheme, $path) = isset($components[1]) ? $components : array(null, $components[0]);

// Optimization to bypass complex logic for simple paths (eg. not in phar archives)
if (!$pathScheme && ($fastPath = stream_resolve_include_path($somePath))) {
return $fastPath;
}

$isRelative = !$pathScheme && ($path[0] !== '/') && ($path[1] !== ':');
if ($isRelative) {
$path = getcwd() . DIRECTORY_SEPARATOR . $path;
}

// resolve path parts (single dot, double dot and double delimiters)
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
if (strpos($path, '.') !== false) {
$parts = explode(DIRECTORY_SEPARATOR, $path);
$absolutes = array();
foreach ($parts as $part) {
if ('.' == $part) {
continue;
} elseif ('..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
$path = implode(DIRECTORY_SEPARATOR, $absolutes);
}

if ($pathScheme) {
$path = "{$pathScheme}://{$path}";
}

if ($shouldCheckExistence && !file_exists($path)) {
return false;
}

return $path;
}
}
6 changes: 3 additions & 3 deletions src/Go/Instrument/Transformer/CachingTransformer.php
Expand Up @@ -63,13 +63,13 @@ public function __construct(AspectKernel $kernel, $transformers)
}
mkdir($cacheDir, 0770);
}
if (!is_writable($cacheDir)) {
if (!$this->options['prebuiltCache'] && !is_writable($cacheDir)) {
throw new \InvalidArgumentException("Cache directory {$cacheDir} is not writable");
}
$this->cachePath = realpath($cacheDir);
$this->cachePath = $cacheDir;
}

$this->rootPath = realpath($this->options['appDir']);
$this->rootPath = $this->options['appDir'];
$this->transformers = $transformers;
}

Expand Down
62 changes: 18 additions & 44 deletions src/Go/Instrument/Transformer/FilterInjectorTransformer.php
Expand Up @@ -10,6 +10,8 @@

namespace Go\Instrument\Transformer;

use Go\Instrument\PathResolver;

/**
* Injects source filter for require and include operations
*
Expand All @@ -25,22 +27,6 @@ class FilterInjectorTransformer implements SourceTransformer
*/
const PHP_FILTER_READ = 'php://filter/read=';

/**
* Root path of application
*
* This path will be replaced with rewriteToPath
*
* @var string
*/
protected static $rootPath;

/**
* Path to rewrite for absolute address
*
* @var string
*/
protected static $rewriteToPath = null;

/**
* Name of the filter to inject
*
Expand All @@ -49,18 +35,11 @@ class FilterInjectorTransformer implements SourceTransformer
protected static $filterName;

/**
* Flag of configuration
*
* @var bool
*/
protected static $configured = false;

/**
* Debug mode
* Kernel options
*
* @var bool
* @var array
*/
protected static $debug = false;
protected static $options = array();

/**
* Class constructor
Expand All @@ -83,17 +62,11 @@ public function __construct(array $options, $filterName)
*/
protected static function configure(array $options, $filterName)
{
if (self::$configured) {
if (self::$options) {
throw new \RuntimeException("Filter injector can be configured only once.");
}
$rewriteToPath = $options['cacheDir'];
if ($rewriteToPath) {
self::$rewriteToPath = realpath($rewriteToPath);
}
self::$rootPath = realpath($options['appDir']);
self::$options = $options;
self::$filterName = $filterName;
self::$debug = $options['debug'];
self::$configured = true;
}

/**
Expand All @@ -108,25 +81,26 @@ protected static function configure(array $options, $filterName)
*/
public static function rewrite($resource, $originalDir = '')
{
static $appDir, $cacheDir, $debug, $prebuiltCache;
if (!$appDir) {
extract(self::$options, EXTR_IF_EXISTS);
}

$resource = (string) $resource;
if ($resource['0'] !== '/' && $resource[1] !== ':') {
if ($resource['0'] !== '/') {
$resource
= stream_resolve_include_path($resource)
?: stream_resolve_include_path("{$originalDir}/{$resource}");
= PathResolver::realpath($resource, $checkExistence = true)
?: PathResolver::realpath("{$originalDir}/{$resource}", $checkExistence = true);
}
// If the cache is disabled, then use on-fly method
if (!self::$rewriteToPath || self::$debug) {
if (!$cacheDir || $debug) {
return self::PHP_FILTER_READ . self::$filterName . "/resource=" . $resource;
}

$newResource = str_replace(
array('/', self::$rootPath),
array(DIRECTORY_SEPARATOR, self::$rewriteToPath),
$resource
);
$newResource = str_replace($appDir, $cacheDir, $resource);

// Trigger creation of cache, this will create a cache file with $newResource name
if (!file_exists($newResource)) {
if (!$prebuiltCache && !file_exists($newResource)) {
file_get_contents(self::PHP_FILTER_READ . self::$filterName . "/resource=" . $resource);
}

Expand Down
7 changes: 2 additions & 5 deletions src/Go/Instrument/Transformer/MagicConstantTransformer.php
Expand Up @@ -43,11 +43,8 @@ class MagicConstantTransformer extends BaseSourceTransformer
public function __construct(AspectKernel $kernel)
{
parent::__construct($kernel);
self::$rootPath = realpath($this->options['appDir']);
$rewriteToPath = $this->options['cacheDir'];
if ($rewriteToPath) {
self::$rewriteToPath = realpath($rewriteToPath);
}
self::$rootPath = $this->options['appDir'];
self::$rewriteToPath = $this->options['cacheDir'];
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/Go/Instrument/Transformer/StreamMetaData.php
Expand Up @@ -11,6 +11,7 @@
namespace Go\Instrument\Transformer;

use ArrayObject;
use Go\Instrument\PathResolver;
use InvalidArgumentException;

/**
Expand Down Expand Up @@ -48,7 +49,7 @@ public function __construct($stream, $source = null)
$metadata['source'] = $source;
}
if (preg_match('/resource=(.+)$/', $metadata['uri'], $matches)) {
$metadata['uri'] = stream_resolve_include_path($matches[1]);
$metadata['uri'] = PathResolver::realpath($matches[1]);
}
parent::__construct($metadata, ArrayObject::ARRAY_AS_PROPS);
}
Expand Down