Drupal investigation

PhpDumper.php 55KB

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