Skip to content

Commit 0923326

Browse files
authored
Merge pull request #9 from adriansuter/patch-refactor
Patch refactor
2 parents 8e94772 + eb8a613 commit 0923326

File tree

4 files changed

+205
-81
lines changed

4 files changed

+205
-81
lines changed

src/CodeConverter.php

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,7 @@ public function __construct(
7575
?Standard $printer = null,
7676
?NodeFinder $nodeFinder = null
7777
) {
78-
$this->lexer = $lexer ?? new Emulative(
79-
[
80-
'usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'],
81-
]
82-
);
78+
$this->lexer = $lexer ?? $this->defaultLexer();
8379

8480
$this->parser = $parser ?? new Php7($this->lexer);
8581

@@ -95,46 +91,71 @@ public function __construct(
9591
$this->nodeFinder = $nodeFinder ?? new NodeFinder();
9692
}
9793

94+
/**
95+
* @return Lexer
96+
*/
97+
private function defaultLexer(): Lexer
98+
{
99+
return new Emulative(
100+
[
101+
'usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'],
102+
]
103+
);
104+
}
105+
98106
/**
99107
* Convert the given source code.
100108
*
101109
* @param string $code The source code.
102-
* @param array $functionCallMappings The function call mappings.
110+
* @param array $functionCallMap The function call map.
103111
*
104112
* @return string
105113
*/
106-
public function convert(string $code, array $functionCallMappings): string
114+
public function convert(string $code, array $functionCallMap): string
107115
{
108116
$oldStmts = $this->parser->parse($code);
109117
$oldTokens = $this->lexer->getTokens();
110118

111119
$overridePlaceholders = [];
112120

113121
$newStmts = $this->traverser->traverse($oldStmts);
122+
123+
// Find function calls.
114124
$funcCalls = $this->nodeFinder->findInstanceOf($newStmts, FuncCall::class);
115125
foreach ($funcCalls as $funcCall) {
116126
/** @var FuncCall $funcCall */
117127
if (!$funcCall->name->hasAttribute(self::ATTR_RESOLVED_NAME)) {
128+
// This function call has no resolved fully qualified name.
118129
continue;
119130
}
120131

121132
/** @var FullyQualified $resolvedName */
122133
$resolvedName = $funcCall->name->getAttribute(self::ATTR_RESOLVED_NAME);
123134

124135
$resolvedNameCode = $resolvedName->toCodeString();
125-
if (isset($functionCallMappings[$resolvedNameCode])) {
126-
$k = uniqid(md5($resolvedNameCode), true);
127-
$overridePlaceholders[$k] = $functionCallMappings[$resolvedNameCode];
136+
if (isset($functionCallMap[$resolvedNameCode])) {
137+
// There is a function call map > Create a unique key.
138+
$key = uniqid(md5($resolvedNameCode), true);
139+
140+
// Put the key into the overridePlaceholders array as at the end we need to
141+
// replace those keys with the corresponding target function call.
142+
$overridePlaceholders[$key] = $functionCallMap[$resolvedNameCode];
128143

129-
$funcCall->name = new FullyQualified($k);
144+
// Replace the name to be the fully qualified name, i.e. the given unique key
145+
// (we will replace that at the end).
146+
$funcCall->name = new FullyQualified($key);
130147
}
131148
}
132149

150+
// Print the source code.
133151
$code = $this->printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
152+
153+
// Return the source code if there are no override placeholders.
134154
if (empty($overridePlaceholders)) {
135155
return $code;
136156
}
137157

158+
// Replace all override placeholders by their target function call.
138159
return str_replace(array_keys($overridePlaceholders), array_values($overridePlaceholders), $code);
139160
}
140161
}

