Skip to content

Commit c90583a

Browse files
committed
Resolve all relative paths to be compatibe with original reflection filenames, thanks to @aik099. Resolves #31
1 parent 40032e3 commit c90583a

File tree

6 files changed

+113
-1
lines changed

6 files changed

+113
-1
lines changed

src/Instrument/PathResolver.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
/*
3+
* Go! AOP framework
4+
*
5+
* @copyright Copyright 2014, Lisachenko Alexander <lisachenko.it@gmail.com>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
11+
namespace Go\ParserReflection\Instrument;
12+
13+
/**
14+
* Special class for resolving path for different file systems, wrappers, etc
15+
*
16+
* @see http://stackoverflow.com/questions/4049856/replace-phps-realpath/4050444
17+
* @see http://bugs.php.net/bug.php?id=52769
18+
*
19+
* @link https://github.com/goaop/framework/blob/master/src/Instrument/PathResolver.php
20+
*/
21+
class PathResolver
22+
{
23+
24+
/**
25+
* Custom replacement for realpath() and stream_resolve_include_path()
26+
*
27+
* @param string|array $somePath Path without normalization or array of paths
28+
* @param bool $shouldCheckExistence Flag for checking existence of resolved filename
29+
*
30+
* @return array|bool|string
31+
*/
32+
public static function realpath($somePath, $shouldCheckExistence = false)
33+
{
34+
// Do not resolve empty string/false/arrays into the current path
35+
if (!$somePath) {
36+
return $somePath;
37+
}
38+
39+
if (is_array($somePath)) {
40+
return array_map(array(__CLASS__, __FUNCTION__), $somePath);
41+
}
42+
// Trick to get scheme name and path in one action. If no scheme, then there will be only one part
43+
$components = explode('://', $somePath, 2);
44+
list ($pathScheme, $path) = isset($components[1]) ? $components : array(null, $components[0]);
45+
46+
// Optimization to bypass complex logic for simple paths (eg. not in phar archives)
47+
if (!$pathScheme && ($fastPath = stream_resolve_include_path($somePath))) {
48+
return $fastPath;
49+
}
50+
51+
$isRelative = !$pathScheme && ($path[0] !== '/') && ($path[1] !== ':');
52+
if ($isRelative) {
53+
$path = getcwd() . DIRECTORY_SEPARATOR . $path;
54+
}
55+
56+
// resolve path parts (single dot, double dot and double delimiters)
57+
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
58+
if (strpos($path, '.') !== false) {
59+
$parts = explode(DIRECTORY_SEPARATOR, $path);
60+
$absolutes = [];
61+
foreach ($parts as $part) {
62+
if ('.' == $part) {
63+
continue;
64+
} elseif ('..' == $part) {
65+
array_pop($absolutes);
66+
} else {
67+
$absolutes[] = $part;
68+
}
69+
}
70+
$path = implode(DIRECTORY_SEPARATOR, $absolutes);
71+
}
72+
73+
if ($pathScheme) {
74+
$path = "{$pathScheme}://{$path}";
75+
}
76+
77+
if ($shouldCheckExistence && !file_exists($path)) {
78+
return false;
79+
}
80+
81+
return $path;
82+
}
83+
}

src/Locator/ComposerLocator.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace Go\ParserReflection\Locator;
1212

1313
use Composer\Autoload\ClassLoader;
14+
use Go\ParserReflection\Instrument\PathResolver;
1415
use Go\ParserReflection\LocatorInterface;
1516
use Go\ParserReflection\ReflectionException;
1617

@@ -50,6 +51,11 @@ public function __construct(ClassLoader $loader = null)
5051
*/
5152
public function locateClass($className)
5253
{
53-
return $this->loader->findFile($className);
54+
$filePath = $this->loader->findFile($className);
55+
if (!empty($filePath)) {
56+
$filePath = PathResolver::realpath($filePath);
57+
}
58+
59+
return $filePath;
5460
}
5561
}

src/ReflectionEngine.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Go\ParserReflection;
1212

13+
use Go\ParserReflection\Instrument\PathResolver;
1314
use PhpParser\Lexer;
1415
use PhpParser\Node;
1516
use PhpParser\Node\Stmt\ClassLike;
@@ -204,6 +205,7 @@ public static function parseClassProperty($fullClassName, $propertyName)
204205
*/
205206
public static function parseFile($fileName, $fileContent = null)
206207
{
208+
$fileName = PathResolver::realpath($fileName);
207209
if (isset(self::$parsedFiles[$fileName])) {
208210
return self::$parsedFiles[$fileName];
209211
}

src/ReflectionFile.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace Go\ParserReflection;
1212

1313

14+
use Go\ParserReflection\Instrument\PathResolver;
1415
use PhpParser\Node;
1516
use PhpParser\Node\Name\FullyQualified;
1617
use PhpParser\Node\Stmt\Namespace_;
@@ -50,6 +51,7 @@ class ReflectionFile
5051
*/
5152
public function __construct($fileName, $topLevelNodes = null)
5253
{
54+
$fileName = PathResolver::realpath($fileName);
5355
$this->fileName = $fileName;
5456
$this->topLevelNodes = $topLevelNodes ?: ReflectionEngine::parseFile($fileName);
5557
}

src/ReflectionFileNamespace.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Go\ParserReflection;
1212

13+
use Go\ParserReflection\Instrument\PathResolver;
1314
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
1415
use PhpParser\Node\Stmt\ClassLike;
1516
use PhpParser\Node\Stmt\Const_;
@@ -73,6 +74,7 @@ class ReflectionFileNamespace
7374
*/
7475
public function __construct($fileName, $namespaceName, Namespace_ $namespaceNode = null)
7576
{
77+
$fileName = PathResolver::realpath($fileName);
7678
if (!$namespaceNode) {
7779
$namespaceNode = ReflectionEngine::parseFileNamespace($fileName, $namespaceName);
7880
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
namespace Go\ParserReflection\Locator;
3+
4+
use Go\ParserReflection\ReflectionClass;
5+
6+
class ComposerLocatorTest extends \PHPUnit_Framework_TestCase
7+
{
8+
public function testLocateClass()
9+
{
10+
$locator = new ComposerLocator();
11+
$reflectionClass = new \ReflectionClass(ReflectionClass::class);
12+
$this->assertSame(
13+
$reflectionClass->getFileName(),
14+
$locator->locateClass(ReflectionClass::class)
15+
);
16+
}
17+
}

0 commit comments

Comments
 (0)