PhpDumper.php 55 KB

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