PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/zendframework/zend-di/src/ServiceLocator/Generator.php

https://gitlab.com/daigiangaitu91/magento
PHP | 342 lines | 208 code | 45 blank | 89 comment | 28 complexity | b7bd6effb98dd1e99bb0eafd1d632b5b MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Di\ServiceLocator;
  10. use Zend\Code\Generator\ClassGenerator;
  11. use Zend\Code\Generator\FileGenerator;
  12. use Zend\Code\Generator\MethodGenerator;
  13. use Zend\Code\Generator\ParameterGenerator;
  14. use Zend\Di\Di;
  15. use Zend\Di\Exception;
  16. /**
  17. * Generator that creates the body of a service locator that can emulate the logic of the given Zend\Di\Di instance
  18. * without class definitions
  19. */
  20. class Generator
  21. {
  22. protected $containerClass = 'ApplicationContext';
  23. /** @var DependencyInjectorProxy */
  24. protected $injector;
  25. /**
  26. * @var null|string
  27. */
  28. protected $namespace;
  29. /**
  30. * Constructor
  31. *
  32. * Requires a DependencyInjection manager on which to operate.
  33. *
  34. * @param Di $injector
  35. */
  36. public function __construct(Di $injector)
  37. {
  38. $this->injector = new DependencyInjectorProxy($injector);
  39. }
  40. /**
  41. * Set the class name for the generated service locator container
  42. *
  43. * @param string $name
  44. * @return Generator
  45. */
  46. public function setContainerClass($name)
  47. {
  48. $this->containerClass = $name;
  49. return $this;
  50. }
  51. /**
  52. * Set the namespace to use for the generated class file
  53. *
  54. * @param string $namespace
  55. * @return Generator
  56. */
  57. public function setNamespace($namespace)
  58. {
  59. $this->namespace = $namespace;
  60. return $this;
  61. }
  62. /**
  63. * Construct, configure, and return a PHP class file code generation object
  64. *
  65. * Creates a Zend\Code\Generator\FileGenerator object that has
  66. * created the specified class and service locator methods.
  67. *
  68. * @param null|string $filename
  69. * @throws \Zend\Di\Exception\RuntimeException
  70. * @return FileGenerator
  71. */
  72. public function getCodeGenerator($filename = null)
  73. {
  74. $injector = $this->injector;
  75. $im = $injector->instanceManager();
  76. $indent = ' ';
  77. $aliases = $this->reduceAliases($im->getAliases());
  78. $caseStatements = array();
  79. $getters = array();
  80. $definitions = $injector->definitions();
  81. $fetched = array_unique(array_merge($definitions->getClasses(), $im->getAliases()));
  82. foreach ($fetched as $name) {
  83. $getter = $this->normalizeAlias($name);
  84. $meta = $injector->get($name);
  85. $params = $meta->getParams();
  86. // Build parameter list for instantiation
  87. foreach ($params as $key => $param) {
  88. if (null === $param || is_scalar($param) || is_array($param)) {
  89. $string = var_export($param, 1);
  90. if (strstr($string, '::__set_state(')) {
  91. throw new Exception\RuntimeException('Arguments in definitions may not contain objects');
  92. }
  93. $params[$key] = $string;
  94. } elseif ($param instanceof GeneratorInstance) {
  95. /* @var $param GeneratorInstance */
  96. $params[$key] = sprintf('$this->%s()', $this->normalizeAlias($param->getName()));
  97. } else {
  98. $message = sprintf('Unable to use object arguments when building containers. Encountered with "%s", parameter of type "%s"', $name, get_class($param));
  99. throw new Exception\RuntimeException($message);
  100. }
  101. }
  102. // Strip null arguments from the end of the params list
  103. $reverseParams = array_reverse($params, true);
  104. foreach ($reverseParams as $key => $param) {
  105. if ('NULL' === $param) {
  106. unset($params[$key]);
  107. continue;
  108. }
  109. break;
  110. }
  111. // Create instantiation code
  112. $constructor = $meta->getConstructor();
  113. if ('__construct' != $constructor) {
  114. // Constructor callback
  115. $callback = var_export($constructor, 1);
  116. if (strstr($callback, '::__set_state(')) {
  117. throw new Exception\RuntimeException('Unable to build containers that use callbacks requiring object instances');
  118. }
  119. if (count($params)) {
  120. $creation = sprintf('$object = call_user_func(%s, %s);', $callback, implode(', ', $params));
  121. } else {
  122. $creation = sprintf('$object = call_user_func(%s);', $callback);
  123. }
  124. } else {
  125. // Normal instantiation
  126. $className = '\\' . ltrim($name, '\\');
  127. $creation = sprintf('$object = new %s(%s);', $className, implode(', ', $params));
  128. }
  129. // Create method call code
  130. $methods = '';
  131. foreach ($meta->getMethods() as $methodData) {
  132. if (!isset($methodData['name']) && !isset($methodData['method'])) {
  133. continue;
  134. }
  135. $methodName = isset($methodData['name']) ? $methodData['name'] : $methodData['method'];
  136. $methodParams = $methodData['params'];
  137. // Create method parameter representation
  138. foreach ($methodParams as $key => $param) {
  139. if (null === $param || is_scalar($param) || is_array($param)) {
  140. $string = var_export($param, 1);
  141. if (strstr($string, '::__set_state(')) {
  142. throw new Exception\RuntimeException('Arguments in definitions may not contain objects');
  143. }
  144. $methodParams[$key] = $string;
  145. } elseif ($param instanceof GeneratorInstance) {
  146. $methodParams[$key] = sprintf('$this->%s()', $this->normalizeAlias($param->getName()));
  147. } else {
  148. $message = sprintf('Unable to use object arguments when generating method calls. Encountered with class "%s", method "%s", parameter of type "%s"', $name, $methodName, get_class($param));
  149. throw new Exception\RuntimeException($message);
  150. }
  151. }
  152. // Strip null arguments from the end of the params list
  153. $reverseParams = array_reverse($methodParams, true);
  154. foreach ($reverseParams as $key => $param) {
  155. if ('NULL' === $param) {
  156. unset($methodParams[$key]);
  157. continue;
  158. }
  159. break;
  160. }
  161. $methods .= sprintf("\$object->%s(%s);\n", $methodName, implode(', ', $methodParams));
  162. }
  163. // Generate caching statement
  164. $storage = '';
  165. if ($im->hasSharedInstance($name, $params)) {
  166. $storage = sprintf("\$this->services['%s'] = \$object;\n", $name);
  167. }
  168. // Start creating getter
  169. $getterBody = '';
  170. // Create fetch of stored service
  171. if ($im->hasSharedInstance($name, $params)) {
  172. $getterBody .= sprintf("if (isset(\$this->services['%s'])) {\n", $name);
  173. $getterBody .= sprintf("%sreturn \$this->services['%s'];\n}\n\n", $indent, $name);
  174. }
  175. // Creation and method calls
  176. $getterBody .= sprintf("%s\n", $creation);
  177. $getterBody .= $methods;
  178. // Stored service
  179. $getterBody .= $storage;
  180. // End getter body
  181. $getterBody .= "return \$object;\n";
  182. $getterDef = new MethodGenerator();
  183. $getterDef->setName($getter);
  184. $getterDef->setBody($getterBody);
  185. $getters[] = $getterDef;
  186. // Get cases for case statements
  187. $cases = array($name);
  188. if (isset($aliases[$name])) {
  189. $cases = array_merge($aliases[$name], $cases);
  190. }
  191. // Build case statement and store
  192. $statement = '';
  193. foreach ($cases as $value) {
  194. $statement .= sprintf("%scase '%s':\n", $indent, $value);
  195. }
  196. $statement .= sprintf("%sreturn \$this->%s();\n", str_repeat($indent, 2), $getter);
  197. $caseStatements[] = $statement;
  198. }
  199. // Build switch statement
  200. $switch = sprintf("switch (%s) {\n%s\n", '$name', implode("\n", $caseStatements));
  201. $switch .= sprintf("%sdefault:\n%sreturn parent::get(%s, %s);\n", $indent, str_repeat($indent, 2), '$name', '$params');
  202. $switch .= "}\n\n";
  203. // Build get() method
  204. $nameParam = new ParameterGenerator();
  205. $nameParam->setName('name');
  206. $paramsParam = new ParameterGenerator();
  207. $paramsParam->setName('params')
  208. ->setType('array')
  209. ->setDefaultValue(array());
  210. $get = new MethodGenerator();
  211. $get->setName('get');
  212. $get->setParameters(array(
  213. $nameParam,
  214. $paramsParam,
  215. ));
  216. $get->setBody($switch);
  217. // Create getters for aliases
  218. $aliasMethods = array();
  219. foreach ($aliases as $class => $classAliases) {
  220. foreach ($classAliases as $alias) {
  221. $aliasMethods[] = $this->getCodeGenMethodFromAlias($alias, $class);
  222. }
  223. }
  224. // Create class code generation object
  225. $container = new ClassGenerator();
  226. $container->setName($this->containerClass)
  227. ->setExtendedClass('ServiceLocator')
  228. ->addMethodFromGenerator($get)
  229. ->addMethods($getters)
  230. ->addMethods($aliasMethods);
  231. // Create PHP file code generation object
  232. $classFile = new FileGenerator();
  233. $classFile->setUse('Zend\Di\ServiceLocator')
  234. ->setClass($container);
  235. if (null !== $this->namespace) {
  236. $classFile->setNamespace($this->namespace);
  237. }
  238. if (null !== $filename) {
  239. $classFile->setFilename($filename);
  240. }
  241. return $classFile;
  242. }
  243. /**
  244. * Reduces aliases
  245. *
  246. * Takes alias list and reduces it to a 2-dimensional array of
  247. * class names pointing to an array of aliases that resolve to
  248. * it.
  249. *
  250. * @param array $aliasList
  251. * @return array
  252. */
  253. protected function reduceAliases(array $aliasList)
  254. {
  255. $reduced = array();
  256. $aliases = array_keys($aliasList);
  257. foreach ($aliasList as $alias => $service) {
  258. if (in_array($service, $aliases)) {
  259. do {
  260. $service = $aliasList[$service];
  261. } while (in_array($service, $aliases));
  262. }
  263. if (!isset($reduced[$service])) {
  264. $reduced[$service] = array();
  265. }
  266. $reduced[$service][] = $alias;
  267. }
  268. return $reduced;
  269. }
  270. /**
  271. * Create a PhpMethod code generation object named after a given alias
  272. *
  273. * @param string $alias
  274. * @param string $class Class to which alias refers
  275. * @return MethodGenerator
  276. */
  277. protected function getCodeGenMethodFromAlias($alias, $class)
  278. {
  279. $alias = $this->normalizeAlias($alias);
  280. $method = new MethodGenerator();
  281. $method->setName($alias);
  282. $method->setBody(sprintf('return $this->get(\'%s\');', $class));
  283. return $method;
  284. }
  285. /**
  286. * Normalize an alias to a getter method name
  287. *
  288. * @param string $alias
  289. * @return string
  290. */
  291. protected function normalizeAlias($alias)
  292. {
  293. $normalized = preg_replace('/[^a-zA-Z0-9]/', ' ', $alias);
  294. $normalized = 'get' . str_replace(' ', '', ucwords($normalized));
  295. return $normalized;
  296. }
  297. }