Skip to content

Commit

Permalink
Merge pull request #38 from goaop/fix/resolve-relative-paths
Browse files Browse the repository at this point in the history
Resolve all relative paths to be compatible with original reflection. Closes #28, #31
  • Loading branch information
lisachenko authored Sep 25, 2016
2 parents 40032e3 + c90583a commit fdee566
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 1 deletion.
83 changes: 83 additions & 0 deletions src/Instrument/PathResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
/*
* Go! AOP framework
*
* @copyright Copyright 2014, Lisachenko Alexander <[email protected]>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/

namespace Go\ParserReflection\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
*
* @link https://github.com/goaop/framework/blob/master/src/Instrument/PathResolver.php
*/
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)
{
// Do not resolve empty string/false/arrays into the current path
if (!$somePath) {
return $somePath;
}

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 = [];
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;
}
}
8 changes: 7 additions & 1 deletion src/Locator/ComposerLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Go\ParserReflection\Locator;

use Composer\Autoload\ClassLoader;
use Go\ParserReflection\Instrument\PathResolver;
use Go\ParserReflection\LocatorInterface;
use Go\ParserReflection\ReflectionException;

Expand Down Expand Up @@ -50,6 +51,11 @@ public function __construct(ClassLoader $loader = null)
*/
public function locateClass($className)
{
return $this->loader->findFile($className);
$filePath = $this->loader->findFile($className);
if (!empty($filePath)) {
$filePath = PathResolver::realpath($filePath);
}

return $filePath;
}
}
2 changes: 2 additions & 0 deletions src/ReflectionEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Go\ParserReflection;

use Go\ParserReflection\Instrument\PathResolver;
use PhpParser\Lexer;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
Expand Down Expand Up @@ -204,6 +205,7 @@ public static function parseClassProperty($fullClassName, $propertyName)
*/
public static function parseFile($fileName, $fileContent = null)
{
$fileName = PathResolver::realpath($fileName);
if (isset(self::$parsedFiles[$fileName])) {
return self::$parsedFiles[$fileName];
}
Expand Down
2 changes: 2 additions & 0 deletions src/ReflectionFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Go\ParserReflection;


use Go\ParserReflection\Instrument\PathResolver;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Namespace_;
Expand Down Expand Up @@ -50,6 +51,7 @@ class ReflectionFile
*/
public function __construct($fileName, $topLevelNodes = null)
{
$fileName = PathResolver::realpath($fileName);
$this->fileName = $fileName;
$this->topLevelNodes = $topLevelNodes ?: ReflectionEngine::parseFile($fileName);
}
Expand Down
2 changes: 2 additions & 0 deletions src/ReflectionFileNamespace.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Go\ParserReflection;

use Go\ParserReflection\Instrument\PathResolver;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Const_;
Expand Down Expand Up @@ -73,6 +74,7 @@ class ReflectionFileNamespace
*/
public function __construct($fileName, $namespaceName, Namespace_ $namespaceNode = null)
{
$fileName = PathResolver::realpath($fileName);
if (!$namespaceNode) {
$namespaceNode = ReflectionEngine::parseFileNamespace($fileName, $namespaceName);
}
Expand Down
17 changes: 17 additions & 0 deletions tests/Locator/ComposerLocatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
namespace Go\ParserReflection\Locator;

use Go\ParserReflection\ReflectionClass;

class ComposerLocatorTest extends \PHPUnit_Framework_TestCase
{
public function testLocateClass()
{
$locator = new ComposerLocator();
$reflectionClass = new \ReflectionClass(ReflectionClass::class);
$this->assertSame(
$reflectionClass->getFileName(),
$locator->locateClass(ReflectionClass::class)
);
}
}

0 comments on commit fdee566

Please sign in to comment.