Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
PhpScanner.php
Go to the documentation of this file.
1 <?php
7 
12 use \Magento\Framework\Reflection\TypeProcessor;
13 
14 class PhpScanner implements ScannerInterface
15 {
19  protected $_log;
20 
24  private $typeProcessor;
25 
32  public function __construct(Log $log, TypeProcessor $typeProcessor = null)
33  {
34  $this->_log = $log;
35  $this->typeProcessor = $typeProcessor
36  ?: \Magento\Framework\App\ObjectManager::getInstance()->get(TypeProcessor::class);
37  }
38 
48  protected function _findMissingClasses($file, $classReflection, $methodName, $entityType)
49  {
50  $missingClasses = [];
51  if ($classReflection->hasMethod($methodName)) {
52  $constructor = $classReflection->getMethod($methodName);
53  $parameters = $constructor->getParameters();
55  foreach ($parameters as $parameter) {
56  preg_match('/\[\s<\w+?>\s([\w\\\\]+)/s', $parameter->__toString(), $matches);
57  if (isset($matches[1]) && substr($matches[1], -strlen($entityType)) == $entityType) {
58  $missingClassName = $matches[1];
59  if ($this->shouldGenerateClass($missingClassName, $entityType, $file)) {
60  $missingClasses[] = $missingClassName;
61  }
62  }
63  }
64  }
65  return $missingClasses;
66  }
67 
75  protected function getSourceClassName($missingClassName, $entityType)
76  {
77  $sourceClassName = rtrim(substr($missingClassName, 0, -strlen($entityType)), '\\');
78  $entityType = lcfirst($entityType);
79  if ($entityType == ExtensionAttributesInterfaceGenerator::ENTITY_TYPE
80  || $entityType == ExtensionAttributesGenerator::ENTITY_TYPE
81  ) {
83  return $sourceClassName . 'Interface';
84  } elseif ($entityType == FactoryGenerator::ENTITY_TYPE) {
85  $extensionAttributesSuffix = ucfirst(ExtensionAttributesGenerator::ENTITY_TYPE);
86  if (substr($sourceClassName, -strlen($extensionAttributesSuffix)) == $extensionAttributesSuffix) {
88  $extensionAttributesClass = substr(
89  $sourceClassName,
90  0,
91  -strlen(ExtensionAttributesGenerator::ENTITY_TYPE)
92  );
93  $sourceClassName = $extensionAttributesClass . 'Interface';
94  }
95  }
96  return $sourceClassName;
97  }
98 
106  protected function _fetchFactories($reflectionClass, $file)
107  {
108  $factorySuffix = '\\' . ucfirst(FactoryGenerator::ENTITY_TYPE);
109  $absentFactories = $this->_findMissingClasses(
110  $file,
112  '__construct',
113  ucfirst(FactoryGenerator::ENTITY_TYPE)
114  );
115  foreach ($absentFactories as $key => $absentFactory) {
116  if (substr($absentFactory, -strlen($factorySuffix)) == $factorySuffix) {
117  $entityName = rtrim(substr($absentFactory, 0, -strlen($factorySuffix)), '\\');
118  $this->_log->add(
119  Log::CONFIGURATION_ERROR,
120  $absentFactory,
121  'Invalid Factory declaration for class ' . $entityName . ' in file ' . $file
122  );
123  unset($absentFactories[$key]);
124  }
125  }
126  return $absentFactories;
127  }
128 
137  {
138  $missingExtensionInterfaces = [];
139  $methodName = 'getExtensionAttributes';
140  $entityType = ucfirst(\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::ENTITY_TYPE);
141  if ($reflectionClass->hasMethod($methodName) && $reflectionClass->isInterface()) {
142  $returnType = $this->typeProcessor->getGetterReturnType(
143  (new \Zend\Code\Reflection\ClassReflection($reflectionClass->getName()))->getMethod($methodName)
144  );
145  $missingClassName = $returnType['type'];
146  if ($this->shouldGenerateClass($missingClassName, $entityType, $file)) {
147  $missingExtensionInterfaces[] = $missingClassName;
148  }
149  }
150  $missingExtensionClasses = [];
151  $missingExtensionFactories = [];
152  foreach ($missingExtensionInterfaces as $missingExtensionInterface) {
153  $extension = rtrim(substr($missingExtensionInterface, 0, -strlen('Interface')), '\\');
154  if (!class_exists($extension)) {
155  $missingExtensionClasses[] = $extension;
156  }
157  $extensionFactory = $extension . 'Factory';
158  if (!class_exists($extensionFactory)) {
159  $missingExtensionFactories[] = $extensionFactory;
160  }
161  }
162  return array_merge($missingExtensionInterfaces, $missingExtensionClasses, $missingExtensionFactories);
163  }
164 
171  public function collectEntities(array $files)
172  {
173  $output = [];
174  foreach ($files as $file) {
175  $classes = $this->_getDeclaredClasses($file);
176  foreach ($classes as $className) {
177  $reflectionClass = new \ReflectionClass($className);
178  $output = array_merge(
179  $output,
180  $this->_fetchFactories($reflectionClass, $file),
182  );
183  }
184  }
185  return array_unique($output);
186  }
187 
194  protected function _fetchNamespace($tokenIterator, $count, $tokens)
195  {
196  $namespace = '';
197  for ($tokenOffset = $tokenIterator + 1; $tokenOffset < $count; ++$tokenOffset) {
198  if ($tokens[$tokenOffset][0] === T_STRING) {
199  $namespace .= "\\" . $tokens[$tokenOffset][1];
200  } elseif ($tokens[$tokenOffset] === '{' || $tokens[$tokenOffset] === ';') {
201  break;
202  }
203  }
204  return $namespace;
205  }
206 
214  protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens)
215  {
216  $classes = [];
217  for ($tokenOffset = $tokenIterator + 1; $tokenOffset < $count; ++$tokenOffset) {
218  if ($tokens[$tokenOffset] === '{') {
219  $classes[] = $namespace . "\\" . $tokens[$tokenIterator + 2][1];
220  }
221  }
222  return $classes;
223  }
224 
231  protected function _getDeclaredClasses($file)
232  {
233  $classes = [];
234  $namespace = '';
235  $tokens = token_get_all(file_get_contents($file));
236  $count = count($tokens);
237 
238  for ($tokenIterator = 0; $tokenIterator < $count; $tokenIterator++) {
239  if ($tokens[$tokenIterator][0] == T_NAMESPACE) {
240  $namespace .= $this->_fetchNamespace($tokenIterator, $count, $tokens);
241  }
242 
243  if (($tokens[$tokenIterator][0] == T_CLASS || $tokens[$tokenIterator][0] == T_INTERFACE)
244  && $tokens[$tokenIterator - 1][0] != T_DOUBLE_COLON
245  ) {
246  $classes = array_merge($classes, $this->_fetchClasses($namespace, $tokenIterator, $count, $tokens));
247  }
248  }
249  return array_unique($classes);
250  }
251 
260  private function shouldGenerateClass($missingClassName, $entityType, $file)
261  {
262  try {
263  if (class_exists($missingClassName)) {
264  return false;
265  }
266  } catch (\RuntimeException $e) {
267  }
268  $sourceClassName = $this->getSourceClassName($missingClassName, $entityType);
269  if (!class_exists($sourceClassName) && !interface_exists($sourceClassName)) {
270  $this->_log->add(
271  Log::CONFIGURATION_ERROR,
272  $missingClassName,
273  "Invalid {$entityType} for nonexistent class {$sourceClassName} in file {$file}"
274  );
275  return false;
276  }
277  return true;
278  }
279 }
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
_fetchClasses($namespace, $tokenIterator, $count, $tokens)
Definition: PhpScanner.php:214
$count
Definition: recent.phtml:13
getSourceClassName($missingClassName, $entityType)
Definition: PhpScanner.php:75
_fetchNamespace($tokenIterator, $count, $tokens)
Definition: PhpScanner.php:194
$reflectionClass
Definition: categories.php:25
__construct(Log $log, TypeProcessor $typeProcessor=null)
Definition: PhpScanner.php:32
foreach($appDirs as $dir) $files
_fetchMissingExtensionAttributesClasses($reflectionClass, $file)
Definition: PhpScanner.php:136
$tokens
Definition: cards_list.phtml:9
if($currentSelectedMethod==$_code) $className
Definition: form.phtml:31