namespace Symfony\Component\DependencyInjection\Compiler;
+use Symfony\Component\DependencyInjection\Config\AutowireServiceResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
private $reflectionClasses = array();
private $definedTypes = array();
private $types;
- private $notGuessableTypes = array();
+ private $ambiguousServiceTypes = array();
private $autowired = array();
/**
$this->completeDefinition($id, $definition);
}
}
- } catch (\Exception $e) {
- } catch (\Throwable $e) {
+ } finally {
+ spl_autoload_unregister($throwingAutoloader);
+
+ // Free memory and remove circular reference to container
+ $this->reflectionClasses = array();
+ $this->definedTypes = array();
+ $this->types = null;
+ $this->ambiguousServiceTypes = array();
+ $this->autowired = array();
}
+ }
- spl_autoload_unregister($throwingAutoloader);
+ /**
+ * Creates a resource to help know if this service has changed.
+ *
+ * @param \ReflectionClass $reflectionClass
+ *
+ * @return AutowireServiceResource
+ */
+ public static function createResourceForClass(\ReflectionClass $reflectionClass)
+ {
+ $metadata = array();
- // Free memory and remove circular reference to container
- $this->container = null;
- $this->reflectionClasses = array();
- $this->definedTypes = array();
- $this->types = null;
- $this->notGuessableTypes = array();
- $this->autowired = array();
+ if ($constructor = $reflectionClass->getConstructor()) {
+ $metadata['__construct'] = self::getResourceMetadataForMethod($constructor);
+ }
- if (isset($e)) {
- throw $e;
+ foreach (self::getSetters($reflectionClass) as $reflectionMethod) {
+ $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod);
}
+
+ return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata);
}
/**
*/
private function completeDefinition($id, Definition $definition)
{
- if ($definition->getFactory() || $definition->getFactoryClass(false) || $definition->getFactoryService(false)) {
+ if ($definition->getFactory()) {
throw new RuntimeException(sprintf('Service "%s" can use either autowiring or a factory, not both.', $id));
}
return;
}
- $this->container->addClassResource($reflectionClass);
+ if ($this->container->isTrackingResources()) {
+ $this->container->addResource(static::createResourceForClass($reflectionClass));
+ }
if (!$constructor = $reflectionClass->getConstructor()) {
return;
$this->populateAvailableTypes();
}
- if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) {
+ if (isset($this->types[$typeHint->name])) {
$value = new Reference($this->types[$typeHint->name]);
} else {
try {
foreach ($definition->getAutowiringTypes() as $type) {
$this->definedTypes[$type] = true;
$this->types[$type] = $id;
- unset($this->notGuessableTypes[$type]);
+ unset($this->ambiguousServiceTypes[$type]);
}
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
return;
}
- if (!isset($this->types[$type])) {
- $this->types[$type] = $id;
+ // is this already a type/class that is known to match multiple services?
+ if (isset($this->ambiguousServiceTypes[$type])) {
+ $this->ambiguousServiceTypes[$type][] = $id;
return;
}
- if ($this->types[$type] === $id) {
+ // check to make sure the type doesn't match multiple services
+ if (!isset($this->types[$type]) || $this->types[$type] === $id) {
+ $this->types[$type] = $id;
+
return;
}
- if (!isset($this->notGuessableTypes[$type])) {
- $this->notGuessableTypes[$type] = true;
- $this->types[$type] = (array) $this->types[$type];
+ // keep an array of all services matching this type
+ if (!isset($this->ambiguousServiceTypes[$type])) {
+ $this->ambiguousServiceTypes[$type] = array($this->types[$type]);
+ unset($this->types[$type]);
}
-
- $this->types[$type][] = $id;
+ $this->ambiguousServiceTypes[$type][] = $id;
}
/**
*/
private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
{
- if (isset($this->notGuessableTypes[$typeHint->name])) {
+ if (isset($this->ambiguousServiceTypes[$typeHint->name])) {
$classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
- $matchingServices = implode(', ', $this->types[$typeHint->name]);
+ $matchingServices = implode(', ', $this->ambiguousServiceTypes[$typeHint->name]);
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices));
}
return $this->reflectionClasses[$id] = $reflector;
}
+
+ /**
+ * @param \ReflectionClass $reflectionClass
+ *
+ * @return \ReflectionMethod[]
+ */
+ private static function getSetters(\ReflectionClass $reflectionClass)
+ {
+ foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
+ if (!$reflectionMethod->isStatic() && 1 === $reflectionMethod->getNumberOfParameters() && 0 === strpos($reflectionMethod->name, 'set')) {
+ yield $reflectionMethod;
+ }
+ }
+ }
+
+ private static function getResourceMetadataForMethod(\ReflectionMethod $method)
+ {
+ $methodArgumentsMetadata = array();
+ foreach ($method->getParameters() as $parameter) {
+ try {
+ $class = $parameter->getClass();
+ } catch (\ReflectionException $e) {
+ // type-hint is against a non-existent class
+ $class = false;
+ }
+
+ $isVariadic = method_exists($parameter, 'isVariadic') && $parameter->isVariadic();
+ $methodArgumentsMetadata[] = array(
+ 'class' => $class,
+ 'isOptional' => $parameter->isOptional(),
+ 'defaultValue' => ($parameter->isOptional() && !$isVariadic) ? $parameter->getDefaultValue() : null,
+ );
+ }
+
+ return $methodArgumentsMetadata;
+ }
}