PageRenderTime 26ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

https://github.com/thewiredman/symfony
PHP | 442 lines | 403 code | 14 blank | 25 comment | 6 complexity | 936c519def5a189f6d11c5afd004279e MD5 | raw file
  1. <?php
  2. namespace Symfony\Component\DependencyInjection\Dumper;
  3. use Symfony\Component\DependencyInjection\ContainerBuilder;
  4. use Symfony\Component\DependencyInjection\Container;
  5. use Symfony\Component\DependencyInjection\ContainerInterface;
  6. use Symfony\Component\DependencyInjection\Reference;
  7. use Symfony\Component\DependencyInjection\Parameter;
  8. /*
  9. * This file is part of the Symfony framework.
  10. *
  11. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  12. *
  13. * This source file is subject to the MIT license that is bundled
  14. * with this source code in the file LICENSE.
  15. */
  16. /**
  17. * PhpDumper dumps a service container as a PHP class.
  18. *
  19. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  20. */
  21. class PhpDumper extends Dumper
  22. {
  23. /**
  24. * Dumps the service container as a PHP class.
  25. *
  26. * Available options:
  27. *
  28. * * class: The class name
  29. * * base_class: The base class name
  30. *
  31. * @param array $options An array of options
  32. *
  33. * @return string A PHP class representing of the service container
  34. */
  35. public function dump(array $options = array())
  36. {
  37. $options = array_merge(array(
  38. 'class' => 'ProjectServiceContainer',
  39. 'base_class' => 'Container',
  40. ), $options);
  41. return
  42. $this->startClass($options['class'], $options['base_class']).
  43. $this->addConstructor().
  44. $this->addServices().
  45. $this->addTags().
  46. $this->addDefaultParametersMethod().
  47. $this->endClass()
  48. ;
  49. }
  50. protected function addServiceInclude($id, $definition)
  51. {
  52. if (null !== $definition->getFile()) {
  53. return sprintf(" require_once %s;\n\n", $this->dumpValue($definition->getFile()));
  54. }
  55. }
  56. protected function addServiceShared($id, $definition)
  57. {
  58. if ($definition->isShared()) {
  59. return <<<EOF
  60. if (isset(\$this->shared['$id'])) return \$this->shared['$id'];
  61. EOF;
  62. }
  63. }
  64. protected function addServiceReturn($id, $definition)
  65. {
  66. return <<<EOF
  67. return \$instance;
  68. }
  69. EOF;
  70. }
  71. protected function addServiceInstance($id, $definition)
  72. {
  73. $class = $this->dumpValue($definition->getClass());
  74. if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
  75. throw new \InvalidArgumentException(sprintf('"%s" is not a valid class name.', $class));
  76. }
  77. $arguments = array();
  78. foreach ($definition->getArguments() as $value) {
  79. $arguments[] = $this->dumpValue($value);
  80. }
  81. if (null !== $definition->getFactoryMethod()) {
  82. if (null !== $definition->getFactoryService()) {
  83. $code = sprintf(" \$instance = %s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments));
  84. } else {
  85. $code = sprintf(" \$instance = call_user_func(array(%s, '%s')%s);\n", $class, $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
  86. }
  87. } elseif ($class != "'".str_replace('\\', '\\\\', $definition->getClass())."'") {
  88. $code = sprintf(" \$class = %s;\n \$instance = new \$class(%s);\n", $class, implode(', ', $arguments));
  89. } else {
  90. $code = sprintf(" \$instance = new %s(%s);\n", $definition->getClass(), implode(', ', $arguments));
  91. }
  92. if ($definition->isShared()) {
  93. $code .= sprintf(" \$this->shared['$id'] = \$instance;\n");
  94. }
  95. return $code;
  96. }
  97. protected function addServiceMethodCalls($id, $definition)
  98. {
  99. $calls = '';
  100. foreach ($definition->getMethodCalls() as $call) {
  101. $arguments = array();
  102. foreach ($call[1] as $value) {
  103. $arguments[] = $this->dumpValue($value);
  104. }
  105. $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$instance->%s(%s);\n", $call[0], implode(', ', $arguments)));
  106. }
  107. return $calls;
  108. }
  109. protected function addServiceConfigurator($id, $definition)
  110. {
  111. if (!$callable = $definition->getConfigurator()) {
  112. return '';
  113. }
  114. if (is_array($callable)) {
  115. if (is_object($callable[0]) && $callable[0] instanceof Reference) {
  116. return sprintf(" %s->%s(\$instance);\n", $this->getServiceCall((string) $callable[0]), $callable[1]);
  117. } else {
  118. return sprintf(" call_user_func(array(%s, '%s'), \$instance);\n", $this->dumpValue($callable[0]), $callable[1]);
  119. }
  120. } else {
  121. return sprintf(" %s(\$instance);\n", $callable);
  122. }
  123. }
  124. protected function addService($id, $definition)
  125. {
  126. $name = Container::camelize($id);
  127. $return = '';
  128. if ($class = $definition->getClass()) {
  129. $return = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'Object' : $class, $class);
  130. } elseif ($definition->getFactoryService()) {
  131. $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod());
  132. }
  133. $doc = '';
  134. if ($definition->isShared()) {
  135. $doc = <<<EOF
  136. *
  137. * This service is shared.
  138. * This method always returns the same instance of the service.
  139. EOF;
  140. }
  141. $code = <<<EOF
  142. /**
  143. * Gets the '$id' service.$doc
  144. *
  145. * $return
  146. */
  147. protected function get{$name}Service()
  148. {
  149. EOF;
  150. $code .=
  151. $this->addServiceInclude($id, $definition).
  152. $this->addServiceShared($id, $definition).
  153. $this->addServiceInstance($id, $definition).
  154. $this->addServiceMethodCalls($id, $definition).
  155. $this->addServiceConfigurator($id, $definition).
  156. $this->addServiceReturn($id, $definition)
  157. ;
  158. return $code;
  159. }
  160. protected function addServiceAlias($alias, $id)
  161. {
  162. $name = Container::camelize($alias);
  163. $type = 'Object';
  164. if ($this->container->hasDefinition($id)) {
  165. $class = $this->container->getDefinition($id)->getClass();
  166. $type = 0 === strpos($class, '%') ? 'Object' : $class;
  167. }
  168. return <<<EOF
  169. /**
  170. * Gets the $alias service alias.
  171. *
  172. * @return $type An instance of the $id service
  173. */
  174. protected function get{$name}Service()
  175. {
  176. return {$this->getServiceCall($id)};
  177. }
  178. EOF;
  179. }
  180. protected function addServices()
  181. {
  182. $code = '';
  183. foreach ($this->container->getDefinitions() as $id => $definition) {
  184. $code .= $this->addService($id, $definition);
  185. }
  186. foreach ($this->container->getAliases() as $alias => $id) {
  187. $code .= $this->addServiceAlias($alias, $id);
  188. }
  189. return $code;
  190. }
  191. protected function addTags()
  192. {
  193. $tags = array();
  194. foreach ($this->container->getDefinitions() as $id => $definition) {
  195. foreach ($definition->getTags() as $name => $ann) {
  196. if (!isset($tags[$name])) {
  197. $tags[$name] = array();
  198. }
  199. $tags[$name][$id] = $ann;
  200. }
  201. }
  202. $tags = var_export($tags, true);
  203. return <<<EOF
  204. /**
  205. * Returns service ids for a given tag.
  206. *
  207. * @param string \$name The tag name
  208. *
  209. * @return array An array of tags
  210. */
  211. public function findTaggedServiceIds(\$name)
  212. {
  213. static \$tags = $tags;
  214. return isset(\$tags[\$name]) ? \$tags[\$name] : array();
  215. }
  216. EOF;
  217. }
  218. protected function startClass($class, $baseClass)
  219. {
  220. $bagClass = $this->container->isFrozen() ? 'FrozenParameterBag' : 'ParameterBag';
  221. return <<<EOF
  222. <?php
  223. use Symfony\Component\DependencyInjection\ContainerInterface;
  224. use Symfony\Component\DependencyInjection\Container;
  225. use Symfony\Component\DependencyInjection\Reference;
  226. use Symfony\Component\DependencyInjection\Parameter;
  227. use Symfony\Component\DependencyInjection\ParameterBag\\$bagClass;
  228. /**
  229. * $class
  230. *
  231. * This class has been auto-generated
  232. * by the Symfony Dependency Injection Component.
  233. */
  234. class $class extends $baseClass
  235. {
  236. protected \$shared = array();
  237. EOF;
  238. }
  239. protected function addConstructor()
  240. {
  241. $bagClass = $this->container->isFrozen() ? 'FrozenParameterBag' : 'ParameterBag';
  242. return <<<EOF
  243. /**
  244. * Constructor.
  245. */
  246. public function __construct()
  247. {
  248. parent::__construct(new $bagClass(\$this->getDefaultParameters()));
  249. }
  250. EOF;
  251. }
  252. protected function addDefaultParametersMethod()
  253. {
  254. if (!$this->container->getParameterBag()->all()) {
  255. return '';
  256. }
  257. $parameters = $this->exportParameters($this->container->getParameterBag()->all());
  258. return <<<EOF
  259. /**
  260. * Gets the default parameters.
  261. *
  262. * @return array An array of the default parameters
  263. */
  264. protected function getDefaultParameters()
  265. {
  266. return $parameters;
  267. }
  268. EOF;
  269. }
  270. protected function exportParameters($parameters, $indent = 12)
  271. {
  272. $php = array();
  273. foreach ($parameters as $key => $value) {
  274. if (is_array($value)) {
  275. $value = $this->exportParameters($value, $indent + 4);
  276. } elseif ($value instanceof Reference) {
  277. throw new \InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service %s found).', $value));
  278. } else {
  279. $value = var_export($value, true);
  280. }
  281. $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
  282. }
  283. return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
  284. }
  285. protected function endClass()
  286. {
  287. return <<<EOF
  288. }
  289. EOF;
  290. }
  291. protected function wrapServiceConditionals($value, $code)
  292. {
  293. if (!$services = ContainerBuilder::getServiceConditionals($value)) {
  294. return $code;
  295. }
  296. $conditions = array();
  297. foreach ($services as $service) {
  298. $conditions[] = sprintf("\$this->has('%s')", $service);
  299. }
  300. // re-indent the wrapped code
  301. $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
  302. return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
  303. }
  304. protected function dumpValue($value, $interpolate = true)
  305. {
  306. if (is_array($value)) {
  307. $code = array();
  308. foreach ($value as $k => $v) {
  309. $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
  310. }
  311. return sprintf('array(%s)', implode(', ', $code));
  312. } elseif (is_object($value) && $value instanceof Reference) {
  313. return $this->getServiceCall((string) $value, $value);
  314. } elseif (is_object($value) && $value instanceof Parameter) {
  315. return $this->dumpParameter($value);
  316. } elseif (true === $interpolate && is_string($value)) {
  317. if (preg_match('/^%([^%]+)%$/', $value, $match)) {
  318. // we do this to deal with non string values (boolean, integer, ...)
  319. // the preg_replace_callback converts them to strings
  320. return $this->dumpParameter(strtolower($match[1]));
  321. } else {
  322. $that = $this;
  323. $replaceParameters = function ($match) use ($that)
  324. {
  325. return sprintf("'.".$that->dumpParameter(strtolower($match[2])).".'");
  326. };
  327. $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, var_export($value, true)));
  328. // optimize string
  329. $code = preg_replace(array("/^''\./", "/\.''$/", "/\.''\./"), array('', '', '.'), $code);
  330. return $code;
  331. }
  332. } elseif (is_object($value) || is_resource($value)) {
  333. throw new \RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
  334. } else {
  335. return var_export($value, true);
  336. }
  337. }
  338. public function dumpParameter($name)
  339. {
  340. if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
  341. return $this->dumpValue($this->container->getParameter($name), false);
  342. }
  343. return sprintf("\$this->getParameter('%s')", strtolower($name));
  344. }
  345. protected function getServiceCall($id, Reference $reference = null)
  346. {
  347. if ('service_container' === $id) {
  348. return '$this';
  349. }
  350. if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
  351. return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
  352. } else {
  353. if ($this->container->hasAlias($id)) {
  354. $id = $this->container->getAlias($id);
  355. }
  356. if ($this->container->hasDefinition($id)) {
  357. return sprintf('$this->get%sService()', Container::camelize($id));
  358. }
  359. return sprintf('$this->get(\'%s\')', $id);
  360. }
  361. }
  362. }