Skip to content

Commit 6b82de1

Browse files
ENGCOM-6045: DI Compile - Performance fixes #24897
2 parents 685254d + 187d652 commit 6b82de1

File tree

6 files changed

+124
-71
lines changed

6 files changed

+124
-71
lines changed

setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
use Magento\Framework\App\ObjectManager;
1111
use Magento\Framework\Exception\FileSystemException;
1212

13+
/**
14+
* Class ClassesScanner
15+
*
16+
* @package Magento\Setup\Module\Di\Code\Reader
17+
*/
1318
class ClassesScanner implements ClassesScannerInterface
1419
{
1520
/**
@@ -29,7 +34,8 @@ class ClassesScanner implements ClassesScannerInterface
2934

3035
/**
3136
* @param array $excludePatterns
32-
* @param string $generationDirectory
37+
* @param DirectoryList|null $directoryList
38+
* @throws FileSystemException
3339
*/
3440
public function __construct(array $excludePatterns = [], DirectoryList $directoryList = null)
3541
{
@@ -61,7 +67,7 @@ public function addExcludePatterns(array $excludePatterns)
6167
*/
6268
public function getList($path)
6369
{
64-
70+
// phpcs:ignore
6571
$realPath = realpath($path);
6672
$isGeneration = strpos($realPath, $this->generationDirectory) === 0;
6773

@@ -94,7 +100,7 @@ public function getList($path)
94100
*/
95101
private function extract(\RecursiveIteratorIterator $recursiveIterator)
96102
{
97-
$classes = [];
103+
$classes = [[]];
98104
foreach ($recursiveIterator as $fileItem) {
99105
/** @var $fileItem \SplFileInfo */
100106
if ($fileItem->isDir() || $fileItem->getExtension() !== 'php' || $fileItem->getBasename()[0] == '.') {
@@ -109,12 +115,14 @@ private function extract(\RecursiveIteratorIterator $recursiveIterator)
109115
$fileScanner = new FileClassScanner($fileItemPath);
110116
$classNames = $fileScanner->getClassNames();
111117
$this->includeClasses($classNames, $fileItemPath);
112-
$classes = array_merge($classes, $classNames);
118+
$classes [] = $classNames;
113119
}
114-
return $classes;
120+
return array_merge(...$classes);
115121
}
116122

117123
/**
124+
* Include classes from file path
125+
*
118126
* @param array $classNames
119127
* @param string $fileItemPath
120128
* @return bool Whether the class is included or not
@@ -123,6 +131,7 @@ private function includeClasses(array $classNames, $fileItemPath)
123131
{
124132
foreach ($classNames as $className) {
125133
if (!class_exists($className)) {
134+
// phpcs:ignore
126135
require_once $fileItemPath;
127136
return true;
128137
}

setup/src/Magento/Setup/Module/Di/Code/Reader/FileClassScanner.php

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,24 @@
66

77
namespace Magento\Setup\Module\Di\Code\Reader;
88

9+
/**
10+
* Class FileClassScanner
11+
*
12+
* @package Magento\Setup\Module\Di\Code\Reader
13+
*/
914
class FileClassScanner
1015
{
16+
private const NAMESPACE_TOKENS = [
17+
T_WHITESPACE => true,
18+
T_STRING => true,
19+
T_NS_SEPARATOR => true
20+
];
21+
22+
private const ALLOWED_OPEN_BRACES_TOKENS = [
23+
T_CURLY_OPEN => true,
24+
T_DOLLAR_OPEN_CURLY_BRACES => true,
25+
T_STRING_VARNAME => true];
26+
1127
/**
1228
* The filename of the file to introspect
1329
*
@@ -34,7 +50,9 @@ class FileClassScanner
3450
*/
3551
public function __construct($filename)
3652
{
53+
// phpcs:ignore
3754
$filename = realpath($filename);
55+
// phpcs:ignore
3856
if (!file_exists($filename) || !\is_file($filename)) {
3957
throw new InvalidFileException(
4058
sprintf(
@@ -53,12 +71,14 @@ public function __construct($filename)
5371
*/
5472
public function getFileContents()
5573
{
74+
// phpcs:ignore
5675
return file_get_contents($this->filename);
5776
}
5877

5978
/**
60-
* Extracts the fully qualified class name from a file. It only searches for the first match and stops looking
61-
* as soon as it enters the class definition itself.
79+
* Extracts the fully qualified class name from a file.
80+
*
81+
* It only searches for the first match and stops looking as soon as it enters the class definition itself.
6282
*
6383
* Warnings are suppressed for this method due to a micro-optimization that only really shows up when this logic
6484
* is called several millions of times, which can happen quite easily with even moderately sized codebases.
@@ -69,35 +89,36 @@ public function getFileContents()
6989
*/
7090
private function extract()
7191
{
72-
$allowedOpenBraces = [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES, T_STRING_VARNAME];
7392
$classes = [];
74-
$namespace = '';
93+
$namespaceParts = [];
7594
$class = '';
7695
$triggerClass = false;
7796
$triggerNamespace = false;
7897
$braceLevel = 0;
7998
$bracedNamespace = false;
8099

100+
// phpcs:ignore
81101
$this->tokens = token_get_all($this->getFileContents());
82102
foreach ($this->tokens as $index => $token) {
103+
$tokenIsArray = is_array($token);
83104
// Is either a literal brace or an interpolated brace with a variable
84-
if ($token == '{' || (is_array($token) && in_array($token[0], $allowedOpenBraces))) {
105+
if ($token === '{' || ($tokenIsArray && isset(self::ALLOWED_OPEN_BRACES_TOKENS[$token[0]]))) {
85106
$braceLevel++;
86-
} else if ($token == '}') {
107+
} elseif ($token === '}') {
87108
$braceLevel--;
88109
}
89110
// The namespace keyword was found in the last loop
90111
if ($triggerNamespace) {
91112
// A string ; or a discovered namespace that looks like "namespace name { }"
92-
if (!is_array($token) || ($namespace && $token[0] == T_WHITESPACE)) {
113+
if (!$tokenIsArray || ($namespaceParts && $token[0] === T_WHITESPACE)) {
93114
$triggerNamespace = false;
94-
$namespace .= '\\';
115+
$namespaceParts[] = '\\';
95116
continue;
96117
}
97-
$namespace .= $token[1];
118+
$namespaceParts[] = $token[1];
98119

99-
// The class keyword was found in the last loop
100-
} else if ($triggerClass && $token[0] == T_STRING) {
120+
// The class keyword was found in the last loop
121+
} elseif ($triggerClass && $token[0] === T_STRING) {
101122
$triggerClass = false;
102123
$class = $token[1];
103124
}
@@ -106,7 +127,7 @@ private function extract()
106127
case T_NAMESPACE:
107128
// Current loop contains the namespace keyword. Between this and the semicolon is the namespace
108129
$triggerNamespace = true;
109-
$namespace = '';
130+
$namespaceParts = [];
110131
$bracedNamespace = $this->isBracedNamespace($index);
111132
break;
112133
case T_CLASS:
@@ -118,9 +139,8 @@ private function extract()
118139
}
119140

120141
// We have a class name, let's concatenate and store it!
121-
if ($class != '') {
122-
$namespace = trim($namespace);
123-
$fqClassName = $namespace . trim($class);
142+
if ($class !== '') {
143+
$fqClassName = trim(join('', $namespaceParts)) . trim($class);
124144
$classes[] = $fqClassName;
125145
$class = '';
126146
}
@@ -139,24 +159,25 @@ private function isBracedNamespace($index)
139159
$len = count($this->tokens);
140160
while ($index++ < $len) {
141161
if (!is_array($this->tokens[$index])) {
142-
if ($this->tokens[$index] == ';') {
162+
if ($this->tokens[$index] === ';') {
143163
return false;
144-
} else if ($this->tokens[$index] == '{') {
164+
} elseif ($this->tokens[$index] === '{') {
145165
return true;
146166
}
147167
continue;
148168
}
149169

150-
if (!in_array($this->tokens[$index][0], [T_WHITESPACE, T_STRING, T_NS_SEPARATOR])) {
170+
if (!isset(self::NAMESPACE_TOKENS[$this->tokens[$index][0]])) {
151171
throw new InvalidFileException('Namespace not defined properly');
152172
}
153173
}
154174
throw new InvalidFileException('Could not find namespace termination');
155175
}
156176

157177
/**
158-
* Retrieves the first class found in a class file. The return value is in an array format so it retains the
159-
* same usage as the FileScanner.
178+
* Retrieves the first class found in a class file.
179+
*
180+
* The return value is in an array format so it retains the same usage as the FileScanner.
160181
*
161182
* @return array
162183
*/

setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
use Magento\Setup\Module\Di\Compiler\Log\Log;
1212
use \Magento\Framework\Reflection\TypeProcessor;
1313

14+
/**
15+
* Class PhpScanner
16+
*
17+
* @package Magento\Setup\Module\Di\Code\Scanner
18+
*/
1419
class PhpScanner implements ScannerInterface
1520
{
1621
/**
@@ -167,48 +172,51 @@ protected function _fetchMissingExtensionAttributesClasses($reflectionClass, $fi
167172
*
168173
* @param array $files
169174
* @return array
175+
* @throws \ReflectionException
170176
*/
171177
public function collectEntities(array $files)
172178
{
173-
$output = [];
179+
$output = [[]];
174180
foreach ($files as $file) {
175181
$classes = $this->_getDeclaredClasses($file);
176182
foreach ($classes as $className) {
177183
$reflectionClass = new \ReflectionClass($className);
178-
$output = array_merge(
179-
$output,
180-
$this->_fetchFactories($reflectionClass, $file),
181-
$this->_fetchMissingExtensionAttributesClasses($reflectionClass, $file)
182-
);
184+
$output [] = $this->_fetchFactories($reflectionClass, $file);
185+
$output [] = $this->_fetchMissingExtensionAttributesClasses($reflectionClass, $file);
183186
}
184187
}
185-
return array_unique($output);
188+
return array_unique(array_merge(...$output));
186189
}
187190

188191
/**
189-
* @param $tokenIterator int
190-
* @param $count int
191-
* @param $tokens array
192+
* Fetch namespaces from tokenized PHP file
193+
*
194+
* @param int $tokenIterator
195+
* @param int $count
196+
* @param array $tokens
192197
* @return string
193198
*/
194199
protected function _fetchNamespace($tokenIterator, $count, $tokens)
195200
{
196-
$namespace = '';
201+
$namespaceParts = [];
197202
for ($tokenOffset = $tokenIterator + 1; $tokenOffset < $count; ++$tokenOffset) {
198203
if ($tokens[$tokenOffset][0] === T_STRING) {
199-
$namespace .= "\\" . $tokens[$tokenOffset][1];
204+
$namespaceParts[] = "\\";
205+
$namespaceParts[] = $tokens[$tokenOffset][1];
200206
} elseif ($tokens[$tokenOffset] === '{' || $tokens[$tokenOffset] === ';') {
201207
break;
202208
}
203209
}
204-
return $namespace;
210+
return join('', $namespaceParts);
205211
}
206212

207213
/**
208-
* @param $namespace string
209-
* @param $tokenIterator int
210-
* @param $count int
211-
* @param $tokens array
214+
* Fetch class names from tokenized PHP file
215+
*
216+
* @param string $namespace
217+
* @param int $tokenIterator
218+
* @param int $count
219+
* @param array $tokens
212220
* @return array
213221
*/
214222
protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens)
@@ -230,23 +238,24 @@ protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens)
230238
*/
231239
protected function _getDeclaredClasses($file)
232240
{
233-
$classes = [];
234-
$namespace = '';
241+
$classes = [[]];
242+
$namespaceParts = [];
243+
// phpcs:ignore
235244
$tokens = token_get_all(file_get_contents($file));
236245
$count = count($tokens);
237246

238247
for ($tokenIterator = 0; $tokenIterator < $count; $tokenIterator++) {
239248
if ($tokens[$tokenIterator][0] == T_NAMESPACE) {
240-
$namespace .= $this->_fetchNamespace($tokenIterator, $count, $tokens);
249+
$namespaceParts[] = $this->_fetchNamespace($tokenIterator, $count, $tokens);
241250
}
242251

243252
if (($tokens[$tokenIterator][0] == T_CLASS || $tokens[$tokenIterator][0] == T_INTERFACE)
244253
&& $tokens[$tokenIterator - 1][0] != T_DOUBLE_COLON
245254
) {
246-
$classes = array_merge($classes, $this->_fetchClasses($namespace, $tokenIterator, $count, $tokens));
255+
$classes[] = $this->_fetchClasses(join('', $namespaceParts), $tokenIterator, $count, $tokens);
247256
}
248257
}
249-
return array_unique($classes);
258+
return array_unique(array_merge(...$classes));
250259
}
251260

252261
/**
@@ -263,7 +272,7 @@ private function shouldGenerateClass($missingClassName, $entityType, $file)
263272
if (class_exists($missingClassName)) {
264273
return false;
265274
}
266-
} catch (\RuntimeException $e) {
275+
} catch (\RuntimeException $e) { //phpcs:ignore
267276
}
268277
$sourceClassName = $this->getSourceClassName($missingClassName, $entityType);
269278
if (!class_exists($sourceClassName) && !interface_exists($sourceClassName)) {

setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/BackslashTrim.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,21 @@
88

99
use Magento\Setup\Module\Di\Compiler\Config\ModificationInterface;
1010

11+
/**
12+
* Class BackslashTrim
13+
*
14+
* @package Magento\Setup\Module\Di\Compiler\Config\Chain
15+
*/
1116
class BackslashTrim implements ModificationInterface
1217
{
18+
/**
19+
* Argument keys which require recursive resolving
20+
*/
21+
private const RECURSIVE_ARGUMENT_KEYS = [
22+
'_i_' => true, // shared instance of a class or interface
23+
'_ins_' => true // non-shared instance of a class or interface
24+
];
25+
1326
/**
1427
* Modifies input config
1528
*
@@ -48,7 +61,6 @@ private function resolveInstancesNames(array $arguments)
4861
* Resolves instances arguments
4962
*
5063
* @param array $argument
51-
* @return array
5264
*/
5365
private function resolveArguments(&$argument)
5466
{
@@ -57,15 +69,12 @@ private function resolveArguments(&$argument)
5769
}
5870

5971
foreach ($argument as $key => &$value) {
60-
if (in_array($key, ['_i_', '_ins_'], true)) {
72+
if (isset(self::RECURSIVE_ARGUMENT_KEYS[$key])) {
6173
$value = ltrim($value, '\\');
6274
continue;
6375
}
6476

65-
if (is_array($value)) {
66-
$this->resolveArguments($value);
67-
}
77+
$this->resolveArguments($value);
6878
}
69-
return;
7079
}
7180
}

0 commit comments

Comments
 (0)