PageRenderTime 57ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://github.com/Exercise/symfony
PHP | 1137 lines | 1004 code | 40 blank | 93 comment | 33 complexity | 7e22b73979248210e55fe3973b574272 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\DependencyInjection\Dumper;
  11. use Symfony\Component\DependencyInjection\Variable;
  12. use Symfony\Component\DependencyInjection\Definition;
  13. use Symfony\Component\DependencyInjection\ContainerBuilder;
  14. use Symfony\Component\DependencyInjection\Container;
  15. use Symfony\Component\DependencyInjection\ContainerInterface;
  16. use Symfony\Component\DependencyInjection\Reference;
  17. use Symfony\Component\DependencyInjection\Parameter;
  18. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  19. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  20. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  21. /**
  22. * PhpDumper dumps a service container as a PHP class.
  23. *
  24. * @author Fabien Potencier <fabien@symfony.com>
  25. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  26. *
  27. * @api
  28. */
  29. class PhpDumper extends Dumper
  30. {
  31. /**
  32. * Characters that might appear in the generated variable name as first character
  33. * @var string
  34. */
  35. const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
  36. /**
  37. * Characters that might appear in the generated variable name as any but the first character
  38. * @var string
  39. */
  40. const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
  41. private $inlinedDefinitions;
  42. private $definitionVariables;
  43. private $referenceVariables;
  44. private $variableCount;
  45. private $reservedVariables = array('instance', 'class');
  46. /**
  47. * {@inheritDoc}
  48. *
  49. * @api
  50. */
  51. public function __construct(ContainerBuilder $container)
  52. {
  53. parent::__construct($container);
  54. $this->inlinedDefinitions = new \SplObjectStorage;
  55. }
  56. /**
  57. * Dumps the service container as a PHP class.
  58. *
  59. * Available options:
  60. *
  61. * * class: The class name
  62. * * base_class: The base class name
  63. *
  64. * @param array $options An array of options
  65. *
  66. * @return string A PHP class representing of the service container
  67. *
  68. * @api
  69. */
  70. public function dump(array $options = array())
  71. {
  72. $options = array_merge(array(
  73. 'class' => 'ProjectServiceContainer',
  74. 'base_class' => 'Container',
  75. ), $options);
  76. $code = $this->startClass($options['class'], $options['base_class']);
  77. if ($this->container->isFrozen()) {
  78. $code .= $this->addFrozenConstructor();
  79. } else {
  80. $code .= $this->addConstructor();
  81. }
  82. $code .=
  83. $this->addServices().
  84. $this->addDefaultParametersMethod().
  85. $this->endClass()
  86. ;
  87. return $code;
  88. }
  89. /**
  90. * Generates Service local temp variables.
  91. *
  92. * @param string $cId
  93. * @param string $definition
  94. *
  95. * @return string
  96. */
  97. private function addServiceLocalTempVariables($cId, $definition)
  98. {
  99. static $template = " \$%s = %s;\n";
  100. $localDefinitions = array_merge(
  101. array($definition),
  102. $this->getInlinedDefinitions($definition)
  103. );
  104. $calls = $behavior = array();
  105. foreach ($localDefinitions as $iDefinition) {
  106. $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
  107. $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
  108. $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
  109. }
  110. $code = '';
  111. foreach ($calls as $id => $callCount) {
  112. if ('service_container' === $id || $id === $cId) {
  113. continue;
  114. }
  115. if ($callCount > 1) {
  116. $name = $this->getNextVariableName();
  117. $this->referenceVariables[$id] = new Variable($name);
  118. if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
  119. $code .= sprintf($template, $name, $this->getServiceCall($id));
  120. } else {
  121. $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
  122. }
  123. }
  124. }
  125. if ('' !== $code) {
  126. $code .= "\n";
  127. }
  128. return $code;
  129. }
  130. /**
  131. * Generates the require_once statement for service includes.
  132. *
  133. * @param string $id The service id
  134. * @param Definition $definition
  135. *
  136. * @return string
  137. */
  138. private function addServiceInclude($id, $definition)
  139. {
  140. $template = " require_once %s;\n";
  141. $code = '';
  142. if (null !== $file = $definition->getFile()) {
  143. $code .= sprintf($template, $this->dumpValue($file));
  144. }
  145. foreach ($this->getInlinedDefinitions($definition) as $definition) {
  146. if (null !== $file = $definition->getFile()) {
  147. $code .= sprintf($template, $this->dumpValue($file));
  148. }
  149. }
  150. if ('' !== $code) {
  151. $code .= "\n";
  152. }
  153. return $code;
  154. }
  155. /**
  156. * Generates the inline definition of a service.
  157. *
  158. * @param string $id
  159. * @param Definition $definition
  160. *
  161. * @return string
  162. */
  163. private function addServiceInlinedDefinitions($id, $definition)
  164. {
  165. $code = '';
  166. $variableMap = $this->definitionVariables;
  167. $nbOccurrences = new \SplObjectStorage();
  168. $processed = new \SplObjectStorage();
  169. $inlinedDefinitions = $this->getInlinedDefinitions($definition);
  170. foreach ($inlinedDefinitions as $definition) {
  171. if (false === $nbOccurrences->contains($definition)) {
  172. $nbOccurrences->offsetSet($definition, 1);
  173. } else {
  174. $i = $nbOccurrences->offsetGet($definition);
  175. $nbOccurrences->offsetSet($definition, $i+1);
  176. }
  177. }
  178. foreach ($inlinedDefinitions as $sDefinition) {
  179. if ($processed->contains($sDefinition)) {
  180. continue;
  181. }
  182. $processed->offsetSet($sDefinition);
  183. $class = $this->dumpValue($sDefinition->getClass());
  184. if ($nbOccurrences->offsetGet($sDefinition) > 1 || count($sDefinition->getMethodCalls()) > 0 || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
  185. $name = $this->getNextVariableName();
  186. $variableMap->offsetSet($sDefinition, new Variable($name));
  187. // a construct like:
  188. // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
  189. // this is an indication for a wrong implementation, you can circumvent this problem
  190. // by setting up your service structure like this:
  191. // $b = new ServiceB();
  192. // $a = new ServiceA(ServiceB $b);
  193. // $b->setServiceA(ServiceA $a);
  194. if ($this->hasReference($id, $sDefinition->getArguments())) {
  195. throw new ServiceCircularReferenceException($id, array($id));
  196. }
  197. $arguments = array();
  198. foreach ($sDefinition->getArguments() as $argument) {
  199. $arguments[] = $this->dumpValue($argument);
  200. }
  201. if (null !== $sDefinition->getFactoryMethod()) {
  202. if (null !== $sDefinition->getFactoryClass()) {
  203. $code .= sprintf(" \$%s = call_user_func(array(%s, '%s')%s);\n", $name, $this->dumpValue($sDefinition->getFactoryClass()), $sDefinition->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
  204. } elseif (null !== $sDefinition->getFactoryService()) {
  205. $code .= sprintf(" \$%s = %s->%s(%s);\n", $name, $this->getServiceCall($sDefinition->getFactoryService()), $sDefinition->getFactoryMethod(), implode(', ', $arguments));
  206. } else {
  207. throw new RuntimeException('Factory service or factory class must be defined in service definition for '.$id);
  208. }
  209. } elseif (false !== strpos($class, '$')) {
  210. $code .= sprintf(" \$class = %s;\n \$%s = new \$class(%s);\n", $class, $name, implode(', ', $arguments));
  211. } else {
  212. $code .= sprintf(" \$%s = new \\%s(%s);\n", $name, substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
  213. }
  214. if (!$this->hasReference($id, $sDefinition->getMethodCalls()) && !$this->hasReference($id, $sDefinition->getProperties())) {
  215. $code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
  216. $code .= $this->addServiceProperties(null, $sDefinition, $name);
  217. $code .= $this->addServiceConfigurator(null, $sDefinition, $name);
  218. }
  219. $code .= "\n";
  220. }
  221. }
  222. return $code;
  223. }
  224. /**
  225. * Adds the service return statement.
  226. *
  227. * @param string $id Service id
  228. * @param Definition $definition
  229. *
  230. * @return string
  231. */
  232. private function addServiceReturn($id, $definition)
  233. {
  234. if ($this->isSimpleInstance($id, $definition)) {
  235. return " }\n";
  236. }
  237. return "\n return \$instance;\n }\n";
  238. }
  239. /**
  240. * Generates the service instance.
  241. *
  242. * @param string $id
  243. * @param Definition $definition
  244. *
  245. * @return string
  246. *
  247. * @throws InvalidArgumentException
  248. * @throws RuntimeException
  249. */
  250. private function addServiceInstance($id, $definition)
  251. {
  252. $class = $this->dumpValue($definition->getClass());
  253. 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)) {
  254. throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
  255. }
  256. $arguments = array();
  257. foreach ($definition->getArguments() as $value) {
  258. $arguments[] = $this->dumpValue($value);
  259. }
  260. $simple = $this->isSimpleInstance($id, $definition);
  261. $instantiation = '';
  262. if (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
  263. $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
  264. } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
  265. $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
  266. } elseif (!$simple) {
  267. $instantiation = '$instance';
  268. }
  269. $return = '';
  270. if ($simple) {
  271. $return = 'return ';
  272. } else {
  273. $instantiation .= ' = ';
  274. }
  275. if (null !== $definition->getFactoryMethod()) {
  276. if (null !== $definition->getFactoryClass()) {
  277. $code = sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
  278. } elseif (null !== $definition->getFactoryService()) {
  279. $code = sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments));
  280. } else {
  281. throw new RuntimeException('Factory method requires a factory service or factory class in service definition for '.$id);
  282. }
  283. } elseif (false !== strpos($class, '$')) {
  284. $code = sprintf(" \$class = %s;\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
  285. } else {
  286. $code = sprintf(" $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
  287. }
  288. if (!$simple) {
  289. $code .= "\n";
  290. }
  291. return $code;
  292. }
  293. /**
  294. * Checks if the definition is a simple instance.
  295. *
  296. * @param string $id
  297. * @param Definition $definition
  298. *
  299. * @return Boolean
  300. */
  301. private function isSimpleInstance($id, $definition)
  302. {
  303. foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
  304. if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
  305. continue;
  306. }
  307. if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
  308. return false;
  309. }
  310. }
  311. return true;
  312. }
  313. /**
  314. * Adds method calls to a service definition.
  315. *
  316. * @param string $id
  317. * @param Definition $definition
  318. * @param string $variableName
  319. *
  320. * @return string
  321. */
  322. private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
  323. {
  324. $calls = '';
  325. foreach ($definition->getMethodCalls() as $call) {
  326. $arguments = array();
  327. foreach ($call[1] as $value) {
  328. $arguments[] = $this->dumpValue($value);
  329. }
  330. $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
  331. }
  332. return $calls;
  333. }
  334. private function addServiceProperties($id, $definition, $variableName = 'instance')
  335. {
  336. $code = '';
  337. foreach ($definition->getProperties() as $name => $value) {
  338. $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
  339. }
  340. return $code;
  341. }
  342. /**
  343. * Generates the inline definition setup.
  344. *
  345. * @param string $id
  346. * @param Definition $definition
  347. * @return string
  348. */
  349. private function addServiceInlinedDefinitionsSetup($id, $definition)
  350. {
  351. $this->referenceVariables[$id] = new Variable('instance');
  352. $code = '';
  353. $processed = new \SplObjectStorage();
  354. foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
  355. if ($processed->contains($iDefinition)) {
  356. continue;
  357. }
  358. $processed->offsetSet($iDefinition);
  359. if (!$this->hasReference($id, $iDefinition->getMethodCalls())) {
  360. continue;
  361. }
  362. if ($iDefinition->getMethodCalls()) {
  363. $code .= $this->addServiceMethodCalls(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition));
  364. }
  365. if ($iDefinition->getConfigurator()) {
  366. $code .= $this->addServiceConfigurator(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition));
  367. }
  368. }
  369. if ('' !== $code) {
  370. $code .= "\n";
  371. }
  372. return $code;
  373. }
  374. /**
  375. * Adds configurator definition
  376. *
  377. * @param string $id
  378. * @param Definition $definition
  379. * @param string $variableName
  380. *
  381. * @return string
  382. */
  383. private function addServiceConfigurator($id, $definition, $variableName = 'instance')
  384. {
  385. if (!$callable = $definition->getConfigurator()) {
  386. return '';
  387. }
  388. if (is_array($callable)) {
  389. if (is_object($callable[0]) && $callable[0] instanceof Reference) {
  390. return sprintf(" %s->%s(\$%s);\n", $this->getServiceCall((string) $callable[0]), $callable[1], $variableName);
  391. }
  392. return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
  393. }
  394. return sprintf(" %s(\$%s);\n", $callable, $variableName);
  395. }
  396. /**
  397. * Adds a service
  398. *
  399. * @param string $id
  400. * @param Definition $definition
  401. *
  402. * @return string
  403. */
  404. private function addService($id, $definition)
  405. {
  406. $name = Container::camelize($id);
  407. $this->definitionVariables = new \SplObjectStorage();
  408. $this->referenceVariables = array();
  409. $this->variableCount = 0;
  410. $return = '';
  411. if ($definition->isSynthetic()) {
  412. $return = sprintf('@throws RuntimeException always since this service is expected to be injected dynamically');
  413. } elseif ($class = $definition->getClass()) {
  414. $return = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'Object' : $class, $class);
  415. } elseif ($definition->getFactoryClass()) {
  416. $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
  417. } elseif ($definition->getFactoryService()) {
  418. $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod());
  419. }
  420. $doc = '';
  421. if (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) {
  422. $doc .= <<<EOF
  423. *
  424. * This service is shared.
  425. * This method always returns the same instance of the service.
  426. EOF;
  427. }
  428. if (!$definition->isPublic()) {
  429. $doc .= <<<EOF
  430. *
  431. * This service is private.
  432. * If you want to be able to request this service from the container directly,
  433. * make it public, otherwise you might end up with broken code.
  434. EOF;
  435. }
  436. $code = <<<EOF
  437. /**
  438. * Gets the '$id' service.$doc
  439. *
  440. * $return
  441. */
  442. protected function get{$name}Service()
  443. {
  444. EOF;
  445. $scope = $definition->getScope();
  446. if (ContainerInterface::SCOPE_CONTAINER !== $scope && ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
  447. $code .= <<<EOF
  448. if (!isset(\$this->scopedServices['$scope'])) {
  449. throw new InactiveScopeException('$id', '$scope');
  450. }
  451. EOF;
  452. }
  453. if ($definition->isSynthetic()) {
  454. $code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id);
  455. } else {
  456. $code .=
  457. $this->addServiceInclude($id, $definition).
  458. $this->addServiceLocalTempVariables($id, $definition).
  459. $this->addServiceInlinedDefinitions($id, $definition).
  460. $this->addServiceInstance($id, $definition).
  461. $this->addServiceInlinedDefinitionsSetup($id, $definition).
  462. $this->addServiceMethodCalls($id, $definition).
  463. $this->addServiceProperties($id, $definition).
  464. $this->addServiceConfigurator($id, $definition).
  465. $this->addServiceReturn($id, $definition)
  466. ;
  467. }
  468. $this->definitionVariables = null;
  469. $this->referenceVariables = null;
  470. return $code;
  471. }
  472. /**
  473. * Adds a service alias.
  474. *
  475. * @param string $alias
  476. * @param string $id
  477. *
  478. * @return string
  479. */
  480. private function addServiceAlias($alias, $id)
  481. {
  482. $name = Container::camelize($alias);
  483. $type = 'Object';
  484. if ($this->container->hasDefinition($id)) {
  485. $class = $this->container->getDefinition($id)->getClass();
  486. $type = 0 === strpos($class, '%') ? 'Object' : $class;
  487. }
  488. return <<<EOF
  489. /**
  490. * Gets the $alias service alias.
  491. *
  492. * @return $type An instance of the $id service
  493. */
  494. protected function get{$name}Service()
  495. {
  496. return {$this->getServiceCall($id)};
  497. }
  498. EOF;
  499. }
  500. /**
  501. * Adds multiple services
  502. *
  503. * @return string
  504. */
  505. private function addServices()
  506. {
  507. $publicServices = $privateServices = $aliasServices = '';
  508. $definitions = $this->container->getDefinitions();
  509. ksort($definitions);
  510. foreach ($definitions as $id => $definition) {
  511. if ($definition->isPublic()) {
  512. $publicServices .= $this->addService($id, $definition);
  513. } else {
  514. $privateServices .= $this->addService($id, $definition);
  515. }
  516. }
  517. $aliases = $this->container->getAliases();
  518. ksort($aliases);
  519. foreach ($aliases as $alias => $id) {
  520. $aliasServices .= $this->addServiceAlias($alias, $id);
  521. }
  522. return $publicServices.$aliasServices.$privateServices;
  523. }
  524. /**
  525. * Adds the class headers.
  526. *
  527. * @param string $class Class name
  528. * @param string $baseClass The name of the base class
  529. *
  530. * @return string
  531. */
  532. private function startClass($class, $baseClass)
  533. {
  534. $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
  535. return <<<EOF
  536. <?php
  537. use Symfony\Component\DependencyInjection\ContainerInterface;
  538. use Symfony\Component\DependencyInjection\Container;
  539. use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
  540. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  541. use Symfony\Component\DependencyInjection\Exception\LogicException;
  542. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  543. use Symfony\Component\DependencyInjection\Reference;
  544. use Symfony\Component\DependencyInjection\Parameter;
  545. $bagClass
  546. /**
  547. * $class
  548. *
  549. * This class has been auto-generated
  550. * by the Symfony Dependency Injection Component.
  551. */
  552. class $class extends $baseClass
  553. {
  554. EOF;
  555. }
  556. /**
  557. * Adds the constructor.
  558. *
  559. * @return string
  560. */
  561. private function addConstructor()
  562. {
  563. $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
  564. $code = <<<EOF
  565. /**
  566. * Constructor.
  567. */
  568. public function __construct()
  569. {
  570. parent::__construct($arguments);
  571. EOF;
  572. if (count($scopes = $this->container->getScopes()) > 0) {
  573. $code .= "\n";
  574. $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n";
  575. $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
  576. }
  577. $code .= <<<EOF
  578. }
  579. EOF;
  580. return $code;
  581. }
  582. /**
  583. * Adds the constructor for a frozen container.
  584. *
  585. * @return string
  586. */
  587. private function addFrozenConstructor()
  588. {
  589. $code = <<<EOF
  590. /**
  591. * Constructor.
  592. */
  593. public function __construct()
  594. {
  595. \$this->parameters = \$this->getDefaultParameters();
  596. \$this->services =
  597. \$this->scopedServices =
  598. \$this->scopeStacks = array();
  599. \$this->set('service_container', \$this);
  600. EOF;
  601. $code .= "\n";
  602. if (count($scopes = $this->container->getScopes()) > 0) {
  603. $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n";
  604. $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
  605. } else {
  606. $code .= " \$this->scopes = array();\n";
  607. $code .= " \$this->scopeChildren = array();\n";
  608. }
  609. $code .= <<<EOF
  610. }
  611. EOF;
  612. return $code;
  613. }
  614. /**
  615. * Adds default parameters method.
  616. *
  617. * @return string
  618. */
  619. private function addDefaultParametersMethod()
  620. {
  621. if (!$this->container->getParameterBag()->all()) {
  622. return '';
  623. }
  624. $parameters = $this->exportParameters($this->container->getParameterBag()->all());
  625. $code = '';
  626. if ($this->container->isFrozen()) {
  627. $code .= <<<EOF
  628. /**
  629. * {@inheritdoc}
  630. */
  631. public function getParameter(\$name)
  632. {
  633. \$name = strtolower(\$name);
  634. if (!array_key_exists(\$name, \$this->parameters)) {
  635. throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name));
  636. }
  637. return \$this->parameters[\$name];
  638. }
  639. /**
  640. * {@inheritdoc}
  641. */
  642. public function hasParameter(\$name)
  643. {
  644. return array_key_exists(strtolower(\$name), \$this->parameters);
  645. }
  646. /**
  647. * {@inheritdoc}
  648. */
  649. public function setParameter(\$name, \$value)
  650. {
  651. throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
  652. }
  653. /**
  654. * {@inheritDoc}
  655. */
  656. public function getParameterBag()
  657. {
  658. if (null === \$this->parameterBag) {
  659. \$this->parameterBag = new FrozenParameterBag(\$this->parameters);
  660. }
  661. return \$this->parameterBag;
  662. }
  663. EOF;
  664. }
  665. $code .= <<<EOF
  666. /**
  667. * Gets the default parameters.
  668. *
  669. * @return array An array of the default parameters
  670. */
  671. protected function getDefaultParameters()
  672. {
  673. return $parameters;
  674. }
  675. EOF;
  676. return $code;
  677. }
  678. /**
  679. * Exports parameters.
  680. *
  681. * @param array $parameters
  682. * @param string $path
  683. * @param integer $indent
  684. *
  685. * @return string
  686. */
  687. private function exportParameters($parameters, $path = '', $indent = 12)
  688. {
  689. $php = array();
  690. foreach ($parameters as $key => $value) {
  691. if (is_array($value)) {
  692. $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
  693. } elseif ($value instanceof Variable) {
  694. throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
  695. } elseif ($value instanceof Definition) {
  696. throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found in "%s".', $value->getClass(), $path.'/'.$key));
  697. } elseif ($value instanceof Reference) {
  698. throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service "%s" found in "%s").', $value, $path.'/'.$key));
  699. } else {
  700. $value = var_export($value, true);
  701. }
  702. $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
  703. }
  704. return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
  705. }
  706. /**
  707. * Ends the class definition.
  708. *
  709. * @return string
  710. */
  711. private function endClass()
  712. {
  713. return <<<EOF
  714. }
  715. EOF;
  716. }
  717. /**
  718. * Wraps the service conditionals.
  719. *
  720. * @param string $value
  721. * @param string $code
  722. *
  723. * @return string
  724. */
  725. private function wrapServiceConditionals($value, $code)
  726. {
  727. if (!$services = ContainerBuilder::getServiceConditionals($value)) {
  728. return $code;
  729. }
  730. $conditions = array();
  731. foreach ($services as $service) {
  732. $conditions[] = sprintf("\$this->has('%s')", $service);
  733. }
  734. // re-indent the wrapped code
  735. $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
  736. return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
  737. }
  738. /**
  739. * Builds service calls from arguments
  740. *
  741. * @param array $arguments
  742. * @param string &$calls By reference
  743. * @param string &$behavior By reference
  744. */
  745. private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
  746. {
  747. foreach ($arguments as $argument) {
  748. if (is_array($argument)) {
  749. $this->getServiceCallsFromArguments($argument, $calls, $behavior);
  750. } elseif ($argument instanceof Reference) {
  751. $id = (string) $argument;
  752. if (!isset($calls[$id])) {
  753. $calls[$id] = 0;
  754. }
  755. if (!isset($behavior[$id])) {
  756. $behavior[$id] = $argument->getInvalidBehavior();
  757. } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
  758. $behavior[$id] = $argument->getInvalidBehavior();
  759. }
  760. $calls[$id] += 1;
  761. }
  762. }
  763. }
  764. /**
  765. * Returns the inline definition
  766. *
  767. * @param Definition $definition
  768. *
  769. * @return array
  770. */
  771. private function getInlinedDefinitions(Definition $definition)
  772. {
  773. if (false === $this->inlinedDefinitions->contains($definition)) {
  774. $definitions = array_merge(
  775. $this->getDefinitionsFromArguments($definition->getArguments()),
  776. $this->getDefinitionsFromArguments($definition->getMethodCalls()),
  777. $this->getDefinitionsFromArguments($definition->getProperties())
  778. );
  779. $this->inlinedDefinitions->offsetSet($definition, $definitions);
  780. return $definitions;
  781. }
  782. return $this->inlinedDefinitions->offsetGet($definition);
  783. }
  784. /**
  785. * Gets the definition from arguments
  786. *
  787. * @param array $arguments
  788. *
  789. * @return array
  790. */
  791. private function getDefinitionsFromArguments(array $arguments)
  792. {
  793. $definitions = array();
  794. foreach ($arguments as $argument) {
  795. if (is_array($argument)) {
  796. $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
  797. } elseif ($argument instanceof Definition) {
  798. $definitions = array_merge(
  799. $definitions,
  800. $this->getInlinedDefinitions($argument),
  801. array($argument)
  802. );
  803. }
  804. }
  805. return $definitions;
  806. }
  807. /**
  808. * Checks if a service id has a reference
  809. *
  810. * @param string $id
  811. * @param array $arguments
  812. *
  813. * @return Boolean
  814. */
  815. private function hasReference($id, array $arguments)
  816. {
  817. foreach ($arguments as $argument) {
  818. if (is_array($argument)) {
  819. if ($this->hasReference($id, $argument)) {
  820. return true;
  821. }
  822. } elseif ($argument instanceof Reference) {
  823. if ($id === (string) $argument) {
  824. return true;
  825. }
  826. }
  827. }
  828. return false;
  829. }
  830. /**
  831. * Dumps values.
  832. *
  833. * @param array $value
  834. * @param Boolean $interpolate
  835. *
  836. * @return string
  837. */
  838. private function dumpValue($value, $interpolate = true)
  839. {
  840. if (is_array($value)) {
  841. $code = array();
  842. foreach ($value as $k => $v) {
  843. $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
  844. }
  845. return sprintf('array(%s)', implode(', ', $code));
  846. } elseif (is_object($value) && $value instanceof Definition) {
  847. if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
  848. return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
  849. }
  850. if (count($value->getMethodCalls()) > 0) {
  851. throw new RuntimeException('Cannot dump definitions which have method calls.');
  852. }
  853. if (null !== $value->getConfigurator()) {
  854. throw new RuntimeException('Cannot dump definitions which have a configurator.');
  855. }
  856. $arguments = array();
  857. foreach ($value->getArguments() as $argument) {
  858. $arguments[] = $this->dumpValue($argument);
  859. }
  860. $class = $this->dumpValue($value->getClass());
  861. if (false !== strpos($class, '$')) {
  862. throw new RuntimeException('Cannot dump definitions which have a variable class name.');
  863. }
  864. if (null !== $value->getFactoryMethod()) {
  865. if (null !== $value->getFactoryClass()) {
  866. return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
  867. } elseif (null !== $value->getFactoryService()) {
  868. return sprintf("%s->%s(%s)", $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments));
  869. } else {
  870. throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
  871. }
  872. }
  873. return sprintf("new \\%s(%s)", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
  874. } elseif (is_object($value) && $value instanceof Variable) {
  875. return '$'.$value;
  876. } elseif (is_object($value) && $value instanceof Reference) {
  877. if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
  878. return $this->dumpValue($this->referenceVariables[$id], $interpolate);
  879. }
  880. return $this->getServiceCall((string) $value, $value);
  881. } elseif (is_object($value) && $value instanceof Parameter) {
  882. return $this->dumpParameter($value);
  883. } elseif (true === $interpolate && is_string($value)) {
  884. if (preg_match('/^%([^%]+)%$/', $value, $match)) {
  885. // we do this to deal with non string values (Boolean, integer, ...)
  886. // the preg_replace_callback converts them to strings
  887. return $this->dumpParameter(strtolower($match[1]));
  888. } else {
  889. $that = $this;
  890. $replaceParameters = function ($match) use ($that)
  891. {
  892. return "'.".$that->dumpParameter(strtolower($match[2])).".'";
  893. };
  894. $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, var_export($value, true)));
  895. // optimize string
  896. $code = preg_replace(array("/^''\./", "/\.''$/", "/(\w+)(?:'\.')/", "/(.+)(?:\.''\.)/"), array('', '', '$1', '$1.'), $code);
  897. return $code;
  898. }
  899. } elseif (is_object($value) || is_resource($value)) {
  900. throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
  901. } else {
  902. return var_export($value, true);
  903. }
  904. }
  905. /**
  906. * Dumps a parameter
  907. *
  908. * @param string $name
  909. *
  910. * @return string
  911. */
  912. public function dumpParameter($name)
  913. {
  914. if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
  915. return $this->dumpValue($this->container->getParameter($name), false);
  916. }
  917. return sprintf("\$this->getParameter('%s')", strtolower($name));
  918. }
  919. /**
  920. * Gets a service call
  921. *
  922. * @param string $id
  923. * @param Reference $reference
  924. *
  925. * @return string
  926. */
  927. private function getServiceCall($id, Reference $reference = null)
  928. {
  929. if ('service_container' === $id) {
  930. return '$this';
  931. }
  932. if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
  933. return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
  934. } else {
  935. if ($this->container->hasAlias($id)) {
  936. $id = (string) $this->container->getAlias($id);
  937. }
  938. return sprintf('$this->get(\'%s\')', $id);
  939. }
  940. }
  941. /**
  942. * Returns the next name to use
  943. *
  944. * @return string
  945. */
  946. private function getNextVariableName()
  947. {
  948. $firstChars = self::FIRST_CHARS;
  949. $firstCharsLength = strlen($firstChars);
  950. $nonFirstChars = self::NON_FIRST_CHARS;
  951. $nonFirstCharsLength = strlen($nonFirstChars);
  952. while (true) {
  953. $name = '';
  954. $i = $this->variableCount;
  955. if ('' === $name) {
  956. $name .= $firstChars[$i%$firstCharsLength];
  957. $i = intval($i/$firstCharsLength);
  958. }
  959. while ($i > 0) {
  960. $i -= 1;
  961. $name .= $nonFirstChars[$i%$nonFirstCharsLength];
  962. $i = intval($i/$nonFirstCharsLength);
  963. }
  964. $this->variableCount += 1;
  965. // check that the name is not reserved
  966. if (in_array($name, $this->reservedVariables, true)) {
  967. continue;
  968. }
  969. return $name;
  970. }
  971. }
  972. }