PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://gitlab.com/axeltizon/magento-demopoweraccess
PHP | 247 lines | 184 code | 11 blank | 52 comment | 27 complexity | 30e0cab89339c63197a10cb97214a31c MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Setup\Module\Di\Code\Scanner;
  7. use Magento\Setup\Module\Di\Compiler\Log\Log;
  8. use Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator;
  9. use Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator;
  10. use Magento\Framework\ObjectManager\Code\Generator\Factory as FactoryGenerator;
  11. class PhpScanner implements ScannerInterface
  12. {
  13. /**
  14. * @var Log $log
  15. */
  16. protected $_log;
  17. /**
  18. * @param Log $log
  19. */
  20. public function __construct(Log $log)
  21. {
  22. $this->_log = $log;
  23. }
  24. /**
  25. * Find classes which are used as parameters types of the specified method and are not declared.
  26. *
  27. * @param string $file
  28. * @param \ReflectionClass $classReflection
  29. * @param string $methodName
  30. * @param string $entityType
  31. * @return string[]
  32. */
  33. protected function _findMissingClasses($file, $classReflection, $methodName, $entityType)
  34. {
  35. $missingClasses = [];
  36. if ($classReflection->hasMethod($methodName)) {
  37. $constructor = $classReflection->getMethod($methodName);
  38. $parameters = $constructor->getParameters();
  39. /** @var $parameter \ReflectionParameter */
  40. foreach ($parameters as $parameter) {
  41. preg_match('/\[\s\<\w+?>\s([\w\\\\]+)/s', $parameter->__toString(), $matches);
  42. if (isset($matches[1]) && substr($matches[1], -strlen($entityType)) == $entityType) {
  43. $missingClassName = $matches[1];
  44. try {
  45. if (class_exists($missingClassName)) {
  46. continue;
  47. }
  48. } catch (\RuntimeException $e) {
  49. }
  50. $sourceClassName = $this->getSourceClassName($missingClassName, $entityType);
  51. if (!class_exists($sourceClassName) && !interface_exists($sourceClassName)) {
  52. $this->_log->add(
  53. Log::CONFIGURATION_ERROR,
  54. $missingClassName,
  55. "Invalid {$entityType} for nonexistent class {$sourceClassName} in file {$file}"
  56. );
  57. continue;
  58. }
  59. $missingClasses[] = $missingClassName;
  60. }
  61. }
  62. }
  63. return $missingClasses;
  64. }
  65. /**
  66. * Identify source class name for the provided class.
  67. *
  68. * @param string $missingClassName
  69. * @param string $entityType
  70. * @return string
  71. */
  72. protected function getSourceClassName($missingClassName, $entityType)
  73. {
  74. $sourceClassName = rtrim(substr($missingClassName, 0, -strlen($entityType)), '\\');
  75. $entityType = lcfirst($entityType);
  76. if ($entityType == ExtensionAttributesInterfaceGenerator::ENTITY_TYPE
  77. || $entityType == ExtensionAttributesGenerator::ENTITY_TYPE
  78. ) {
  79. /** Process special cases for extension class and extension interface */
  80. return $sourceClassName . 'Interface';
  81. } else if ($entityType == FactoryGenerator::ENTITY_TYPE) {
  82. $extensionAttributesSuffix = ucfirst(ExtensionAttributesGenerator::ENTITY_TYPE);
  83. if (substr($sourceClassName, -strlen($extensionAttributesSuffix)) == $extensionAttributesSuffix) {
  84. /** Process special case for extension factories */
  85. $extensionAttributesClass = substr(
  86. $sourceClassName,
  87. 0,
  88. -strlen(ExtensionAttributesGenerator::ENTITY_TYPE)
  89. );
  90. $sourceClassName = $extensionAttributesClass . 'Interface';
  91. }
  92. }
  93. return $sourceClassName;
  94. }
  95. /**
  96. * Fetch factories from class constructor
  97. *
  98. * @param \ReflectionClass $reflectionClass
  99. * @param string $file
  100. * @return string[]
  101. */
  102. protected function _fetchFactories($reflectionClass, $file)
  103. {
  104. $factorySuffix = '\\'.ucfirst(FactoryGenerator::ENTITY_TYPE);
  105. $absentFactories = $this->_findMissingClasses(
  106. $file,
  107. $reflectionClass,
  108. '__construct',
  109. ucfirst(FactoryGenerator::ENTITY_TYPE)
  110. );
  111. foreach ($absentFactories as $key => $absentFactory) {
  112. if (substr($absentFactory, -strlen($factorySuffix)) == $factorySuffix) {
  113. $entityName = rtrim(substr($absentFactory, 0, -strlen($factorySuffix)), '\\');
  114. $this->_log->add(
  115. Log::CONFIGURATION_ERROR,
  116. $absentFactory,
  117. 'Invalid Factory declaration for class ' . $entityName . ' in file ' . $file
  118. );
  119. unset($absentFactories[$key]);
  120. }
  121. }
  122. return $absentFactories;
  123. }
  124. /**
  125. * Find missing extension attributes related classes, interfaces and factories.
  126. *
  127. * @param \ReflectionClass $reflectionClass
  128. * @param string $file
  129. * @return string[]
  130. */
  131. protected function _fetchMissingExtensionAttributesClasses($reflectionClass, $file)
  132. {
  133. $missingExtensionInterfaces = $this->_findMissingClasses(
  134. $file,
  135. $reflectionClass,
  136. 'setExtensionAttributes',
  137. ucfirst(\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::ENTITY_TYPE)
  138. );
  139. $missingExtensionClasses = [];
  140. $missingExtensionFactories = [];
  141. foreach ($missingExtensionInterfaces as $missingExtensionInterface) {
  142. $extension = rtrim(substr($missingExtensionInterface, 0, -strlen('Interface')), '\\');
  143. if (!class_exists($extension)) {
  144. $missingExtensionClasses[] = $extension;
  145. }
  146. $extensionFactory = $extension . 'Factory';
  147. if (!class_exists($extensionFactory)) {
  148. $missingExtensionFactories[] = $extensionFactory;
  149. }
  150. }
  151. return array_merge($missingExtensionInterfaces, $missingExtensionClasses, $missingExtensionFactories);
  152. }
  153. /**
  154. * Get array of class names
  155. *
  156. * @param array $files
  157. * @return array
  158. */
  159. public function collectEntities(array $files)
  160. {
  161. $output = [];
  162. foreach ($files as $file) {
  163. $classes = $this->_getDeclaredClasses($file);
  164. foreach ($classes as $className) {
  165. $reflectionClass = new \ReflectionClass($className);
  166. $output = array_merge(
  167. $output,
  168. $this->_fetchFactories($reflectionClass, $file),
  169. $this->_fetchMissingExtensionAttributesClasses($reflectionClass, $file)
  170. );
  171. }
  172. }
  173. return array_unique($output);
  174. }
  175. /**
  176. * @param $tokenIterator int
  177. * @param $count int
  178. * @param $tokens array
  179. * @return string
  180. */
  181. protected function _fetchNamespace($tokenIterator, $count, $tokens)
  182. {
  183. $namespace = '';
  184. for ($tokenOffset = $tokenIterator + 1; $tokenOffset < $count; ++$tokenOffset) {
  185. if ($tokens[$tokenOffset][0] === T_STRING) {
  186. $namespace .= "\\" . $tokens[$tokenOffset][1];
  187. } elseif ($tokens[$tokenOffset] === '{' || $tokens[$tokenOffset] === ';') {
  188. break;
  189. }
  190. }
  191. return $namespace;
  192. }
  193. /**
  194. * @param $namespace string
  195. * @param $tokenIterator int
  196. * @param $count int
  197. * @param $tokens array
  198. * @return array
  199. */
  200. protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens)
  201. {
  202. $classes = [];
  203. for ($tokenOffset = $tokenIterator + 1; $tokenOffset < $count; ++$tokenOffset) {
  204. if ($tokens[$tokenOffset] === '{') {
  205. $classes[] = $namespace . "\\" . $tokens[$tokenIterator + 2][1];
  206. }
  207. }
  208. return $classes;
  209. }
  210. /**
  211. * Get classes and interfaces declared in the file
  212. *
  213. * @param string $file
  214. * @return array
  215. */
  216. protected function _getDeclaredClasses($file)
  217. {
  218. $classes = [];
  219. $namespace = '';
  220. $tokens = token_get_all(file_get_contents($file));
  221. $count = count($tokens);
  222. for ($tokenIterator = 0; $tokenIterator < $count; $tokenIterator++) {
  223. if ($tokens[$tokenIterator][0] == T_NAMESPACE) {
  224. $namespace .= $this->_fetchNamespace($tokenIterator, $count, $tokens);
  225. }
  226. if (($tokens[$tokenIterator][0] == T_CLASS || $tokens[$tokenIterator][0] == T_INTERFACE)
  227. && $tokens[$tokenIterator - 1][0] != T_DOUBLE_COLON
  228. ) {
  229. $classes = array_merge($classes, $this->_fetchClasses($namespace, $tokenIterator, $count, $tokens));
  230. }
  231. }
  232. return array_unique($classes);
  233. }
  234. }