src/FileStreamWrapper.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,12 @@ public function stream_open(string $path, string $mode, int $options, ?string &$
356356

357357
// TODO Implement error reporting as well as opened_path.
358358

359-
$functionCallMappings = Override::getFunctionMappings($path);
359+
$functionCallMap = Override::getFunctionCallMap($path);
360360

361361
// Replace the global function calls into local function calls.
362-
if (!empty($functionCallMappings)) {
362+
if (!empty($functionCallMap)) {
363363
$source = file_get_contents($path, $usePath);
364-
365-
$source = Override::getCodeConverter()->convert($source, $functionCallMappings);
364+
$source = Override::convert($source, $functionCallMap);
366365

367366
$this->resource = fopen('php://temp', 'w+');
368367
fwrite($this->resource, $source);

src/Override.php

Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,20 @@
3535
use function substr;
3636
use function trim;
3737

38+
/**
39+
* @package AdrianSuter\Autoload\Override
40+
*/
3841
class Override
3942
{
4043
/**
4144
* @var array
4245
*/
43-
private static $fileFunctionCallMappings;
46+
private static $fileFunctionCallMap;
4447

4548
/**
4649
* @var array
4750
*/
48-
private static $dirFunctionCallMappings;
51+
private static $dirFunctionCallMap;
4952

5053
/**
5154
* @var CodeConverter|null
@@ -64,7 +67,7 @@ public static function setCodeConverter(CodeConverter $converter): void
6467
/**
6568
* @return CodeConverter
6669
*/
67-
public static function getCodeConverter(): CodeConverter
70+
private static function getCodeConverter(): CodeConverter
6871
{
6972
if (self::$converter === null) {
7073
self::setCodeConverter(new CodeConverter());
@@ -75,13 +78,13 @@ public static function getCodeConverter(): CodeConverter
7578

7679
/**
7780
* @param ClassLoader $classLoader
78-
* @param string[]|Closure[] $functionCallMappings
79-
* @param string $namespace
81+
* @param string[]|Closure[] $functionCallMap
82+
* @param string $overrideNamespace
8083
*/
8184
public static function apply(
8285
ClassLoader $classLoader,
83-
array $functionCallMappings,
84-
string $namespace = 'PHPAutoloadOverride'
86+
array $functionCallMap,
87+
string $overrideNamespace = 'PHPAutoloadOverride'
8588
) {
8689
if ($classLoader->getApcuPrefix() !== null) {
8790
throw new RuntimeException('APC User Cache is not supported.');
@@ -92,15 +95,16 @@ public static function apply(
9295
$classLoader->loadClass(FileStreamWrapper::class);
9396
}
9497

95-
// Reset the function call mappings.
96-
self::$fileFunctionCallMappings = [];
97-
self::$dirFunctionCallMappings = [];
98+
// Reset the function call maps.
99+
self::$fileFunctionCallMap = [];
100+
self::$dirFunctionCallMap = [];
98101

99102
// Initialize the collection of files we would force to load (include).
100103
$autoloadCollection = new AutoloadCollection();
101104

102-
foreach ($functionCallMappings as $fqn => $mappings) {
103-
$fqnFunctionCallMappings = self::buildMappings($mappings, $namespace);
105+
foreach ($functionCallMap as $fqn => $map) {
106+
// Build the fqn function call map.
107+
$fqnFunctionCallMap = self::buildFunctionCallMap($map, $overrideNamespace);
104108

105109
if (substr($fqn, -1, 1) === '\\') {
106110
// The given fqn is a namespace.
@@ -124,8 +128,7 @@ public static function apply(
124128
if (is_dir($dir) && !isset($handled[$dir])) {
125129
$handled[$dir] = true;
126130

127-
self::addNamespaceData([$dir], $fqnFunctionCallMappings);
128-
$autoloadCollection->addDirectory($dir);
131+
self::addDirectoryFunctionCallMap($autoloadCollection, $dir, $fqnFunctionCallMap);
129132
}
130133
}
131134
}
@@ -140,13 +143,13 @@ public static function apply(
140143
continue;
141144
}
142145

143-
if (isset(self::$fileFunctionCallMappings[$p])) {
144-
self::$fileFunctionCallMappings[$p] = array_merge(
145-
$fqnFunctionCallMappings,
146-
self::$fileFunctionCallMappings[$p]
146+
if (isset(self::$fileFunctionCallMap[$p])) {
147+
self::$fileFunctionCallMap[$p] = array_merge(
148+
$fqnFunctionCallMap,
149+
self::$fileFunctionCallMap[$p]
147150
);
148151
} else {
149-
self::$fileFunctionCallMappings[$p] = $fqnFunctionCallMappings;
152+
self::$fileFunctionCallMap[$p] = $fqnFunctionCallMap;
150153
}
151154
$autoloadCollection->addFile($p);
152155
}
@@ -170,85 +173,107 @@ public static function apply(
170173
continue;
171174
}
172175

173-
self::$fileFunctionCallMappings[$path] = $fqnFunctionCallMappings;
176+
self::$fileFunctionCallMap[$path] = $fqnFunctionCallMap;
174177
$autoloadCollection->addFile($path);
175178
}
176179

177180
// Load the classes that are affected by the FQFC-override converter.
178181
stream_wrapper_unregister('file');
179182
stream_wrapper_register('file', FileStreamWrapper::class);
180-
foreach ($autoloadCollection->getFilePaths() as $file) {
183+
foreach ($autoloadCollection->getFilePaths() as $filePath) {
181184
/** @noinspection PhpIncludeInspection */
182-
include_once $file;
185+
include_once $filePath;
183186
}
184187

185188
stream_wrapper_restore('file');
186189
clearstatcache();
187190
}
188191

189192
/**
190-
* @param string[]|Closure[] $mappings
193+
* Build a mapping between root namespaced function calls and their overridden fully qualified name.
194+
*
195+
* @param string[]|Closure[] $map
191196
* @param string $namespace
192197
*
193-
* @return array
198+
* @return string[]
194199
*/
195-
private static function buildMappings(array $mappings, string $namespace): array
200+
private static function buildFunctionCallMap(array $map, string $namespace): array
196201
{
197-
$fcMappings = [];
198-
foreach ($mappings as $key => $val) {
202+
$functionCallMap = [];
203+
foreach ($map as $key => $val) {
199204
if (is_numeric($key)) {
200-
$fcMappings['\\' . $val] = $namespace . '\\' . $val;
205+
$functionCallMap['\\' . $val] = $namespace . '\\' . $val;
201206
} elseif (is_string($val)) {
202-
$fcMappings['\\' . $key] = $val . '\\' . $key;
207+
$functionCallMap['\\' . $key] = $val . '\\' . $key;
203208
} elseif ($val instanceof Closure) {
204209
$name = $key . '_' . spl_object_hash($val);
205210
ClosureHandler::getInstance()->addClosure($name, $val);
206211

207-
$fcMappings['\\' . $key] = ClosureHandler::class . '::getInstance()->' . $name;
212+
$functionCallMap['\\' . $key] = ClosureHandler::class . '::getInstance()->' . $name;
208213
}
209214
}
210215

211-
return $fcMappings;
216+
return $functionCallMap;
212217
}
213218

214-
private static function addNamespaceData(array $directories, array $functionMappings): void
215-
{
216-
foreach ($directories as $dir) {
217-
if (!file_exists($dir)) {
218-
continue;
219-
}
219+
private static function addDirectoryFunctionCallMap(
220+
AutoloadCollection $autoloadCollection,
221+
string $directory,
222+
array $fqnFunctionCallMap
223+
): void {
224+
$directory = realpath($directory);
225+
if ($directory === false) {
226+
return;
227+
}
220228

221-
$dir = realpath($dir);
222-
if (isset(self::$dirFunctionCallMappings[$dir])) {
223-
self::$dirFunctionCallMappings[$dir] = array_merge(
224-
self::$dirFunctionCallMappings[$dir],
225-
$functionMappings
226-
);
227-
} else {
228-
self::$dirFunctionCallMappings[$dir] = $functionMappings;
229-
}
229+
if (isset(self::$dirFunctionCallMap[$directory])) {
230+
self::$dirFunctionCallMap[$directory] = array_merge(
231+
self::$dirFunctionCallMap[$directory],
232+
$fqnFunctionCallMap
233+
);
234+
} else {
235+
self::$dirFunctionCallMap[$directory] = $fqnFunctionCallMap;
230236
}
237+
238+
$autoloadCollection->addDirectory($directory);
231239
}
232240

233241
/**
234242
* @param string $filePath
235243
*
236244
* @return string[]
237245
*/
238-
public static function getFunctionMappings(string $filePath): array
246+
public static function getFunctionCallMap(string $filePath): array
239247
{
240248
$filePath = realpath($filePath);
249+
if ($filePath === false) {
250+
return [];
251+
}
252+
241253
$dirPath = dirname($filePath);
242254

243-
$mappings = [];
244-
if (isset(self::$dirFunctionCallMappings[$dirPath])) {
245-
$mappings = array_merge($mappings, self::$dirFunctionCallMappings[$dirPath]);
255+
$functionCallMap = [];
256+
if (isset(self::$dirFunctionCallMap[$dirPath])) {
257+
$functionCallMap = array_merge($functionCallMap, self::$dirFunctionCallMap[$dirPath]);
246258
}
247259

248-
if (isset(self::$fileFunctionCallMappings[$filePath])) {
249-
$mappings = array_merge($mappings, self::$fileFunctionCallMappings[$filePath]);
260+
if (isset(self::$fileFunctionCallMap[$filePath])) {
261+
$functionCallMap = array_merge($functionCallMap, self::$fileFunctionCallMap[$filePath]);
250262
}
251263

252-
return $mappings;
264+
return $functionCallMap;
265+
}
266+
267+
/**
268+
* Convert the source code using the fqn function call map.
269+
*
270+
* @param string $source
271+
* @param array $functionCallMap
272+
*
273+
* @return string
274+
*/
275+
public static function convert(string $source, array $functionCallMap): string
276+
{
277+
return self::getCodeConverter()->convert($source, $functionCallMap);
253278
}
254279
}

0 commit comments

Comments
 (0)