Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Exception\FileSystemException;

/**
* Class ClassesScanner
*
* @package Magento\Setup\Module\Di\Code\Reader
*/
class ClassesScanner implements ClassesScannerInterface
{
/**
Expand All @@ -29,7 +34,8 @@ class ClassesScanner implements ClassesScannerInterface

/**
* @param array $excludePatterns
* @param string $generationDirectory
* @param DirectoryList|null $directoryList
* @throws FileSystemException
*/
public function __construct(array $excludePatterns = [], DirectoryList $directoryList = null)
{
Expand Down Expand Up @@ -61,7 +67,7 @@ public function addExcludePatterns(array $excludePatterns)
*/
public function getList($path)
{

// phpcs:ignore
$realPath = realpath($path);
$isGeneration = strpos($realPath, $this->generationDirectory) === 0;

Expand Down Expand Up @@ -94,7 +100,7 @@ public function getList($path)
*/
private function extract(\RecursiveIteratorIterator $recursiveIterator)
{
$classes = [];
$classes = [[]];
foreach ($recursiveIterator as $fileItem) {
/** @var $fileItem \SplFileInfo */
if ($fileItem->isDir() || $fileItem->getExtension() !== 'php' || $fileItem->getBasename()[0] == '.') {
Expand All @@ -109,12 +115,14 @@ private function extract(\RecursiveIteratorIterator $recursiveIterator)
$fileScanner = new FileClassScanner($fileItemPath);
$classNames = $fileScanner->getClassNames();
$this->includeClasses($classNames, $fileItemPath);
$classes = array_merge($classes, $classNames);
$classes [] = $classNames;
}
return $classes;
return array_merge(...$classes);
}

/**
* Include classes from file path
*
* @param array $classNames
* @param string $fileItemPath
* @return bool Whether the class is included or not
Expand All @@ -123,6 +131,7 @@ private function includeClasses(array $classNames, $fileItemPath)
{
foreach ($classNames as $className) {
if (!class_exists($className)) {
// phpcs:ignore
require_once $fileItemPath;
return true;
}
Expand Down
61 changes: 41 additions & 20 deletions setup/src/Magento/Setup/Module/Di/Code/Reader/FileClassScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,24 @@

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

/**
* Class FileClassScanner
*
* @package Magento\Setup\Module\Di\Code\Reader
*/
class FileClassScanner
{
private const NAMESPACE_TOKENS = [
T_WHITESPACE => true,
T_STRING => true,
T_NS_SEPARATOR => true
];

private const ALLOWED_OPEN_BRACES_TOKENS = [
T_CURLY_OPEN => true,
T_DOLLAR_OPEN_CURLY_BRACES => true,
T_STRING_VARNAME => true];

/**
* The filename of the file to introspect
*
Expand All @@ -34,7 +50,9 @@ class FileClassScanner
*/
public function __construct($filename)
{
// phpcs:ignore
$filename = realpath($filename);
// phpcs:ignore
if (!file_exists($filename) || !\is_file($filename)) {
throw new InvalidFileException(
sprintf(
Expand All @@ -53,12 +71,14 @@ public function __construct($filename)
*/
public function getFileContents()
{
// phpcs:ignore
return file_get_contents($this->filename);
}

/**
* Extracts the fully qualified class name from a file. It only searches for the first match and stops looking
* as soon as it enters the class definition itself.
* Extracts the fully qualified class name from a file.
*
* It only searches for the first match and stops looking as soon as it enters the class definition itself.
*
* Warnings are suppressed for this method due to a micro-optimization that only really shows up when this logic
* is called several millions of times, which can happen quite easily with even moderately sized codebases.
Expand All @@ -69,35 +89,36 @@ public function getFileContents()
*/
private function extract()
{
$allowedOpenBraces = [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES, T_STRING_VARNAME];
$classes = [];
$namespace = '';
$namespaceParts = [];
$class = '';
$triggerClass = false;
$triggerNamespace = false;
$braceLevel = 0;
$bracedNamespace = false;

// phpcs:ignore
$this->tokens = token_get_all($this->getFileContents());
foreach ($this->tokens as $index => $token) {
$tokenIsArray = is_array($token);
// Is either a literal brace or an interpolated brace with a variable
if ($token == '{' || (is_array($token) && in_array($token[0], $allowedOpenBraces))) {
if ($token === '{' || ($tokenIsArray && isset(self::ALLOWED_OPEN_BRACES_TOKENS[$token[0]]))) {
$braceLevel++;
} else if ($token == '}') {
} elseif ($token === '}') {
$braceLevel--;
}
// The namespace keyword was found in the last loop
if ($triggerNamespace) {
// A string ; or a discovered namespace that looks like "namespace name { }"
if (!is_array($token) || ($namespace && $token[0] == T_WHITESPACE)) {
if (!$tokenIsArray || ($namespaceParts && $token[0] === T_WHITESPACE)) {
$triggerNamespace = false;
$namespace .= '\\';
$namespaceParts[] = '\\';
continue;
}
$namespace .= $token[1];
$namespaceParts[] = $token[1];

// The class keyword was found in the last loop
} else if ($triggerClass && $token[0] == T_STRING) {
// The class keyword was found in the last loop
} elseif ($triggerClass && $token[0] === T_STRING) {
$triggerClass = false;
$class = $token[1];
}
Expand All @@ -106,7 +127,7 @@ private function extract()
case T_NAMESPACE:
// Current loop contains the namespace keyword. Between this and the semicolon is the namespace
$triggerNamespace = true;
$namespace = '';
$namespaceParts = [];
$bracedNamespace = $this->isBracedNamespace($index);
break;
case T_CLASS:
Expand All @@ -118,9 +139,8 @@ private function extract()
}

// We have a class name, let's concatenate and store it!
if ($class != '') {
$namespace = trim($namespace);
$fqClassName = $namespace . trim($class);
if ($class !== '') {
$fqClassName = trim(join('', $namespaceParts)) . trim($class);
$classes[] = $fqClassName;
$class = '';
}
Expand All @@ -139,24 +159,25 @@ private function isBracedNamespace($index)
$len = count($this->tokens);
while ($index++ < $len) {
if (!is_array($this->tokens[$index])) {
if ($this->tokens[$index] == ';') {
if ($this->tokens[$index] === ';') {
return false;
} else if ($this->tokens[$index] == '{') {
} elseif ($this->tokens[$index] === '{') {
return true;
}
continue;
}

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

/**
* Retrieves the first class found in a class file. The return value is in an array format so it retains the
* same usage as the FileScanner.
* Retrieves the first class found in a class file.
*
* The return value is in an array format so it retains the same usage as the FileScanner.
*
* @return array
*/
Expand Down
55 changes: 32 additions & 23 deletions setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
use Magento\Setup\Module\Di\Compiler\Log\Log;
use \Magento\Framework\Reflection\TypeProcessor;

/**
* Class PhpScanner
*
* @package Magento\Setup\Module\Di\Code\Scanner
*/
class PhpScanner implements ScannerInterface
{
/**
Expand Down Expand Up @@ -167,48 +172,51 @@ protected function _fetchMissingExtensionAttributesClasses($reflectionClass, $fi
*
* @param array $files
* @return array
* @throws \ReflectionException
*/
public function collectEntities(array $files)
{
$output = [];
$output = [[]];
foreach ($files as $file) {
$classes = $this->_getDeclaredClasses($file);
foreach ($classes as $className) {
$reflectionClass = new \ReflectionClass($className);
$output = array_merge(
$output,
$this->_fetchFactories($reflectionClass, $file),
$this->_fetchMissingExtensionAttributesClasses($reflectionClass, $file)
);
$output [] = $this->_fetchFactories($reflectionClass, $file);
$output [] = $this->_fetchMissingExtensionAttributesClasses($reflectionClass, $file);
}
}
return array_unique($output);
return array_unique(array_merge(...$output));
}

/**
* @param $tokenIterator int
* @param $count int
* @param $tokens array
* Fetch namespaces from tokenized PHP file
*
* @param int $tokenIterator
* @param int $count
* @param array $tokens
* @return string
*/
protected function _fetchNamespace($tokenIterator, $count, $tokens)
{
$namespace = '';
$namespaceParts = [];
for ($tokenOffset = $tokenIterator + 1; $tokenOffset < $count; ++$tokenOffset) {
if ($tokens[$tokenOffset][0] === T_STRING) {
$namespace .= "\\" . $tokens[$tokenOffset][1];
$namespaceParts[] = "\\";
$namespaceParts[] = $tokens[$tokenOffset][1];
} elseif ($tokens[$tokenOffset] === '{' || $tokens[$tokenOffset] === ';') {
break;
}
}
return $namespace;
return join('', $namespaceParts);
}

/**
* @param $namespace string
* @param $tokenIterator int
* @param $count int
* @param $tokens array
* Fetch class names from tokenized PHP file
*
* @param string $namespace
* @param int $tokenIterator
* @param int $count
* @param array $tokens
* @return array
*/
protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens)
Expand All @@ -230,23 +238,24 @@ protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens)
*/
protected function _getDeclaredClasses($file)
{
$classes = [];
$namespace = '';
$classes = [[]];
$namespaceParts = [];
// phpcs:ignore
$tokens = token_get_all(file_get_contents($file));
$count = count($tokens);

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

if (($tokens[$tokenIterator][0] == T_CLASS || $tokens[$tokenIterator][0] == T_INTERFACE)
&& $tokens[$tokenIterator - 1][0] != T_DOUBLE_COLON
) {
$classes = array_merge($classes, $this->_fetchClasses($namespace, $tokenIterator, $count, $tokens));
$classes[] = $this->_fetchClasses(join('', $namespaceParts), $tokenIterator, $count, $tokens);
}
}
return array_unique($classes);
return array_unique(array_merge(...$classes));
}

/**
Expand All @@ -263,7 +272,7 @@ private function shouldGenerateClass($missingClassName, $entityType, $file)
if (class_exists($missingClassName)) {
return false;
}
} catch (\RuntimeException $e) {
} catch (\RuntimeException $e) { //phpcs:ignore
}
$sourceClassName = $this->getSourceClassName($missingClassName, $entityType);
if (!class_exists($sourceClassName) && !interface_exists($sourceClassName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,21 @@

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

/**
* Class BackslashTrim
*
* @package Magento\Setup\Module\Di\Compiler\Config\Chain
*/
class BackslashTrim implements ModificationInterface
{
/**
* Argument keys which require recursive resolving
*/
private const RECURSIVE_ARGUMENT_KEYS = [
'_i_' => true, // shared instance of a class or interface
'_ins_' => true // non-shared instance of a class or interface
];

/**
* Modifies input config
*
Expand Down Expand Up @@ -48,7 +61,6 @@ private function resolveInstancesNames(array $arguments)
* Resolves instances arguments
*
* @param array $argument
* @return array
*/
private function resolveArguments(&$argument)
{
Expand All @@ -57,15 +69,12 @@ private function resolveArguments(&$argument)
}

foreach ($argument as $key => &$value) {
if (in_array($key, ['_i_', '_ins_'], true)) {
if (isset(self::RECURSIVE_ARGUMENT_KEYS[$key])) {
$value = ltrim($value, '\\');
continue;
}

if (is_array($value)) {
$this->resolveArguments($value);
}
$this->resolveArguments($value);
}
return;
}
}
Loading