PhpDumper.php 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606
  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. use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
  22. use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
  23. use Symfony\Component\DependencyInjection\ExpressionLanguage;
  24. use Symfony\Component\ExpressionLanguage\Expression;
  25. use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
  26. use Symfony\Component\HttpKernel\Kernel;
  27. /**
  28. * PhpDumper dumps a service container as a PHP class.
  29. *
  30. * @author Fabien Potencier <fabien@symfony.com>
  31. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  32. */
  33. class PhpDumper extends Dumper
  34. {
  35. /**
  36. * Characters that might appear in the generated variable name as first character.
  37. *
  38. * @var string
  39. */
  40. const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
  41. /**
  42. * Characters that might appear in the generated variable name as any but the first character.
  43. *
  44. * @var string
  45. */
  46. const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
  47. private $inlinedDefinitions;
  48. private $definitionVariables;
  49. private $referenceVariables;
  50. private $variableCount;
  51. private $reservedVariables = array('instance', 'class');
  52. private $expressionLanguage;
  53. private $targetDirRegex;
  54. private $targetDirMaxMatches;
  55. private $docStar;
  56. /**
  57. * @var ExpressionFunctionProviderInterface[]
  58. */
  59. private $expressionLanguageProviders = array();
  60. /**
  61. * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
  62. */
  63. private $proxyDumper;
  64. /**
  65. * {@inheritdoc}
  66. */
  67. public function __construct(ContainerBuilder $container)
  68. {
  69. parent::__construct($container);
  70. $this->inlinedDefinitions = new \SplObjectStorage();
  71. }
  72. /**
  73. * Sets the dumper to be used when dumping proxies in the generated container.
  74. *
  75. * @param ProxyDumper $proxyDumper
  76. */
  77. public function setProxyDumper(ProxyDumper $proxyDumper)
  78. {
  79. $this->proxyDumper = $proxyDumper;
  80. }
  81. /**
  82. * Dumps the service container as a PHP class.
  83. *
  84. * Available options:
  85. *
  86. * * class: The class name
  87. * * base_class: The base class name
  88. * * namespace: The class namespace
  89. *
  90. * @param array $options An array of options
  91. *
  92. * @return string A PHP class representing of the service container
  93. */
  94. public function dump(array $options = array())
  95. {
  96. $this->targetDirRegex = null;
  97. $options = array_merge(array(
  98. 'class' => 'ProjectServiceContainer',
  99. 'base_class' => 'Container',
  100. 'namespace' => '',
  101. 'debug' => true,
  102. ), $options);
  103. $this->docStar = $options['debug'] ? '*' : '';
  104. if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) {
  105. // Build a regexp where the first root dirs are mandatory,
  106. // but every other sub-dir is optional up to the full path in $dir
  107. // Mandate at least 2 root dirs and not more that 5 optional dirs.
  108. $dir = explode(DIRECTORY_SEPARATOR, realpath($dir));
  109. $i = count($dir);
  110. if (3 <= $i) {
  111. $regex = '';
  112. $lastOptionalDir = $i > 8 ? $i - 5 : 3;
  113. $this->targetDirMaxMatches = $i - $lastOptionalDir;
  114. while (--$i >= $lastOptionalDir) {
  115. $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex);
  116. }
  117. do {
  118. $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex;
  119. } while (0 < --$i);
  120. $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#';
  121. }
  122. }
  123. $code = $this->startClass($options['class'], $options['base_class'], $options['namespace']);
  124. if ($this->container->isFrozen()) {
  125. $code .= $this->addFrozenConstructor();
  126. $code .= $this->addFrozenCompile();
  127. } else {
  128. $code .= $this->addConstructor();
  129. }
  130. $code .=
  131. $this->addServices().
  132. $this->addDefaultParametersMethod().
  133. $this->endClass().
  134. $this->addProxyClasses()
  135. ;
  136. $this->targetDirRegex = null;
  137. return $code;
  138. }
  139. /**
  140. * Retrieves the currently set proxy dumper or instantiates one.
  141. *
  142. * @return ProxyDumper
  143. */
  144. private function getProxyDumper()
  145. {
  146. if (!$this->proxyDumper) {
  147. $this->proxyDumper = new NullDumper();
  148. }
  149. return $this->proxyDumper;
  150. }
  151. /**
  152. * Generates Service local temp variables.
  153. *
  154. * @param string $cId
  155. * @param string $definition
  156. *
  157. * @return string
  158. */
  159. private function addServiceLocalTempVariables($cId, $definition)
  160. {
  161. static $template = " \$%s = %s;\n";
  162. $localDefinitions = array_merge(
  163. array($definition),
  164. $this->getInlinedDefinitions($definition)
  165. );
  166. $calls = $behavior = array();
  167. foreach ($localDefinitions as $iDefinition) {
  168. $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
  169. $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
  170. $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
  171. $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior);
  172. $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior);
  173. }
  174. $code = '';
  175. foreach ($calls as $id => $callCount) {
  176. if ('service_container' === $id || $id === $cId) {
  177. continue;
  178. }
  179. if ($callCount > 1) {
  180. $name = $this->getNextVariableName();
  181. $this->referenceVariables[$id] = new Variable($name);
  182. if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
  183. $code .= sprintf($template, $name, $this->getServiceCall($id));
  184. } else {
  185. $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
  186. }
  187. }
  188. }
  189. if ('' !== $code) {
  190. $code .= "\n";
  191. }
  192. return $code;
  193. }
  194. /**
  195. * Generates code for the proxies to be attached after the container class.
  196. *
  197. * @return string
  198. */
  199. private function addProxyClasses()
  200. {
  201. /* @var $definitions Definition[] */
  202. $definitions = array_filter(
  203. $this->container->getDefinitions(),
  204. array($this->getProxyDumper(), 'isProxyCandidate')
  205. );
  206. $code = '';
  207. $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
  208. foreach ($definitions as $definition) {
  209. $proxyCode = "\n".$this->getProxyDumper()->getProxyCode($definition);
  210. if ($strip) {
  211. $proxyCode = "<?php\n".$proxyCode;
  212. $proxyCode = substr(Kernel::stripComments($proxyCode), 5);
  213. }
  214. $code .= $proxyCode;
  215. }
  216. return $code;
  217. }
  218. /**
  219. * Generates the require_once statement for service includes.
  220. *
  221. * @param string $id The service id
  222. * @param Definition $definition
  223. *
  224. * @return string
  225. */
  226. private function addServiceInclude($id, $definition)
  227. {
  228. $template = " require_once %s;\n";
  229. $code = '';
  230. if (null !== $file = $definition->getFile()) {
  231. $code .= sprintf($template, $this->dumpValue($file));
  232. }
  233. foreach ($this->getInlinedDefinitions($definition) as $definition) {
  234. if (null !== $file = $definition->getFile()) {
  235. $code .= sprintf($template, $this->dumpValue($file));
  236. }
  237. }
  238. if ('' !== $code) {
  239. $code .= "\n";
  240. }
  241. return $code;
  242. }
  243. /**
  244. * Generates the inline definition of a service.
  245. *
  246. * @param string $id
  247. * @param Definition $definition
  248. *
  249. * @return string
  250. *
  251. * @throws RuntimeException When the factory definition is incomplete
  252. * @throws ServiceCircularReferenceException When a circular reference is detected
  253. */
  254. private function addServiceInlinedDefinitions($id, $definition)
  255. {
  256. $code = '';
  257. $variableMap = $this->definitionVariables;
  258. $nbOccurrences = new \SplObjectStorage();
  259. $processed = new \SplObjectStorage();
  260. $inlinedDefinitions = $this->getInlinedDefinitions($definition);
  261. foreach ($inlinedDefinitions as $definition) {
  262. if (false === $nbOccurrences->contains($definition)) {
  263. $nbOccurrences->offsetSet($definition, 1);
  264. } else {
  265. $i = $nbOccurrences->offsetGet($definition);
  266. $nbOccurrences->offsetSet($definition, $i + 1);
  267. }
  268. }
  269. foreach ($inlinedDefinitions as $sDefinition) {
  270. if ($processed->contains($sDefinition)) {
  271. continue;
  272. }
  273. $processed->offsetSet($sDefinition);
  274. $class = $this->dumpValue($sDefinition->getClass());
  275. if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
  276. $name = $this->getNextVariableName();
  277. $variableMap->offsetSet($sDefinition, new Variable($name));
  278. // a construct like:
  279. // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
  280. // this is an indication for a wrong implementation, you can circumvent this problem
  281. // by setting up your service structure like this:
  282. // $b = new ServiceB();
  283. // $a = new ServiceA(ServiceB $b);
  284. // $b->setServiceA(ServiceA $a);
  285. if ($this->hasReference($id, $sDefinition->getArguments())) {
  286. throw new ServiceCircularReferenceException($id, array($id));
  287. }
  288. $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
  289. if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
  290. $code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
  291. $code .= $this->addServiceProperties(null, $sDefinition, $name);
  292. $code .= $this->addServiceConfigurator(null, $sDefinition, $name);
  293. }
  294. $code .= "\n";
  295. }
  296. }
  297. return $code;
  298. }
  299. /**
  300. * Adds the service return statement.
  301. *
  302. * @param string $id Service id
  303. * @param Definition $definition
  304. *
  305. * @return string
  306. */
  307. private function addServiceReturn($id, $definition)
  308. {
  309. if ($this->isSimpleInstance($id, $definition)) {
  310. return " }\n";
  311. }
  312. return "\n return \$instance;\n }\n";
  313. }
  314. /**
  315. * Generates the service instance.
  316. *
  317. * @param string $id
  318. * @param Definition $definition
  319. *
  320. * @return string
  321. *
  322. * @throws InvalidArgumentException
  323. * @throws RuntimeException
  324. */
  325. private function addServiceInstance($id, $definition)
  326. {
  327. $class = $definition->getClass();
  328. if ('\\' === substr($class, 0, 1)) {
  329. $class = substr($class, 1);
  330. }
  331. $class = $this->dumpValue($class);
  332. 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)) {
  333. throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
  334. }
  335. $simple = $this->isSimpleInstance($id, $definition);
  336. $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
  337. $instantiation = '';
  338. if (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) {
  339. $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
  340. } elseif (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) {
  341. $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
  342. } elseif (!$simple) {
  343. $instantiation = '$instance';
  344. }
  345. $return = '';
  346. if ($simple) {
  347. $return = 'return ';
  348. } else {
  349. $instantiation .= ' = ';
  350. }
  351. $code = $this->addNewInstance($id, $definition, $return, $instantiation);
  352. if (!$simple) {
  353. $code .= "\n";
  354. }
  355. return $code;
  356. }
  357. /**
  358. * Checks if the definition is a simple instance.
  359. *
  360. * @param string $id
  361. * @param Definition $definition
  362. *
  363. * @return bool
  364. */
  365. private function isSimpleInstance($id, $definition)
  366. {
  367. foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
  368. if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
  369. continue;
  370. }
  371. if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
  372. return false;
  373. }
  374. }
  375. return true;
  376. }
  377. /**
  378. * Adds method calls to a service definition.
  379. *
  380. * @param string $id
  381. * @param Definition $definition
  382. * @param string $variableName
  383. *
  384. * @return string
  385. */
  386. private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
  387. {
  388. $calls = '';
  389. foreach ($definition->getMethodCalls() as $call) {
  390. $arguments = array();
  391. foreach ($call[1] as $value) {
  392. $arguments[] = $this->dumpValue($value);
  393. }
  394. $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
  395. }
  396. return $calls;
  397. }
  398. private function addServiceProperties($id, $definition, $variableName = 'instance')
  399. {
  400. $code = '';
  401. foreach ($definition->getProperties() as $name => $value) {
  402. $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
  403. }
  404. return $code;
  405. }
  406. /**
  407. * Generates the inline definition setup.
  408. *
  409. * @param string $id
  410. * @param Definition $definition
  411. *
  412. * @return string
  413. *
  414. * @throws ServiceCircularReferenceException when the container contains a circular reference
  415. */
  416. private function addServiceInlinedDefinitionsSetup($id, $definition)
  417. {
  418. $this->referenceVariables[$id] = new Variable('instance');
  419. $code = '';
  420. $processed = new \SplObjectStorage();
  421. foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
  422. if ($processed->contains($iDefinition)) {
  423. continue;
  424. }
  425. $processed->offsetSet($iDefinition);
  426. if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) {
  427. continue;
  428. }
  429. // if the instance is simple, the return statement has already been generated
  430. // so, the only possible way to get there is because of a circular reference
  431. if ($this->isSimpleInstance($id, $definition)) {
  432. throw new ServiceCircularReferenceException($id, array($id));
  433. }
  434. $name = (string) $this->definitionVariables->offsetGet($iDefinition);
  435. $code .= $this->addServiceMethodCalls(null, $iDefinition, $name);
  436. $code .= $this->addServiceProperties(null, $iDefinition, $name);
  437. $code .= $this->addServiceConfigurator(null, $iDefinition, $name);
  438. }
  439. if ('' !== $code) {
  440. $code .= "\n";
  441. }
  442. return $code;
  443. }
  444. /**
  445. * Adds configurator definition.
  446. *
  447. * @param string $id
  448. * @param Definition $definition
  449. * @param string $variableName
  450. *
  451. * @return string
  452. */
  453. private function addServiceConfigurator($id, $definition, $variableName = 'instance')
  454. {
  455. if (!$callable = $definition->getConfigurator()) {
  456. return '';
  457. }
  458. if (is_array($callable)) {
  459. if ($callable[0] instanceof Reference
  460. || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
  461. return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
  462. }
  463. $class = $this->dumpValue($callable[0]);
  464. // If the class is a string we can optimize call_user_func away
  465. if (strpos($class, "'") === 0) {
  466. return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName);
  467. }
  468. return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
  469. }
  470. return sprintf(" %s(\$%s);\n", $callable, $variableName);
  471. }
  472. /**
  473. * Adds a service.
  474. *
  475. * @param string $id
  476. * @param Definition $definition
  477. *
  478. * @return string
  479. */
  480. private function addService($id, $definition)
  481. {
  482. $this->definitionVariables = new \SplObjectStorage();
  483. $this->referenceVariables = array();
  484. $this->variableCount = 0;
  485. $return = array();
  486. if ($definition->isSynthetic()) {
  487. $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
  488. } elseif ($class = $definition->getClass()) {
  489. $return[] = sprintf('@return %s A %s instance.', 0 === strpos($class, '%') ? 'object' : '\\'.ltrim($class, '\\'), ltrim($class, '\\'));
  490. } elseif ($definition->getFactory()) {
  491. $factory = $definition->getFactory();
  492. if (is_string($factory)) {
  493. $return[] = sprintf('@return object An instance returned by %s().', $factory);
  494. } elseif (is_array($factory) && (is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
  495. if (is_string($factory[0]) || $factory[0] instanceof Reference) {
  496. $return[] = sprintf('@return object An instance returned by %s::%s().', (string) $factory[0], $factory[1]);
  497. } elseif ($factory[0] instanceof Definition) {
  498. $return[] = sprintf('@return object An instance returned by %s::%s().', $factory[0]->getClass(), $factory[1]);
  499. }
  500. }
  501. } elseif ($definition->getFactoryClass(false)) {
  502. $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(false), $definition->getFactoryMethod(false));
  503. } elseif ($definition->getFactoryService(false)) {
  504. $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(false), $definition->getFactoryMethod(false));
  505. }
  506. $scope = $definition->getScope(false);
  507. if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
  508. if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
  509. $return[] = '';
  510. }
  511. $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
  512. }
  513. if ($definition->isDeprecated()) {
  514. if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
  515. $return[] = '';
  516. }
  517. $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id));
  518. }
  519. $return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return));
  520. $doc = '';
  521. if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
  522. $doc .= <<<'EOF'
  523. *
  524. * This service is shared.
  525. * This method always returns the same instance of the service.
  526. EOF;
  527. }
  528. if (!$definition->isPublic()) {
  529. $doc .= <<<'EOF'
  530. *
  531. * This service is private.
  532. * If you want to be able to request this service from the container directly,
  533. * make it public, otherwise you might end up with broken code.
  534. EOF;
  535. }
  536. if ($definition->isAutowired()) {
  537. $doc = <<<EOF
  538. *
  539. * This service is autowired.
  540. EOF;
  541. }
  542. if ($definition->isLazy()) {
  543. $lazyInitialization = '$lazyLoad = true';
  544. $lazyInitializationDoc = "\n * @param bool \$lazyLoad whether to try lazy-loading the service with a proxy\n *";
  545. } else {
  546. $lazyInitialization = '';
  547. $lazyInitializationDoc = '';
  548. }
  549. // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
  550. $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
  551. $visibility = $isProxyCandidate ? 'public' : 'protected';
  552. $code = <<<EOF
  553. /*{$this->docStar}
  554. * Gets the '$id' service.$doc
  555. *$lazyInitializationDoc
  556. * $return
  557. */
  558. {$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
  559. {
  560. EOF;
  561. $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
  562. if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
  563. $code .= <<<EOF
  564. if (!isset(\$this->scopedServices['$scope'])) {
  565. throw new InactiveScopeException('$id', '$scope');
  566. }
  567. EOF;
  568. }
  569. if ($definition->isSynthetic()) {
  570. $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);
  571. } else {
  572. if ($definition->isDeprecated()) {
  573. $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", var_export($definition->getDeprecationMessage($id), true));
  574. }
  575. $code .=
  576. $this->addServiceInclude($id, $definition).
  577. $this->addServiceLocalTempVariables($id, $definition).
  578. $this->addServiceInlinedDefinitions($id, $definition).
  579. $this->addServiceInstance($id, $definition).
  580. $this->addServiceInlinedDefinitionsSetup($id, $definition).
  581. $this->addServiceMethodCalls($id, $definition).
  582. $this->addServiceProperties($id, $definition).
  583. $this->addServiceConfigurator($id, $definition).
  584. $this->addServiceReturn($id, $definition)
  585. ;
  586. }
  587. $this->definitionVariables = null;
  588. $this->referenceVariables = null;
  589. return $code;
  590. }
  591. /**
  592. * Adds multiple services.
  593. *
  594. * @return string
  595. */
  596. private function addServices()
  597. {
  598. $publicServices = $privateServices = $synchronizers = '';
  599. $definitions = $this->container->getDefinitions();
  600. ksort($definitions);
  601. foreach ($definitions as $id => $definition) {
  602. if ($definition->isPublic()) {
  603. $publicServices .= $this->addService($id, $definition);
  604. } else {
  605. $privateServices .= $this->addService($id, $definition);
  606. }
  607. $synchronizers .= $this->addServiceSynchronizer($id, $definition);
  608. }
  609. return $publicServices.$synchronizers.$privateServices;
  610. }
  611. /**
  612. * Adds synchronizer methods.
  613. *
  614. * @param string $id A service identifier
  615. * @param Definition $definition A Definition instance
  616. *
  617. * @return string|null
  618. *
  619. * @deprecated since version 2.7, will be removed in 3.0.
  620. */
  621. private function addServiceSynchronizer($id, Definition $definition)
  622. {
  623. if (!$definition->isSynchronized(false)) {
  624. return;
  625. }
  626. if ('request' !== $id) {
  627. @trigger_error('Synchronized services were deprecated in version 2.7 and won\'t work anymore in 3.0.', E_USER_DEPRECATED);
  628. }
  629. $code = '';
  630. foreach ($this->container->getDefinitions() as $definitionId => $definition) {
  631. foreach ($definition->getMethodCalls() as $call) {
  632. foreach ($call[1] as $argument) {
  633. if ($argument instanceof Reference && $id == (string) $argument) {
  634. $arguments = array();
  635. foreach ($call[1] as $value) {
  636. $arguments[] = $this->dumpValue($value);
  637. }
  638. $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
  639. $code .= <<<EOF
  640. if (\$this->initialized('$definitionId')) {
  641. $call
  642. }
  643. EOF;
  644. }
  645. }
  646. }
  647. }
  648. if (!$code) {
  649. return;
  650. }
  651. return <<<EOF
  652. /*{$this->docStar}
  653. * Updates the '$id' service.
  654. */
  655. protected function synchronize{$this->camelize($id)}Service()
  656. {
  657. $code }
  658. EOF;
  659. }
  660. private function addNewInstance($id, Definition $definition, $return, $instantiation)
  661. {
  662. $class = $this->dumpValue($definition->getClass());
  663. $arguments = array();
  664. foreach ($definition->getArguments() as $value) {
  665. $arguments[] = $this->dumpValue($value);
  666. }
  667. if (null !== $definition->getFactory()) {
  668. $callable = $definition->getFactory();
  669. if (is_array($callable)) {
  670. if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) {
  671. throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a'));
  672. }
  673. if ($callable[0] instanceof Reference
  674. || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
  675. return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
  676. }
  677. $class = $this->dumpValue($callable[0]);
  678. // If the class is a string we can optimize call_user_func away
  679. if (strpos($class, "'") === 0) {
  680. return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
  681. }
  682. return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
  683. }
  684. return sprintf(" $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : '');
  685. } elseif (null !== $definition->getFactoryMethod(false)) {
  686. if (null !== $definition->getFactoryClass(false)) {
  687. $class = $this->dumpValue($definition->getFactoryClass(false));
  688. // If the class is a string we can optimize call_user_func away
  689. if (strpos($class, "'") === 0) {
  690. return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $definition->getFactoryMethod(false), $arguments ? implode(', ', $arguments) : '');
  691. }
  692. return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass(false)), $definition->getFactoryMethod(false), $arguments ? ', '.implode(', ', $arguments) : '');
  693. }
  694. if (null !== $definition->getFactoryService(false)) {
  695. return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService(false)), $definition->getFactoryMethod(false), implode(', ', $arguments));
  696. }
  697. throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id));
  698. }
  699. if (false !== strpos($class, '$')) {
  700. return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
  701. }
  702. return sprintf(" $return{$instantiation}new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments));
  703. }
  704. /**
  705. * Adds the class headers.
  706. *
  707. * @param string $class Class name
  708. * @param string $baseClass The name of the base class
  709. * @param string $namespace The class namespace
  710. *
  711. * @return string
  712. */
  713. private function startClass($class, $baseClass, $namespace)
  714. {
  715. $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
  716. $namespaceLine = $namespace ? "namespace $namespace;\n" : '';
  717. return <<<EOF
  718. <?php
  719. $namespaceLine
  720. use Symfony\Component\DependencyInjection\ContainerInterface;
  721. use Symfony\Component\DependencyInjection\Container;
  722. use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
  723. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  724. use Symfony\Component\DependencyInjection\Exception\LogicException;
  725. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  726. $bagClass
  727. /*{$this->docStar}
  728. * $class.
  729. *
  730. * This class has been auto-generated
  731. * by the Symfony Dependency Injection Component.
  732. */
  733. class $class extends $baseClass
  734. {
  735. private \$parameters;
  736. private \$targetDirs = array();
  737. EOF;
  738. }
  739. /**
  740. * Adds the constructor.
  741. *
  742. * @return string
  743. */
  744. private function addConstructor()
  745. {
  746. $targetDirs = $this->exportTargetDirs();
  747. $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
  748. $code = <<<EOF
  749. /*{$this->docStar}
  750. * Constructor.
  751. */
  752. public function __construct()
  753. {{$targetDirs}
  754. parent::__construct($arguments);
  755. EOF;
  756. if (count($scopes = $this->container->getScopes(false)) > 0) {
  757. $code .= "\n";
  758. $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n";
  759. $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren(false)).";\n";
  760. }
  761. $code .= $this->addMethodMap();
  762. $code .= $this->addAliases();
  763. $code .= <<<'EOF'
  764. }
  765. EOF;
  766. return $code;
  767. }
  768. /**
  769. * Adds the constructor for a frozen container.
  770. *
  771. * @return string
  772. */
  773. private function addFrozenConstructor()
  774. {
  775. $targetDirs = $this->exportTargetDirs();
  776. $code = <<<EOF
  777. /*{$this->docStar}
  778. * Constructor.
  779. */
  780. public function __construct()
  781. {{$targetDirs}
  782. EOF;
  783. if ($this->container->getParameterBag()->all()) {
  784. $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n";
  785. }
  786. $code .= <<<'EOF'
  787. $this->services =
  788. $this->scopedServices =
  789. $this->scopeStacks = array();
  790. EOF;
  791. $code .= "\n";
  792. if (count($scopes = $this->container->getScopes(false)) > 0) {
  793. $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n";
  794. $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren(false)).";\n";
  795. } else {
  796. $code .= " \$this->scopes = array();\n";
  797. $code .= " \$this->scopeChildren = array();\n";
  798. }
  799. $code .= $this->addMethodMap();
  800. $code .= $this->addAliases();
  801. $code .= <<<'EOF'
  802. }
  803. EOF;
  804. return $code;
  805. }
  806. /**
  807. * Adds the constructor for a frozen container.
  808. *
  809. * @return string
  810. */
  811. private function addFrozenCompile()
  812. {
  813. return <<<EOF
  814. /*{$this->docStar}
  815. * {@inheritdoc}
  816. */
  817. public function compile()
  818. {
  819. throw new LogicException('You cannot compile a dumped frozen container.');
  820. }
  821. EOF;
  822. }
  823. /**
  824. * Adds the methodMap property definition.
  825. *
  826. * @return string
  827. */
  828. private function addMethodMap()
  829. {
  830. if (!$definitions = $this->container->getDefinitions()) {
  831. return '';
  832. }
  833. $code = " \$this->methodMap = array(\n";
  834. ksort($definitions);
  835. foreach ($definitions as $id => $definition) {
  836. $code .= ' '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
  837. }
  838. return $code." );\n";
  839. }
  840. /**
  841. * Adds the aliases property definition.
  842. *
  843. * @return string
  844. */
  845. private function addAliases()
  846. {
  847. if (!$aliases = $this->container->getAliases()) {
  848. if ($this->container->isFrozen()) {
  849. return "\n \$this->aliases = array();\n";
  850. } else {
  851. return '';
  852. }
  853. }
  854. $code = " \$this->aliases = array(\n";
  855. ksort($aliases);
  856. foreach ($aliases as $alias => $id) {
  857. $id = (string) $id;
  858. while (isset($aliases[$id])) {
  859. $id = (string) $aliases[$id];
  860. }
  861. $code .= ' '.var_export($alias, true).' => '.var_export($id, true).",\n";
  862. }
  863. return $code." );\n";
  864. }
  865. /**
  866. * Adds default parameters method.
  867. *
  868. * @return string
  869. */
  870. private function addDefaultParametersMethod()
  871. {
  872. if (!$this->container->getParameterBag()->all()) {
  873. return '';
  874. }
  875. $parameters = $this->exportParameters($this->container->getParameterBag()->all());
  876. $code = '';
  877. if ($this->container->isFrozen()) {
  878. $code .= <<<'EOF'
  879. /**
  880. * {@inheritdoc}
  881. */
  882. public function getParameter($name)
  883. {
  884. $name = strtolower($name);
  885. if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) {
  886. throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
  887. }
  888. return $this->parameters[$name];
  889. }
  890. /**
  891. * {@inheritdoc}
  892. */
  893. public function hasParameter($name)
  894. {
  895. $name = strtolower($name);
  896. return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
  897. }
  898. /**
  899. * {@inheritdoc}
  900. */
  901. public function setParameter($name, $value)
  902. {
  903. throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
  904. }
  905. /**
  906. * {@inheritdoc}
  907. */
  908. public function getParameterBag()
  909. {
  910. if (null === $this->parameterBag) {
  911. $this->parameterBag = new FrozenParameterBag($this->parameters);
  912. }
  913. return $this->parameterBag;
  914. }
  915. EOF;
  916. if ('' === $this->docStar) {
  917. $code = str_replace('/**', '/*', $code);
  918. }
  919. }
  920. $code .= <<<EOF
  921. /*{$this->docStar}
  922. * Gets the default parameters.
  923. *
  924. * @return array An array of the default parameters
  925. */
  926. protected function getDefaultParameters()
  927. {
  928. return $parameters;
  929. }
  930. EOF;
  931. return $code;
  932. }
  933. /**
  934. * Exports parameters.
  935. *
  936. * @param array $parameters
  937. * @param string $path
  938. * @param int $indent
  939. *
  940. * @return string
  941. *
  942. * @throws InvalidArgumentException
  943. */
  944. private function exportParameters($parameters, $path = '', $indent = 12)
  945. {
  946. $php = array();
  947. foreach ($parameters as $key => $value) {
  948. if (is_array($value)) {
  949. $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
  950. } elseif ($value instanceof Variable) {
  951. throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
  952. } elseif ($value instanceof Definition) {
  953. 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));
  954. } elseif ($value instanceof Reference) {
  955. 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));
  956. } elseif ($value instanceof Expression) {
  957. throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key));
  958. } else {
  959. $value = $this->export($value);
  960. }
  961. $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
  962. }
  963. return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
  964. }
  965. /**
  966. * Ends the class definition.
  967. *
  968. * @return string
  969. */
  970. private function endClass()
  971. {
  972. return <<<'EOF'
  973. }
  974. EOF;
  975. }
  976. /**
  977. * Wraps the service conditionals.
  978. *
  979. * @param string $value
  980. * @param string $code
  981. *
  982. * @return string
  983. */
  984. private function wrapServiceConditionals($value, $code)
  985. {
  986. if (!$services = ContainerBuilder::getServiceConditionals($value)) {
  987. return $code;
  988. }
  989. $conditions = array();
  990. foreach ($services as $service) {
  991. $conditions[] = sprintf("\$this->has('%s')", $service);
  992. }
  993. // re-indent the wrapped code
  994. $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
  995. return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
  996. }
  997. /**
  998. * Builds service calls from arguments.
  999. *
  1000. * @param array $arguments
  1001. * @param array &$calls By reference
  1002. * @param array &$behavior By reference
  1003. */
  1004. private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
  1005. {
  1006. foreach ($arguments as $argument) {
  1007. if (is_array($argument)) {
  1008. $this->getServiceCallsFromArguments($argument, $calls, $behavior);
  1009. } elseif ($argument instanceof Reference) {
  1010. $id = (string) $argument;
  1011. if (!isset($calls[$id])) {
  1012. $calls[$id] = 0;
  1013. }
  1014. if (!isset($behavior[$id])) {
  1015. $behavior[$id] = $argument->getInvalidBehavior();
  1016. } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
  1017. $behavior[$id] = $argument->getInvalidBehavior();
  1018. }
  1019. ++$calls[$id];
  1020. }
  1021. }
  1022. }
  1023. /**
  1024. * Returns the inline definition.
  1025. *
  1026. * @param Definition $definition
  1027. *
  1028. * @return array
  1029. */
  1030. private function getInlinedDefinitions(Definition $definition)
  1031. {
  1032. if (false === $this->inlinedDefinitions->contains($definition)) {
  1033. $definitions = array_merge(
  1034. $this->getDefinitionsFromArguments($definition->getArguments()),
  1035. $this->getDefinitionsFromArguments($definition->getMethodCalls()),
  1036. $this->getDefinitionsFromArguments($definition->getProperties()),
  1037. $this->getDefinitionsFromArguments(array($definition->getConfigurator())),
  1038. $this->getDefinitionsFromArguments(array($definition->getFactory()))
  1039. );
  1040. $this->inlinedDefinitions->offsetSet($definition, $definitions);
  1041. return $definitions;
  1042. }
  1043. return $this->inlinedDefinitions->offsetGet($definition);
  1044. }
  1045. /**
  1046. * Gets the definition from arguments.
  1047. *
  1048. * @param array $arguments
  1049. *
  1050. * @return array
  1051. */
  1052. private function getDefinitionsFromArguments(array $arguments)
  1053. {
  1054. $definitions = array();
  1055. foreach ($arguments as $argument) {
  1056. if (is_array($argument)) {
  1057. $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
  1058. } elseif ($argument instanceof Definition) {
  1059. $definitions = array_merge(
  1060. $definitions,
  1061. $this->getInlinedDefinitions($argument),
  1062. array($argument)
  1063. );
  1064. }
  1065. }
  1066. return $definitions;
  1067. }
  1068. /**
  1069. * Checks if a service id has a reference.
  1070. *
  1071. * @param string $id
  1072. * @param array $arguments
  1073. * @param bool $deep
  1074. * @param array $visited
  1075. *
  1076. * @return bool
  1077. */
  1078. private function hasReference($id, array $arguments, $deep = false, array &$visited = array())
  1079. {
  1080. foreach ($arguments as $argument) {
  1081. if (is_array($argument)) {
  1082. if ($this->hasReference($id, $argument, $deep, $visited)) {
  1083. return true;
  1084. }
  1085. } elseif ($argument instanceof Reference) {
  1086. $argumentId = (string) $argument;
  1087. if ($id === $argumentId) {
  1088. return true;
  1089. }
  1090. if ($deep && !isset($visited[$argumentId])) {
  1091. $visited[$argumentId] = true;
  1092. $service = $this->container->getDefinition($argumentId);
  1093. $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
  1094. if ($this->hasReference($id, $arguments, $deep, $visited)) {
  1095. return true;
  1096. }
  1097. }
  1098. }
  1099. }
  1100. return false;
  1101. }
  1102. /**
  1103. * Dumps values.
  1104. *
  1105. * @param mixed $value
  1106. * @param bool $interpolate
  1107. *
  1108. * @return string
  1109. *
  1110. * @throws RuntimeException
  1111. */
  1112. private function dumpValue($value, $interpolate = true)
  1113. {
  1114. if (is_array($value)) {
  1115. $code = array();
  1116. foreach ($value as $k => $v) {
  1117. $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
  1118. }
  1119. return sprintf('array(%s)', implode(', ', $code));
  1120. } elseif ($value instanceof Definition) {
  1121. if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
  1122. return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
  1123. }
  1124. if (count($value->getMethodCalls()) > 0) {
  1125. throw new RuntimeException('Cannot dump definitions which have method calls.');
  1126. }
  1127. if (null !== $value->getConfigurator()) {
  1128. throw new RuntimeException('Cannot dump definitions which have a configurator.');
  1129. }
  1130. $arguments = array();
  1131. foreach ($value->getArguments() as $argument) {
  1132. $arguments[] = $this->dumpValue($argument);
  1133. }
  1134. if (null !== $value->getFactory()) {
  1135. $factory = $value->getFactory();
  1136. if (is_string($factory)) {
  1137. return sprintf('\\%s(%s)', $factory, implode(', ', $arguments));
  1138. }
  1139. if (is_array($factory)) {
  1140. if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) {
  1141. throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a'));
  1142. }
  1143. if (is_string($factory[0])) {
  1144. return sprintf('%s::%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory[0])), $factory[1], implode(', ', $arguments));
  1145. }
  1146. if ($factory[0] instanceof Definition) {
  1147. return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
  1148. }
  1149. if ($factory[0] instanceof Reference) {
  1150. return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments));
  1151. }
  1152. }
  1153. throw new RuntimeException('Cannot dump definition because of invalid factory');
  1154. }
  1155. if (null !== $value->getFactoryMethod(false)) {
  1156. if (null !== $value->getFactoryClass(false)) {
  1157. return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass(false)), $value->getFactoryMethod(false), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
  1158. } elseif (null !== $value->getFactoryService(false)) {
  1159. $service = $this->dumpValue($value->getFactoryService(false));
  1160. return sprintf('%s->%s(%s)', 0 === strpos($service, '$') ? sprintf('$this->get(%s)', $service) : $this->getServiceCall($value->getFactoryService(false)), $value->getFactoryMethod(false), implode(', ', $arguments));
  1161. } else {
  1162. throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
  1163. }
  1164. }
  1165. $class = $value->getClass();
  1166. if (null === $class) {
  1167. throw new RuntimeException('Cannot dump definitions which have no class nor factory.');
  1168. }
  1169. return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments));
  1170. } elseif ($value instanceof Variable) {
  1171. return '$'.$value;
  1172. } elseif ($value instanceof Reference) {
  1173. if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
  1174. return $this->dumpValue($this->referenceVariables[$id], $interpolate);
  1175. }
  1176. return $this->getServiceCall((string) $value, $value);
  1177. } elseif ($value instanceof Expression) {
  1178. return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container'));
  1179. } elseif ($value instanceof Parameter) {
  1180. return $this->dumpParameter($value);
  1181. } elseif (true === $interpolate && is_string($value)) {
  1182. if (preg_match('/^%([^%]+)%$/', $value, $match)) {
  1183. // we do this to deal with non string values (Boolean, integer, ...)
  1184. // the preg_replace_callback converts them to strings
  1185. return $this->dumpParameter(strtolower($match[1]));
  1186. } else {
  1187. $that = $this;
  1188. $replaceParameters = function ($match) use ($that) {
  1189. return "'.".$that->dumpParameter(strtolower($match[2])).".'";
  1190. };
  1191. $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
  1192. return $code;
  1193. }
  1194. } elseif (is_object($value) || is_resource($value)) {
  1195. throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
  1196. } else {
  1197. return $this->export($value);
  1198. }
  1199. }
  1200. /**
  1201. * Dumps a string to a literal (aka PHP Code) class value.
  1202. *
  1203. * @param string $class
  1204. *
  1205. * @return string
  1206. *
  1207. * @throws RuntimeException
  1208. */
  1209. private function dumpLiteralClass($class)
  1210. {
  1211. if (false !== strpos($class, '$')) {
  1212. throw new RuntimeException('Cannot dump definitions which have a variable class name.');
  1213. }
  1214. 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)) {
  1215. throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a'));
  1216. }
  1217. return '\\'.substr(str_replace('\\\\', '\\', $class), 1, -1);
  1218. }
  1219. /**
  1220. * Dumps a parameter.
  1221. *
  1222. * @param string $name
  1223. *
  1224. * @return string
  1225. */
  1226. public function dumpParameter($name)
  1227. {
  1228. if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
  1229. return $this->dumpValue($this->container->getParameter($name), false);
  1230. }
  1231. return sprintf("\$this->getParameter('%s')", strtolower($name));
  1232. }
  1233. /**
  1234. * @deprecated since version 2.6.2, to be removed in 3.0.
  1235. * Use \Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider instead.
  1236. *
  1237. * @param ExpressionFunctionProviderInterface $provider
  1238. */
  1239. public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
  1240. {
  1241. @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6.2 and will be removed in 3.0. Use the Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider method instead.', E_USER_DEPRECATED);
  1242. $this->expressionLanguageProviders[] = $provider;
  1243. }
  1244. /**
  1245. * Gets a service call.
  1246. *
  1247. * @param string $id
  1248. * @param Reference $reference
  1249. *
  1250. * @return string
  1251. */
  1252. private function getServiceCall($id, Reference $reference = null)
  1253. {
  1254. if ('service_container' === $id) {
  1255. return '$this';
  1256. }
  1257. if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
  1258. return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
  1259. } else {
  1260. if ($this->container->hasAlias($id)) {
  1261. $id = (string) $this->container->getAlias($id);
  1262. }
  1263. return sprintf('$this->get(\'%s\')', $id);
  1264. }
  1265. }
  1266. /**
  1267. * Convert a service id to a valid PHP method name.
  1268. *
  1269. * @param string $id
  1270. *
  1271. * @return string
  1272. *
  1273. * @throws InvalidArgumentException
  1274. */
  1275. private function camelize($id)
  1276. {
  1277. $name = Container::camelize($id);
  1278. if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
  1279. throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id));
  1280. }
  1281. return $name;
  1282. }
  1283. /**
  1284. * Returns the next name to use.
  1285. *
  1286. * @return string
  1287. */
  1288. private function getNextVariableName()
  1289. {
  1290. $firstChars = self::FIRST_CHARS;
  1291. $firstCharsLength = strlen($firstChars);
  1292. $nonFirstChars = self::NON_FIRST_CHARS;
  1293. $nonFirstCharsLength = strlen($nonFirstChars);
  1294. while (true) {
  1295. $name = '';
  1296. $i = $this->variableCount;
  1297. if ('' === $name) {
  1298. $name .= $firstChars[$i % $firstCharsLength];
  1299. $i = (int) ($i / $firstCharsLength);
  1300. }
  1301. while ($i > 0) {
  1302. --$i;
  1303. $name .= $nonFirstChars[$i % $nonFirstCharsLength];
  1304. $i = (int) ($i / $nonFirstCharsLength);
  1305. }
  1306. ++$this->variableCount;
  1307. // check that the name is not reserved
  1308. if (in_array($name, $this->reservedVariables, true)) {
  1309. continue;
  1310. }
  1311. return $name;
  1312. }
  1313. }
  1314. private function getExpressionLanguage()
  1315. {
  1316. if (null === $this->expressionLanguage) {
  1317. if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
  1318. throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
  1319. }
  1320. $providers = array_merge($this->container->getExpressionLanguageProviders(), $this->expressionLanguageProviders);
  1321. $this->expressionLanguage = new ExpressionLanguage(null, $providers);
  1322. if ($this->container->isTrackingResources()) {
  1323. foreach ($providers as $provider) {
  1324. $this->container->addObjectResource($provider);
  1325. }
  1326. }
  1327. }
  1328. return $this->expressionLanguage;
  1329. }
  1330. private function exportTargetDirs()
  1331. {
  1332. return null === $this->targetDirRegex ? '' : <<<EOF
  1333. \$dir = __DIR__;
  1334. for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
  1335. \$this->targetDirs[\$i] = \$dir = dirname(\$dir);
  1336. }
  1337. EOF;
  1338. }
  1339. private function export($value)
  1340. {
  1341. if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) {
  1342. $prefix = $matches[0][1] ? var_export(substr($value, 0, $matches[0][1]), true).'.' : '';
  1343. $suffix = $matches[0][1] + strlen($matches[0][0]);
  1344. $suffix = isset($value[$suffix]) ? '.'.var_export(substr($value, $suffix), true) : '';
  1345. $dirname = '__DIR__';
  1346. if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) {
  1347. $dirname = sprintf('$this->targetDirs[%d]', $offset);
  1348. }
  1349. if ($prefix || $suffix) {
  1350. return sprintf('(%s%s%s)', $prefix, $dirname, $suffix);
  1351. }
  1352. return $dirname;
  1353. }
  1354. return var_export($value, true);
  1355. }
  1356. }