ControllerResolver.php 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. /*
  3. * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace JMS\DiExtraBundle\HttpKernel;
  18. use Metadata\ClassHierarchyMetadata;
  19. use JMS\DiExtraBundle\Metadata\ClassMetadata;
  20. use CG\Core\DefaultNamingStrategy;
  21. use CG\Proxy\Enhancer;
  22. use JMS\AopBundle\DependencyInjection\Compiler\PointcutMatchingPass;
  23. use JMS\DiExtraBundle\Generator\DefinitionInjectorGenerator;
  24. use JMS\DiExtraBundle\Generator\LookupMethodClassGenerator;
  25. use JMS\DiExtraBundle\DependencyInjection\Dumper\PhpDumper;
  26. use Metadata\MetadataFactory;
  27. use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass;
  28. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  29. use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass;
  30. use Symfony\Component\DependencyInjection\Parameter;
  31. use Symfony\Component\DependencyInjection\Reference;
  32. use Symfony\Component\DependencyInjection\Definition;
  33. use Symfony\Component\Config\Resource\FileResource;
  34. use Symfony\Component\DependencyInjection\ContainerBuilder;
  35. use Symfony\Component\Config\ConfigCache;
  36. use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver as BaseControllerResolver;
  37. class ControllerResolver extends BaseControllerResolver
  38. {
  39. protected function createController($controller)
  40. {
  41. if (false === $pos = strpos($controller, '::')) {
  42. $count = substr_count($controller, ':');
  43. if (2 == $count) {
  44. // controller in the a:b:c notation then
  45. $controller = $this->parser->parse($controller);
  46. $pos = strpos($controller, '::');
  47. } elseif (1 == $count) {
  48. // controller in the service:method notation
  49. list($service, $method) = explode(':', $controller);
  50. return array($this->container->get($service), $method);
  51. } else {
  52. throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller));
  53. }
  54. }
  55. $class = substr($controller, 0, $pos);
  56. $method = substr($controller, $pos+2);
  57. if (!class_exists($class)) {
  58. throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
  59. }
  60. $injector = $this->createInjector($class);
  61. $controller = call_user_func($injector, $this->container);
  62. if ($controller instanceof ContainerAwareInterface) {
  63. $controller->setContainer($this->container);
  64. }
  65. return array($controller, $method);
  66. }
  67. public function createInjector($class)
  68. {
  69. $filename = $this->container->getParameter('jms_di_extra.cache_dir').'/controller_injectors/'.str_replace('\\', '', $class).'.php';
  70. $cache = new ConfigCache($filename, $this->container->getParameter('kernel.debug'));
  71. if (!$cache->isFresh()) {
  72. $metadata = $this->container->get('jms_di_extra.metadata.metadata_factory')->getMetadataForClass($class);
  73. if (null === $metadata) {
  74. $metadata = new ClassHierarchyMetadata();
  75. $metadata->addClassMetadata(new ClassMetadata($class));
  76. }
  77. // If the cache warmer tries to warm up a service controller that uses
  78. // annotations, we need to bail out as this is handled by the service
  79. // container directly.
  80. if (null !== $metadata->getOutsideClassMetadata()->id
  81. && 0 !== strpos($metadata->getOutsideClassMetadata()->id, '_jms_di_extra.unnamed.service')) {
  82. return;
  83. }
  84. $this->prepareContainer($cache, $filename, $metadata, $class);
  85. }
  86. if ( ! class_exists($class.'__JMSInjector', false)) {
  87. require $filename;
  88. }
  89. return array($class.'__JMSInjector', 'inject');
  90. }
  91. private function prepareContainer($cache, $containerFilename, $metadata, $className)
  92. {
  93. $container = new ContainerBuilder();
  94. $container->setParameter('jms_aop.cache_dir', $this->container->getParameter('jms_di_extra.cache_dir'));
  95. $def = $container
  96. ->register('jms_aop.interceptor_loader', 'JMS\AopBundle\Aop\InterceptorLoader')
  97. ->addArgument(new Reference('service_container'))
  98. ->setPublic(false)
  99. ;
  100. // add resources
  101. $ref = $metadata->getOutsideClassMetadata()->reflection;
  102. while ($ref && false !== $filename = $ref->getFilename()) {
  103. $container->addResource(new FileResource($filename));
  104. $ref = $ref->getParentClass();
  105. }
  106. // add definitions
  107. $definitions = $this->container->get('jms_di_extra.metadata.converter')->convert($metadata);
  108. $serviceIds = $parameters = array();
  109. $controllerDef = array_pop($definitions);
  110. $container->setDefinition('controller', $controllerDef);
  111. foreach ($definitions as $id => $def) {
  112. $container->setDefinition($id, $def);
  113. }
  114. $this->generateLookupMethods($controllerDef, $metadata);
  115. $config = $container->getCompilerPassConfig();
  116. $config->setOptimizationPasses(array());
  117. $config->setRemovingPasses(array());
  118. $config->addPass(new ResolveDefinitionTemplatesPass());
  119. $config->addPass(new PointcutMatchingPass($this->container->get('jms_aop.pointcut_container')->getPointcuts()));
  120. $config->addPass(new InlineServiceDefinitionsPass());
  121. $container->compile();
  122. if (!file_exists($dir = dirname($containerFilename))) {
  123. if (false === @mkdir($dir, 0777, true)) {
  124. throw new \RuntimeException(sprintf('Could not create directory "%s".', $dir));
  125. }
  126. }
  127. static $generator;
  128. if (null === $generator) {
  129. $generator = new DefinitionInjectorGenerator();
  130. }
  131. $cache->write($generator->generate($container->getDefinition('controller'), $className), $container->getResources());
  132. }
  133. private function generateLookupMethods($def, $metadata)
  134. {
  135. $found = false;
  136. foreach ($metadata->classMetadata as $cMetadata) {
  137. if (!empty($cMetadata->lookupMethods)) {
  138. $found = true;
  139. break;
  140. }
  141. }
  142. if (!$found) {
  143. return;
  144. }
  145. $generator = new LookupMethodClassGenerator($metadata);
  146. $outerClass = $metadata->getOutsideClassMetadata()->reflection;
  147. if ($file = $def->getFile()) {
  148. $generator->setRequiredFile($file);
  149. }
  150. $enhancer = new Enhancer(
  151. $outerClass,
  152. array(),
  153. array(
  154. $generator,
  155. )
  156. );
  157. $filename = $this->container->getParameter('jms_di_extra.cache_dir').'/lookup_method_classes/'.str_replace('\\', '-', $outerClass->name).'.php';
  158. $enhancer->writeClass($filename);
  159. $def->setFile($filename);
  160. $def->setClass($enhancer->getClassName($outerClass));
  161. $def->addMethodCall('__jmsDiExtra_setContainer', array(new Reference('service_container')));
  162. }
  163. }