2015-08-24 02:36:41 +01:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\DependencyInjection\Compiler ;
2017-10-18 02:54:44 +01:00
use Symfony\Component\Config\Resource\ClassExistenceResource ;
2016-03-12 20:10:13 +00:00
use Symfony\Component\DependencyInjection\Config\AutowireServiceResource ;
2015-08-24 02:36:41 +01:00
use Symfony\Component\DependencyInjection\ContainerBuilder ;
use Symfony\Component\DependencyInjection\Definition ;
2017-05-08 01:40:35 +01:00
use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException ;
2015-08-24 02:36:41 +01:00
use Symfony\Component\DependencyInjection\Exception\RuntimeException ;
2017-03-25 13:44:42 +00:00
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper ;
2017-02-26 17:31:03 +00:00
use Symfony\Component\DependencyInjection\TypedReference ;
2015-08-24 02:36:41 +01:00
/**
2017-03-21 15:17:00 +00:00
* Inspects existing service definitions and wires the autowired ones using the type hints of their classes .
2015-08-24 02:36:41 +01:00
*
* @ author Kévin Dunglas < dunglas @ gmail . com >
2017-03-21 15:17:00 +00:00
* @ author Nicolas Grekas < p @ tchwork . com >
2015-08-24 02:36:41 +01:00
*/
2017-01-31 12:10:56 +00:00
class AutowirePass extends AbstractRecursivePass
2015-08-24 02:36:41 +01:00
{
private $definedTypes = array ();
private $types ;
2016-02-22 00:27:26 +00:00
private $ambiguousServiceTypes = array ();
2017-04-03 16:01:14 +01:00
private $autowired = array ();
2017-04-06 22:54:33 +01:00
private $lastFailure ;
2017-05-08 01:40:35 +01:00
private $throwOnAutowiringException ;
private $autowiringExceptions = array ();
2017-10-23 16:00:45 +01:00
private $strictMode ;
2017-05-08 01:40:35 +01:00
/**
2017-09-22 18:54:05 +01:00
* @ param bool $throwOnAutowireException Errors can be retrieved via Definition :: getErrors ()
2017-05-08 01:40:35 +01:00
*/
public function __construct ( $throwOnAutowireException = true )
{
$this -> throwOnAutowiringException = $throwOnAutowireException ;
}
/**
2017-09-22 18:54:05 +01:00
* @ deprecated since version 3.4 , to be removed in 4.0 .
*
2017-05-08 01:40:35 +01:00
* @ return AutowiringFailedException []
*/
public function getAutowiringExceptions ()
{
2017-09-22 18:54:05 +01:00
@ trigger_error ( 'Calling AutowirePass::getAutowiringExceptions() is deprecated since Symfony 3.4 and will be removed in 4.0. Use Definition::getErrors() instead.' , E_USER_DEPRECATED );
2017-05-08 01:40:35 +01:00
return $this -> autowiringExceptions ;
}
2015-08-24 02:36:41 +01:00
/**
* { @ inheritdoc }
*/
public function process ( ContainerBuilder $container )
{
2017-05-08 01:40:35 +01:00
// clear out any possibly stored exceptions from before
$this -> autowiringExceptions = array ();
2017-10-23 16:00:45 +01:00
$this -> strictMode = $container -> hasParameter ( 'container.autowiring.strict_mode' ) && $container -> getParameter ( 'container.autowiring.strict_mode' );
2017-05-08 01:40:35 +01:00
2016-04-20 13:06:08 +01:00
try {
2017-01-17 20:04:53 +00:00
parent :: process ( $container );
2016-08-15 10:28:45 +01:00
} finally {
$this -> definedTypes = array ();
$this -> types = null ;
$this -> ambiguousServiceTypes = array ();
2017-04-04 08:26:27 +01:00
$this -> autowired = array ();
2016-04-20 13:06:08 +01:00
}
2015-08-24 02:36:41 +01:00
}
2016-03-12 20:10:13 +00:00
/**
* Creates a resource to help know if this service has changed .
*
* @ param \ReflectionClass $reflectionClass
*
* @ return AutowireServiceResource
2017-01-26 15:09:57 +00:00
*
* @ deprecated since version 3.3 , to be removed in 4.0 . Use ContainerBuilder :: getReflectionClass () instead .
2016-03-12 20:10:13 +00:00
*/
public static function createResourceForClass ( \ReflectionClass $reflectionClass )
{
2017-01-26 15:09:57 +00:00
@ trigger_error ( 'The ' . __METHOD__ . '() method is deprecated since version 3.3 and will be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.' , E_USER_DEPRECATED );
2016-03-12 20:10:13 +00:00
$metadata = array ();
2016-10-05 21:46:29 +01:00
foreach ( $reflectionClass -> getMethods ( \ReflectionMethod :: IS_PUBLIC ) as $reflectionMethod ) {
if ( ! $reflectionMethod -> isStatic ()) {
$metadata [ $reflectionMethod -> name ] = self :: getResourceMetadataForMethod ( $reflectionMethod );
}
2016-03-12 20:10:13 +00:00
}
return new AutowireServiceResource ( $reflectionClass -> name , $reflectionClass -> getFileName (), $metadata );
}
2015-08-24 02:36:41 +01:00
/**
2017-01-17 20:04:53 +00:00
* { @ inheritdoc }
2015-08-24 02:36:41 +01:00
*/
2017-01-17 20:04:53 +00:00
protected function processValue ( $value , $isRoot = false )
2017-05-08 01:40:35 +01:00
{
try {
return $this -> doProcessValue ( $value , $isRoot );
} catch ( AutowiringFailedException $e ) {
if ( $this -> throwOnAutowiringException ) {
throw $e ;
}
$this -> autowiringExceptions [] = $e ;
2017-09-22 18:54:05 +01:00
$this -> container -> getDefinition ( $this -> currentId ) -> addError ( $e -> getMessage ());
2017-05-08 01:40:35 +01:00
return parent :: processValue ( $value , $isRoot );
}
}
private function doProcessValue ( $value , $isRoot = false )
2015-08-24 02:36:41 +01:00
{
2017-04-06 09:48:39 +01:00
if ( $value instanceof TypedReference ) {
2017-05-05 12:31:04 +01:00
if ( $ref = $this -> getAutowiredReference ( $value , $value -> getRequiringClass () ? sprintf ( 'for "%s" in "%s"' , $value -> getType (), $value -> getRequiringClass ()) : '' )) {
2017-04-06 22:54:33 +01:00
return $ref ;
2017-02-26 17:31:03 +00:00
}
2017-04-16 18:27:11 +01:00
$this -> container -> log ( $this , $this -> createTypeNotFoundMessage ( $value , 'it' ));
2017-01-17 20:04:53 +00:00
}
2017-04-06 22:54:33 +01:00
$value = parent :: processValue ( $value , $isRoot );
if ( ! $value instanceof Definition || ! $value -> isAutowired () || $value -> isAbstract () || ! $value -> getClass ()) {
return $value ;
2017-04-04 18:35:51 +01:00
}
2017-07-18 12:08:22 +01:00
if ( ! $reflectionClass = $this -> container -> getReflectionClass ( $value -> getClass (), false )) {
$this -> container -> log ( $this , sprintf ( 'Skipping service "%s": Class or interface "%s" cannot be loaded.' , $this -> currentId , $value -> getClass ()));
2015-08-24 02:36:41 +01:00
2017-04-06 22:54:33 +01:00
return $value ;
2017-04-04 18:35:51 +01:00
}
2017-02-26 17:31:03 +00:00
2017-04-04 18:35:51 +01:00
$methodCalls = $value -> getMethodCalls ();
2016-01-30 09:38:42 +00:00
2017-05-31 14:45:25 +01:00
try {
$constructor = $this -> getConstructor ( $value , false );
} catch ( RuntimeException $e ) {
throw new AutowiringFailedException ( $this -> currentId , $e -> getMessage (), 0 , $e );
}
if ( $constructor ) {
2017-04-04 18:35:51 +01:00
array_unshift ( $methodCalls , array ( $constructor , $value -> getArguments ()));
}
2017-01-25 12:02:51 +00:00
2017-09-23 23:04:06 +01:00
$methodCalls = $this -> autowireCalls ( $reflectionClass , $methodCalls );
2017-01-25 12:02:51 +00:00
2017-04-04 18:35:51 +01:00
if ( $constructor ) {
list (, $arguments ) = array_shift ( $methodCalls );
2017-01-17 20:04:53 +00:00
2017-04-04 18:35:51 +01:00
if ( $arguments !== $value -> getArguments ()) {
$value -> setArguments ( $arguments );
2017-02-26 17:31:03 +00:00
}
2017-04-04 18:35:51 +01:00
}
2017-01-25 12:02:51 +00:00
2017-04-04 18:35:51 +01:00
if ( $methodCalls !== $value -> getMethodCalls ()) {
$value -> setMethodCalls ( $methodCalls );
2017-02-26 17:31:03 +00:00
}
2017-04-04 18:35:51 +01:00
2017-04-06 22:54:33 +01:00
return $value ;
2016-10-05 21:46:29 +01:00
}
/**
* @ param \ReflectionClass $reflectionClass
2017-09-23 23:04:06 +01:00
* @ param array $methodCalls
2017-01-25 12:02:51 +00:00
*
* @ return array
*/
2017-09-23 23:04:06 +01:00
private function autowireCalls ( \ReflectionClass $reflectionClass , array $methodCalls )
2017-01-25 12:02:51 +00:00
{
foreach ( $methodCalls as $i => $call ) {
list ( $method , $arguments ) = $call ;
2017-04-04 14:48:08 +01:00
if ( $method instanceof \ReflectionFunctionAbstract ) {
$reflectionMethod = $method ;
2017-01-25 12:02:51 +00:00
} else {
2017-04-04 18:35:51 +01:00
$reflectionMethod = $this -> getReflectionMethod ( new Definition ( $reflectionClass -> name ), $method );
2017-01-25 12:02:51 +00:00
}
2017-03-17 09:21:51 +00:00
$arguments = $this -> autowireMethod ( $reflectionMethod , $arguments );
2017-01-25 12:02:51 +00:00
if ( $arguments !== $call [ 1 ]) {
$methodCalls [ $i ][ 1 ] = $arguments ;
}
}
return $methodCalls ;
2016-10-05 21:46:29 +01:00
}
/**
2016-12-23 12:58:16 +00:00
* Autowires the constructor or a method .
2016-10-05 21:46:29 +01:00
*
2017-04-04 14:48:08 +01:00
* @ param \ReflectionFunctionAbstract $reflectionMethod
* @ param array $arguments
2017-01-25 12:02:51 +00:00
*
* @ return array The autowired arguments
2016-10-05 21:46:29 +01:00
*
2017-05-31 14:45:25 +01:00
* @ throws AutowiringFailedException
2016-10-05 21:46:29 +01:00
*/
2017-04-04 14:48:08 +01:00
private function autowireMethod ( \ReflectionFunctionAbstract $reflectionMethod , array $arguments )
2016-10-05 21:46:29 +01:00
{
2017-04-04 14:48:08 +01:00
$class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod -> class : $this -> currentId ;
2017-03-23 22:53:25 +00:00
$method = $reflectionMethod -> name ;
2017-04-04 08:47:41 +01:00
$parameters = $reflectionMethod -> getParameters ();
if ( method_exists ( 'ReflectionMethod' , 'isVariadic' ) && $reflectionMethod -> isVariadic ()) {
2017-04-03 16:01:14 +01:00
array_pop ( $parameters );
}
2017-03-17 09:21:51 +00:00
2017-04-03 16:01:14 +01:00
foreach ( $parameters as $index => $parameter ) {
2016-02-29 00:33:45 +00:00
if ( array_key_exists ( $index , $arguments ) && '' !== $arguments [ $index ]) {
2015-08-24 02:36:41 +01:00
continue ;
}
2017-03-25 13:44:42 +00:00
$type = ProxyHelper :: getTypeHint ( $reflectionMethod , $parameter , true );
2016-02-22 01:17:09 +00:00
2017-03-21 08:00:21 +00:00
if ( ! $type ) {
2017-04-04 08:47:41 +01:00
if ( isset ( $arguments [ $index ])) {
2015-08-24 02:36:41 +01:00
continue ;
}
2017-02-01 12:10:11 +00:00
// no default value? Then fail
2017-04-03 15:43:13 +01:00
if ( ! $parameter -> isDefaultValueAvailable ()) {
2017-07-21 00:10:01 +01:00
// For core classes, isDefaultValueAvailable() can
// be false when isOptional() returns true. If the
// argument *is* optional, allow it to be missing
if ( $parameter -> isOptional ()) {
continue ;
}
2017-05-08 01:40:35 +01:00
throw new AutowiringFailedException ( $this -> currentId , sprintf ( 'Cannot autowire service "%s": argument "$%s" of method "%s()" must have a type-hint or be given a value explicitly.' , $this -> currentId , $parameter -> name , $class !== $this -> currentId ? $class . '::' . $method : $method ));
2015-08-24 02:36:41 +01:00
}
2017-04-04 08:47:41 +01:00
// specifically pass the default value
$arguments [ $index ] = $parameter -> getDefaultValue ();
2015-08-24 02:36:41 +01:00
2017-02-01 12:10:11 +00:00
continue ;
}
2017-05-05 12:31:04 +01:00
if ( ! $value = $this -> getAutowiredReference ( $ref = new TypedReference ( $type , $type , ! $parameter -> isOptional () ? $class : '' ), 'for ' . sprintf ( 'argument "$%s" of method "%s()"' , $parameter -> name , $class . '::' . $method ))) {
2017-04-16 18:27:11 +01:00
$failureMessage = $this -> createTypeNotFoundMessage ( $ref , sprintf ( 'argument "$%s" of method "%s()"' , $parameter -> name , $class !== $this -> currentId ? $class . '::' . $method : $method ));
2017-03-21 15:17:00 +00:00
if ( $parameter -> isDefaultValueAvailable ()) {
$value = $parameter -> getDefaultValue ();
2017-04-06 22:54:33 +01:00
} elseif ( ! $parameter -> allowsNull ()) {
2017-05-08 01:40:35 +01:00
throw new AutowiringFailedException ( $this -> currentId , $failureMessage );
2015-08-24 02:36:41 +01:00
}
2017-03-21 15:17:00 +00:00
$this -> container -> log ( $this , $failureMessage );
2015-08-24 02:36:41 +01:00
}
2016-02-21 23:51:49 +00:00
$arguments [ $index ] = $value ;
2015-08-24 02:36:41 +01:00
}
2016-02-21 23:51:49 +00:00
2017-04-03 16:01:14 +01:00
if ( $parameters && ! isset ( $arguments [ ++ $index ])) {
while ( 0 <= -- $index ) {
$parameter = $parameters [ $index ];
if ( ! $parameter -> isDefaultValueAvailable () || $parameter -> getDefaultValue () !== $arguments [ $index ]) {
break ;
}
unset ( $arguments [ $index ]);
}
}
2016-02-21 23:51:49 +00:00
// it's possible index 1 was set, then index 0, then 2, etc
// make sure that we re-order so they're injected as expected
ksort ( $arguments );
2016-10-05 21:46:29 +01:00
2017-01-25 12:02:51 +00:00
return $arguments ;
2015-08-24 02:36:41 +01:00
}
2017-02-26 17:31:03 +00:00
/**
2017-04-06 09:48:39 +01:00
* @ return TypedReference | null A reference to the service matching the given type , if any
2017-02-26 17:31:03 +00:00
*/
2017-05-05 12:31:04 +01:00
private function getAutowiredReference ( TypedReference $reference , $deprecationMessage )
2017-02-26 17:31:03 +00:00
{
2017-04-06 22:54:33 +01:00
$this -> lastFailure = null ;
2017-04-06 09:48:39 +01:00
$type = $reference -> getType ();
2017-02-19 22:20:06 +00:00
2017-04-06 09:48:39 +01:00
if ( $type !== ( string ) $reference || ( $this -> container -> has ( $type ) && ! $this -> container -> findDefinition ( $type ) -> isAbstract ())) {
return $reference ;
2017-04-04 08:47:41 +01:00
}
2017-02-26 17:31:03 +00:00
if ( null === $this -> types ) {
$this -> populateAvailableTypes ();
}
2016-12-23 12:58:16 +00:00
2017-04-04 18:35:51 +01:00
if ( isset ( $this -> definedTypes [ $type ])) {
2017-04-06 09:48:39 +01:00
return new TypedReference ( $this -> types [ $type ], $type );
2016-12-23 12:58:16 +00:00
}
2017-10-23 16:00:45 +01:00
if ( ! $this -> strictMode && isset ( $this -> types [ $type ])) {
2017-05-05 12:31:04 +01:00
$message = 'Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won\'t be supported in version 4.0.' ;
if ( $aliasSuggestion = $this -> getAliasesSuggestionForType ( $type = $reference -> getType (), $deprecationMessage )) {
$message .= ' ' . $aliasSuggestion ;
} else {
$message .= sprintf ( ' You should %s the "%s" service to "%s" instead.' , isset ( $this -> types [ $this -> types [ $type ]]) ? 'alias' : 'rename (or alias)' , $this -> types [ $type ], $type );
}
@ trigger_error ( $message , E_USER_DEPRECATED );
2017-04-11 22:05:55 +01:00
return new TypedReference ( $this -> types [ $type ], $type );
}
2017-04-06 09:48:39 +01:00
if ( ! $reference -> canBeAutoregistered () || isset ( $this -> types [ $type ]) || isset ( $this -> ambiguousServiceTypes [ $type ])) {
return ;
2017-02-26 17:31:03 +00:00
}
2017-04-06 09:48:39 +01:00
if ( isset ( $this -> autowired [ $type ])) {
return $this -> autowired [ $type ] ? new TypedReference ( $this -> autowired [ $type ], $type ) : null ;
}
2017-10-23 16:00:45 +01:00
if ( ! $this -> strictMode ) {
return $this -> createAutowiredDefinition ( $type );
}
2016-12-23 12:58:16 +00:00
}
2015-08-24 02:36:41 +01:00
/**
* Populates the list of available types .
*/
private function populateAvailableTypes ()
{
$this -> types = array ();
foreach ( $this -> container -> getDefinitions () as $id => $definition ) {
$this -> populateAvailableType ( $id , $definition );
}
}
/**
* Populates the list of available types for a given definition .
*
* @ param string $id
* @ param Definition $definition
*/
private function populateAvailableType ( $id , Definition $definition )
{
2015-11-04 00:40:26 +00:00
// Never use abstract services
if ( $definition -> isAbstract ()) {
return ;
}
2017-02-01 11:51:46 +00:00
foreach ( $definition -> getAutowiringTypes ( false ) as $type ) {
2015-08-24 02:36:41 +01:00
$this -> definedTypes [ $type ] = true ;
$this -> types [ $type ] = $id ;
2017-02-19 22:13:25 +00:00
unset ( $this -> ambiguousServiceTypes [ $type ]);
2015-08-24 02:36:41 +01:00
}
2017-10-08 15:29:36 +01:00
if ( preg_match ( '/^\d+_[^~]++~[._a-zA-Z\d]{7}$/' , $id ) || $definition -> isDeprecated () || ! $reflectionClass = $this -> container -> getReflectionClass ( $definition -> getClass (), false )) {
2017-04-04 18:35:51 +01:00
return ;
2015-11-03 19:42:49 +00:00
}
2016-02-23 09:13:51 +00:00
foreach ( $reflectionClass -> getInterfaces () as $reflectionInterface ) {
$this -> set ( $reflectionInterface -> name , $id );
2015-08-24 02:36:41 +01:00
}
2016-02-23 09:13:51 +00:00
do {
$this -> set ( $reflectionClass -> name , $id );
} while ( $reflectionClass = $reflectionClass -> getParentClass ());
2015-08-24 02:36:41 +01:00
}
/**
* Associates a type and a service id if applicable .
*
* @ param string $type
* @ param string $id
*/
private function set ( $type , $id )
{
2016-02-22 00:27:26 +00:00
if ( isset ( $this -> definedTypes [ $type ])) {
2015-08-24 02:36:41 +01:00
return ;
}
2016-03-06 22:03:14 +00:00
// is this already a type/class that is known to match multiple services?
if ( isset ( $this -> ambiguousServiceTypes [ $type ])) {
2017-02-14 09:20:20 +00:00
$this -> ambiguousServiceTypes [ $type ][] = $id ;
2016-03-06 22:03:14 +00:00
return ;
}
2016-02-22 00:27:26 +00:00
// check to make sure the type doesn't match multiple services
2017-02-14 09:20:20 +00:00
if ( ! isset ( $this -> types [ $type ]) || $this -> types [ $type ] === $id ) {
$this -> types [ $type ] = $id ;
2015-08-24 02:36:41 +01:00
return ;
}
2017-02-14 09:20:20 +00:00
// 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 -> ambiguousServiceTypes [ $type ][] = $id ;
2015-08-24 02:36:41 +01:00
}
/**
* Registers a definition for the type if possible or throws an exception .
*
2017-04-04 08:47:41 +01:00
* @ param string $type
2015-08-24 02:36:41 +01:00
*
2017-04-06 09:48:39 +01:00
* @ return TypedReference | null A reference to the registered definition
2015-08-24 02:36:41 +01:00
*/
2017-04-04 08:47:41 +01:00
private function createAutowiredDefinition ( $type )
2015-08-24 02:36:41 +01:00
{
2017-07-18 12:08:22 +01:00
if ( ! ( $typeHint = $this -> container -> getReflectionClass ( $type , false )) || ! $typeHint -> isInstantiable ()) {
2017-02-26 17:31:03 +00:00
return ;
2015-08-24 02:36:41 +01:00
}
2017-01-17 20:04:53 +00:00
$currentId = $this -> currentId ;
2017-04-06 22:54:33 +01:00
$this -> currentId = $type ;
$this -> autowired [ $type ] = $argumentId = sprintf ( 'autowired.%s' , $type );
2017-04-04 18:35:51 +01:00
$argumentDefinition = new Definition ( $type );
2015-08-24 02:36:41 +01:00
$argumentDefinition -> setPublic ( false );
2017-01-17 20:04:53 +00:00
$argumentDefinition -> setAutowired ( true );
2015-08-24 02:36:41 +01:00
2017-03-21 15:17:00 +00:00
try {
2017-05-08 01:40:35 +01:00
$originalThrowSetting = $this -> throwOnAutowiringException ;
$this -> throwOnAutowiringException = true ;
2017-03-21 15:17:00 +00:00
$this -> processValue ( $argumentDefinition , true );
$this -> container -> setDefinition ( $argumentId , $argumentDefinition );
2017-05-08 01:40:35 +01:00
} catch ( AutowiringFailedException $e ) {
2017-04-04 08:47:41 +01:00
$this -> autowired [ $type ] = false ;
2017-04-06 22:54:33 +01:00
$this -> lastFailure = $e -> getMessage ();
$this -> container -> log ( $this , $this -> lastFailure );
2017-03-21 15:17:00 +00:00
return ;
} finally {
2017-05-08 01:40:35 +01:00
$this -> throwOnAutowiringException = $originalThrowSetting ;
2017-03-21 15:17:00 +00:00
$this -> currentId = $currentId ;
}
2017-08-03 10:15:05 +01:00
@ trigger_error ( sprintf ( 'Relying on service auto-registration for type "%s" is deprecated since version 3.4 and won\'t be supported in 4.0. Create a service named "%s" instead.' , $type , $type ), E_USER_DEPRECATED );
2017-07-29 14:29:10 +01:00
2017-03-23 22:53:25 +00:00
$this -> container -> log ( $this , sprintf ( 'Type "%s" has been auto-registered for service "%s".' , $type , $this -> currentId ));
2015-08-24 02:36:41 +01:00
2017-04-06 09:48:39 +01:00
return new TypedReference ( $argumentId , $type );
2015-08-24 02:36:41 +01:00
}
2017-04-16 18:27:11 +01:00
private function createTypeNotFoundMessage ( TypedReference $reference , $label )
2017-03-21 15:17:00 +00:00
{
2017-07-18 12:08:22 +01:00
if ( ! $r = $this -> container -> getReflectionClass ( $type = $reference -> getType (), false )) {
2017-10-18 02:54:44 +01:00
// either $type does not exist or a parent class does not exist
try {
$resource = new ClassExistenceResource ( $type , false );
// isFresh() will explode ONLY if a parent class/trait does not exist
$resource -> isFresh ( 0 );
$parentMsg = false ;
} catch ( \ReflectionException $e ) {
$parentMsg = $e -> getMessage ();
}
$message = sprintf ( 'has type "%s" but this class %s.' , $type , $parentMsg ? sprintf ( 'is missing a parent class (%s)' , $parentMsg ) : 'was not found' );
2017-03-19 11:22:02 +00:00
} else {
2017-04-04 18:35:51 +01:00
$message = $this -> container -> has ( $type ) ? 'this service is abstract' : 'no such service exists' ;
2017-04-16 18:27:11 +01:00
$message = sprintf ( 'references %s "%s" but %s.%s' , $r -> isInterface () ? 'interface' : 'class' , $type , $message , $this -> createTypeAlternatives ( $reference ));
2017-03-19 11:22:02 +00:00
}
2017-03-23 22:53:25 +00:00
2017-04-06 22:54:33 +01:00
$message = sprintf ( 'Cannot autowire service "%s": %s %s' , $this -> currentId , $label , $message );
if ( null !== $this -> lastFailure ) {
$message = $this -> lastFailure . " \n " . $message ;
$this -> lastFailure = null ;
}
return $message ;
2017-03-23 22:53:25 +00:00
}
2017-04-16 18:27:11 +01:00
private function createTypeAlternatives ( TypedReference $reference )
2017-03-23 22:53:25 +00:00
{
2017-05-05 12:31:04 +01:00
// try suggesting available aliases first
if ( $message = $this -> getAliasesSuggestionForType ( $type = $reference -> getType ())) {
return ' ' . $message ;
2017-05-05 12:31:04 +01:00
}
if ( isset ( $this -> ambiguousServiceTypes [ $type ])) {
$message = sprintf ( 'one of these existing services: "%s"' , implode ( '", "' , $this -> ambiguousServiceTypes [ $type ]));
} elseif ( isset ( $this -> types [ $type ])) {
$message = sprintf ( 'the existing "%s" service' , $this -> types [ $type ]);
} elseif ( $reference -> getRequiringClass () && ! $reference -> canBeAutoregistered ()) {
return ' It cannot be auto-registered because it is from a different root namespace.' ;
} else {
return ;
2017-03-21 15:17:00 +00:00
}
2017-05-05 12:31:04 +01:00
return sprintf ( ' You should maybe alias this %s to %s.' , class_exists ( $type , false ) ? 'class' : 'interface' , $message );
2017-03-21 15:17:00 +00:00
}
2017-01-26 15:09:57 +00:00
/**
* @ deprecated since version 3.3 , to be removed in 4.0 .
*/
2016-04-03 08:41:26 +01:00
private static function getResourceMetadataForMethod ( \ReflectionMethod $method )
2016-03-12 20:10:13 +00:00
{
$methodArgumentsMetadata = array ();
foreach ( $method -> getParameters () as $parameter ) {
try {
$class = $parameter -> getClass ();
} catch ( \ReflectionException $e ) {
// type-hint is against a non-existent class
$class = false ;
}
2017-01-22 14:49:28 +00:00
$isVariadic = method_exists ( $parameter , 'isVariadic' ) && $parameter -> isVariadic ();
2016-03-12 20:10:13 +00:00
$methodArgumentsMetadata [] = array (
'class' => $class ,
'isOptional' => $parameter -> isOptional (),
2017-01-22 14:49:28 +00:00
'defaultValue' => ( $parameter -> isOptional () && ! $isVariadic ) ? $parameter -> getDefaultValue () : null ,
2016-03-12 20:10:13 +00:00
);
}
return $methodArgumentsMetadata ;
}
2017-05-05 12:31:04 +01:00
private function getAliasesSuggestionForType ( $type , $extraContext = null )
{
$aliases = array ();
foreach ( class_parents ( $type ) + class_implements ( $type ) as $parent ) {
if ( $this -> container -> has ( $parent ) && ! $this -> container -> findDefinition ( $parent ) -> isAbstract ()) {
$aliases [] = $parent ;
}
}
$extraContext = $extraContext ? ' ' . $extraContext : '' ;
if ( 1 < $len = count ( $aliases )) {
$message = sprintf ( 'Try changing the type-hint%s to one of its parents: ' , $extraContext );
for ( $i = 0 , -- $len ; $i < $len ; ++ $i ) {
$message .= sprintf ( '%s "%s", ' , class_exists ( $aliases [ $i ], false ) ? 'class' : 'interface' , $aliases [ $i ]);
}
$message .= sprintf ( 'or %s "%s".' , class_exists ( $aliases [ $i ], false ) ? 'class' : 'interface' , $aliases [ $i ]);
return $message ;
}
if ( $aliases ) {
return sprintf ( 'Try changing the type-hint%s to "%s" instead.' , $extraContext , $aliases [ 0 ]);
}
}
2015-08-24 02:36:41 +01:00
}