@@ -63,6 +63,9 @@ class PhpDumper extends Dumper
6363 private $ usedMethodNames ;
6464 private $ namespace ;
6565 private $ asFiles ;
66+ private $ hotPathTag ;
67+ private $ inlineRequires ;
68+ private $ inlinedRequires = array ();
6669
6770 /**
6871 * @var ProxyDumper
@@ -108,16 +111,21 @@ public function setProxyDumper(ProxyDumper $proxyDumper)
108111 public function dump (array $ options = array ())
109112 {
110113 $ this ->targetDirRegex = null ;
114+ $ this ->inlinedRequires = array ();
111115 $ options = array_merge (array (
112116 'class ' => 'ProjectServiceContainer ' ,
113117 'base_class ' => 'Container ' ,
114118 'namespace ' => '' ,
115119 'as_files ' => false ,
116120 'debug ' => true ,
121+ 'hot_path_tag ' => null ,
122+ 'inline_class_loader_parameter ' => 'container.dumper.inline_class_loader ' ,
117123 ), $ options );
118124
119125 $ this ->namespace = $ options ['namespace ' ];
120126 $ this ->asFiles = $ options ['as_files ' ];
127+ $ this ->hotPathTag = $ options ['hot_path_tag ' ];
128+ $ this ->inlineRequires = $ this ->container ->hasParameter ($ options ['inline_class_loader_parameter ' ]) && $ this ->container ->getParameter ($ options ['inline_class_loader_parameter ' ]);
121129 $ this ->initializeMethodNamesMap ($ options ['base_class ' ]);
122130
123131 $ this ->docStar = $ options ['debug ' ] ? '* ' : '' ;
@@ -214,6 +222,7 @@ class_alias(Container{$hash}::class, {$options['class']}::class, false);
214222 }
215223
216224 $ this ->targetDirRegex = null ;
225+ $ this ->inlinedRequires = array ();
217226
218227 $ unusedEnvs = array ();
219228 foreach ($ this ->container ->getEnvCounters () as $ env => $ use ) {
@@ -257,9 +266,13 @@ private function addServiceLocalTempVariables($cId, Definition $definition, arra
257266
258267 array_unshift ($ inlinedDefinitions , $ definition );
259268
269+ $ collectLineage = $ this ->inlineRequires && !($ this ->hotPathTag && $ definition ->hasTag ($ this ->hotPathTag ));
260270 $ isNonLazyShared = !$ this ->getProxyDumper ()->isProxyCandidate ($ definition ) && $ definition ->isShared ();
261- $ calls = $ behavior = array ();
271+ $ lineage = $ calls = $ behavior = array ();
262272 foreach ($ inlinedDefinitions as $ iDefinition ) {
273+ if ($ collectLineage && $ class = is_array ($ factory = $ iDefinition ->getFactory ()) && is_string ($ factory [0 ]) ? $ factory [0 ] : $ iDefinition ->getClass ()) {
274+ $ this ->collectLineage ($ class , $ lineage );
275+ }
263276 $ this ->getServiceCallsFromArguments ($ iDefinition ->getArguments (), $ calls , $ behavior , $ isNonLazyShared );
264277 $ isPreInstantiation = $ isNonLazyShared && $ iDefinition !== $ definition && !$ this ->hasReference ($ cId , $ iDefinition ->getMethodCalls (), true ) && !$ this ->hasReference ($ cId , $ iDefinition ->getProperties (), true );
265278 $ this ->getServiceCallsFromArguments ($ iDefinition ->getMethodCalls (), $ calls , $ behavior , $ isPreInstantiation );
@@ -274,6 +287,13 @@ private function addServiceLocalTempVariables($cId, Definition $definition, arra
274287 continue ;
275288 }
276289
290+ if ($ collectLineage && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $ behavior [$ id ] && $ this ->container ->has ($ id )
291+ && $ this ->isTrivialInstance ($ iDefinition = $ this ->container ->findDefinition ($ id ))
292+ && $ class = is_array ($ factory = $ iDefinition ->getFactory ()) && is_string ($ factory [0 ]) ? $ factory [0 ] : $ iDefinition ->getClass ()
293+ ) {
294+ $ this ->collectLineage ($ class , $ lineage );
295+ }
296+
277297 if ($ callCount > 1 ) {
278298 $ name = $ this ->getNextVariableName ();
279299 $ this ->referenceVariables [$ id ] = new Variable ($ name );
@@ -300,9 +320,48 @@ private function addServiceLocalTempVariables($cId, Definition $definition, arra
300320 $ code .= "\n" ;
301321 }
302322
323+ if ($ lineage && $ lineage = array_diff_key (array_flip ($ lineage ), $ this ->inlinedRequires )) {
324+ $ code = "\n" .$ code ;
325+
326+ foreach (array_reverse ($ lineage ) as $ file => $ class ) {
327+ $ code = sprintf (" require_once %s; \n" , $ file ).$ code ;
328+ }
329+ }
330+
303331 return $ code ;
304332 }
305333
334+ private function collectLineage ($ class , array &$ lineage )
335+ {
336+ if (isset ($ lineage [$ class ])) {
337+ return ;
338+ }
339+ if (!$ r = $ this ->container ->getReflectionClass ($ class )) {
340+ return ;
341+ }
342+ if ($ this ->container instanceof $ class ) {
343+ return ;
344+ }
345+ $ file = $ r ->getFileName ();
346+ if (!$ file || $ this ->doExport ($ file ) === $ exportedFile = $ this ->export ($ file )) {
347+ return ;
348+ }
349+
350+ if ($ parent = $ r ->getParentClass ()) {
351+ $ this ->collectLineage ($ parent ->name , $ lineage );
352+ }
353+
354+ foreach ($ r ->getInterfaces () as $ parent ) {
355+ $ this ->collectLineage ($ parent ->name , $ lineage );
356+ }
357+
358+ foreach ($ r ->getTraits () as $ parent ) {
359+ $ this ->collectLineage ($ parent ->name , $ lineage );
360+ }
361+
362+ $ lineage [$ class ] = substr ($ exportedFile , 1 , -1 );
363+ }
364+
306365 private function generateProxyClasses ()
307366 {
308367 $ definitions = $ this ->container ->getDefinitions ();
@@ -509,10 +568,15 @@ private function isTrivialInstance(Definition $definition)
509568 if (!$ v || ($ v instanceof Reference && 'service_container ' === (string ) $ v )) {
510569 continue ;
511570 }
571+ if ($ v instanceof Reference && $ this ->container ->has ($ id = (string ) $ v ) && $ this ->container ->findDefinition ($ id )->isSynthetic ()) {
572+ continue ;
573+ }
512574 if (!is_scalar ($ v ) || $ this ->dumpValue ($ v ) !== $ this ->dumpValue ($ v , false )) {
513575 return false ;
514576 }
515577 }
578+ } elseif ($ arg instanceof Reference && $ this ->container ->has ($ id = (string ) $ arg ) && $ this ->container ->findDefinition ($ id )->isSynthetic ()) {
579+ continue ;
516580 } elseif (!is_scalar ($ arg ) || $ this ->dumpValue ($ arg ) !== $ this ->dumpValue ($ arg , false )) {
517581 return false ;
518582 }
@@ -694,7 +758,7 @@ private function addService($id, Definition $definition, &$file = null)
694758 $ lazyInitialization = '' ;
695759 }
696760
697- $ asFile = $ this ->asFiles && $ definition ->isShared ();
761+ $ asFile = $ this ->asFiles && $ definition ->isShared () && !( $ this -> hotPathTag && $ definition -> hasTag ( $ this -> hotPathTag )) ;
698762 $ methodName = $ this ->generateMethodName ($ id );
699763 if ($ asFile ) {
700764 $ file = $ methodName .'.php ' ;
@@ -760,7 +824,7 @@ private function addServices()
760824 $ definitions = $ this ->container ->getDefinitions ();
761825 ksort ($ definitions );
762826 foreach ($ definitions as $ id => $ definition ) {
763- if ($ definition ->isSynthetic () || ($ this ->asFiles && $ definition ->isShared ())) {
827+ if ($ definition ->isSynthetic () || ($ this ->asFiles && $ definition ->isShared () && !( $ this -> hotPathTag && $ definition -> hasTag ( $ this -> hotPathTag )) )) {
764828 continue ;
765829 }
766830 if ($ definition ->isPublic ()) {
@@ -778,7 +842,7 @@ private function generateServiceFiles()
778842 $ definitions = $ this ->container ->getDefinitions ();
779843 ksort ($ definitions );
780844 foreach ($ definitions as $ id => $ definition ) {
781- if (!$ definition ->isSynthetic () && $ definition ->isShared ()) {
845+ if (!$ definition ->isSynthetic () && $ definition ->isShared () && !( $ this -> hotPathTag && $ definition -> hasTag ( $ this -> hotPathTag )) ) {
782846 $ code = $ this ->addService ($ id , $ definition , $ file );
783847 yield $ file => $ code ;
784848 }
@@ -899,6 +963,7 @@ public function __construct()
899963 $ code .= $ this ->asFiles ? $ this ->addFileMap () : '' ;
900964 $ code .= $ this ->addPrivateServices ();
901965 $ code .= $ this ->addAliases ();
966+ $ code .= $ this ->addInlineRequires ();
902967 $ code .= <<<'EOF'
903968 }
904969
@@ -1050,7 +1115,7 @@ private function addMethodMap()
10501115 $ definitions = $ this ->container ->getDefinitions ();
10511116 ksort ($ definitions );
10521117 foreach ($ definitions as $ id => $ definition ) {
1053- if (!$ definition ->isSynthetic () && (!$ this ->asFiles || !$ definition ->isShared ())) {
1118+ if (!$ definition ->isSynthetic () && (!$ this ->asFiles || !$ definition ->isShared () || ( $ this -> hotPathTag && $ definition -> hasTag ( $ this -> hotPathTag )) )) {
10541119 $ code .= ' ' .$ this ->export ($ id ).' => ' .$ this ->export ($ this ->generateMethodName ($ id )).", \n" ;
10551120 }
10561121 }
@@ -1069,7 +1134,7 @@ private function addFileMap()
10691134 $ definitions = $ this ->container ->getDefinitions ();
10701135 ksort ($ definitions );
10711136 foreach ($ definitions as $ id => $ definition ) {
1072- if (!$ definition ->isSynthetic () && $ definition ->isShared ()) {
1137+ if (!$ definition ->isSynthetic () && $ definition ->isShared () && !( $ this -> hotPathTag && $ definition -> hasTag ( $ this -> hotPathTag )) ) {
10731138 $ code .= sprintf (" %s => __DIR__.'/%s.php', \n" , $ this ->export ($ id ), $ this ->generateMethodName ($ id ));
10741139 }
10751140 }
@@ -1137,6 +1202,38 @@ private function addAliases()
11371202 return $ code ." ); \n" ;
11381203 }
11391204
1205+ private function addInlineRequires ()
1206+ {
1207+ if (!$ this ->hotPathTag || !$ this ->inlineRequires ) {
1208+ return '' ;
1209+ }
1210+
1211+ $ lineage = array ();
1212+
1213+ foreach ($ this ->container ->findTaggedServiceIds ($ this ->hotPathTag ) as $ id => $ tags ) {
1214+ $ definition = $ this ->container ->getDefinition ($ id );
1215+ $ inlinedDefinitions = $ this ->getInlinedDefinitions ($ definition );
1216+ array_unshift ($ inlinedDefinitions , $ definition );
1217+
1218+ foreach ($ inlinedDefinitions as $ iDefinition ) {
1219+ if ($ class = is_array ($ factory = $ iDefinition ->getFactory ()) && is_string ($ factory [0 ]) ? $ factory [0 ] : $ iDefinition ->getClass ()) {
1220+ $ this ->collectLineage ($ class , $ lineage );
1221+ }
1222+ }
1223+ }
1224+
1225+ $ code = "\n" ;
1226+
1227+ foreach ($ lineage as $ file ) {
1228+ if (!isset ($ this ->inlinedRequires [$ file ])) {
1229+ $ this ->inlinedRequires [$ file ] = true ;
1230+ $ code .= sprintf (" require_once %s; \n" , $ file );
1231+ }
1232+ }
1233+
1234+ return "\n" === $ code ? '' : $ code ;
1235+ }
1236+
11401237 /**
11411238 * Adds default parameters method.
11421239 *
@@ -1408,7 +1505,7 @@ private function getServiceCallsFromArguments(array $arguments, array &$calls, a
14081505 $ id = (string ) $ argument ;
14091506
14101507 if (!isset ($ calls [$ id ])) {
1411- $ calls [$ id ] = (int ) $ isPreInstantiation ;
1508+ $ calls [$ id ] = (int ) ( $ isPreInstantiation && $ this -> container -> has ( $ id ) && ! $ this -> container -> findDefinition ( $ id )-> isSynthetic ()) ;
14121509 }
14131510 if (!isset ($ behavior [$ id ])) {
14141511 $ behavior [$ id ] = $ argument ->getInvalidBehavior ();
@@ -1746,17 +1843,15 @@ private function getServiceCall($id, Reference $reference = null)
17461843 return '$this ' ;
17471844 }
17481845
1749- if ($ this ->container ->hasDefinition ($ id )) {
1750- $ definition = $ this ->container ->getDefinition ($ id );
1751-
1846+ if ($ this ->container ->hasDefinition ($ id ) && ($ definition = $ this ->container ->getDefinition ($ id )) && !$ definition ->isSynthetic ()) {
17521847 if (null !== $ reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $ reference ->getInvalidBehavior ()) {
17531848 $ code = 'null ' ;
17541849 } elseif ($ this ->isTrivialInstance ($ definition )) {
17551850 $ code = substr ($ this ->addNewInstance ($ definition , '' , '' , $ id ), 8 , -2 );
17561851 if ($ definition ->isShared ()) {
17571852 $ code = sprintf ('$this->services[ \'%s \'] = %s ' , $ id , $ code );
1758
5C39
1853 }
1759- } elseif ($ this ->asFiles && $ definition ->isShared ()) {
1854+ } elseif ($ this ->asFiles && $ definition ->isShared () && !( $ this -> hotPathTag && $ definition -> hasTag ( $ this -> hotPathTag )) ) {
17601855 $ code = sprintf ("\$this->load(__DIR__.'/%s.php') " , $ this ->generateMethodName ($ id ));
17611856 } else {
17621857 $ code = sprintf ('$this->%s() ' , $ this ->generateMethodName ($ id ));
0 commit comments