2017-02-06 23:00:41 +00: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 ;
2018-08-16 09:54:12 +01:00
use Psr\Container\ContainerExceptionInterface ;
2017-02-06 23:00:41 +00:00
use Psr\Container\ContainerInterface as PsrContainerInterface ;
2018-08-16 09:54:12 +01:00
use Psr\Container\NotFoundExceptionInterface ;
2018-07-04 14:24:49 +01:00
use Symfony\Component\DependencyInjection\Exception\RuntimeException ;
2017-02-06 23:00:41 +00:00
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException ;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException ;
2018-08-16 09:54:12 +01:00
use Symfony\Contracts\Service\ServiceLocatorTrait ;
use Symfony\Contracts\Service\ServiceSubscriberInterface ;
2017-02-06 23:00:41 +00:00
/**
* @ author Robin Chalas < robin . chalas @ gmail . com >
* @ author Nicolas Grekas < p @ tchwork . com >
*/
class ServiceLocator implements PsrContainerInterface
{
2018-08-16 09:54:12 +01:00
use ServiceLocatorTrait {
get as private doGet ;
2017-02-06 23:00:41 +00:00
}
2018-08-16 09:54:12 +01:00
private $externalId ;
private $container ;
2017-02-06 23:00:41 +00:00
public function get ( $id )
{
2018-08-16 09:54:12 +01:00
if ( ! $this -> externalId ) {
return $this -> doGet ( $id );
2017-02-06 23:00:41 +00:00
}
2017-03-16 18:01:53 +00:00
try {
2018-08-16 09:54:12 +01:00
return $this -> doGet ( $id );
2018-07-04 14:24:49 +01:00
} catch ( RuntimeException $e ) {
$what = sprintf ( 'service "%s" required by "%s"' , $id , $this -> externalId );
$message = preg_replace ( '/service "\.service_locator\.[^"]++"/' , $what , $e -> getMessage ());
if ( $e -> getMessage () === $message ) {
$message = sprintf ( 'Cannot resolve %s: %s' , $what , $message );
}
$r = new \ReflectionProperty ( $e , 'message' );
$r -> setAccessible ( true );
$r -> setValue ( $e , $message );
throw $e ;
2017-02-06 23:00:41 +00:00
}
}
public function __invoke ( $id )
{
return isset ( $this -> factories [ $id ]) ? $this -> get ( $id ) : null ;
}
2017-12-06 20:55:31 +00:00
/**
* @ internal
*/
public function withContext ( $externalId , Container $container )
{
$locator = clone $this ;
$locator -> externalId = $externalId ;
$locator -> container = $container ;
return $locator ;
}
2018-08-16 09:54:12 +01:00
private function createNotFoundException ( string $id ) : NotFoundExceptionInterface
2017-12-06 20:55:31 +00:00
{
if ( $this -> loading ) {
2018-08-16 09:54:12 +01:00
$msg = sprintf ( 'The service "%s" has a dependency on a non-existent service "%s". This locator %s' , end ( $this -> loading ), $id , $this -> formatAlternatives ());
return new ServiceNotFoundException ( $id , end ( $this -> loading ) ? : null , null , array (), $msg );
2017-12-06 20:55:31 +00:00
}
2018-08-16 09:54:12 +01:00
$class = debug_backtrace ( DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS , 4 );
$class = isset ( $class [ 3 ][ 'object' ]) ? \get_class ( $class [ 3 ][ 'object' ]) : null ;
2017-12-06 20:55:31 +00:00
$externalId = $this -> externalId ? : $class ;
2018-12-08 11:49:49 +00:00
$msg = array ();
$msg [] = sprintf ( 'Service "%s" not found:' , $id );
2017-12-06 20:55:31 +00:00
if ( ! $this -> container ) {
$class = null ;
} elseif ( $this -> container -> has ( $id ) || isset ( $this -> container -> getRemovedIds ()[ $id ])) {
2018-12-08 11:49:49 +00:00
$msg [] = 'even though it exists in the app\'s container,' ;
2017-12-06 20:55:31 +00:00
} else {
try {
$this -> container -> get ( $id );
$class = null ;
} catch ( ServiceNotFoundException $e ) {
if ( $e -> getAlternatives ()) {
2018-12-08 11:49:49 +00:00
$msg [] = sprintf ( 'did you mean %s? Anyway,' , $this -> formatAlternatives ( $e -> getAlternatives (), 'or' ));
2017-12-06 20:55:31 +00:00
} else {
$class = null ;
}
}
}
if ( $externalId ) {
2018-12-08 11:49:49 +00:00
$msg [] = sprintf ( 'the container inside "%s" is a smaller service locator that %s' , $externalId , $this -> formatAlternatives ());
2017-12-06 20:55:31 +00:00
} else {
2018-12-08 11:49:49 +00:00
$msg [] = sprintf ( 'the current service locator %s' , $this -> formatAlternatives ());
2017-12-06 20:55:31 +00:00
}
if ( ! $class ) {
// no-op
} elseif ( is_subclass_of ( $class , ServiceSubscriberInterface :: class )) {
2018-12-08 11:49:49 +00:00
$msg [] = sprintf ( 'Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".' , preg_replace ( '/([^\\\\]++\\\\)++/' , '' , $class ));
2017-12-06 20:55:31 +00:00
} else {
2018-12-08 11:49:49 +00:00
$msg [] = 'Try using dependency injection instead.' ;
2017-12-06 20:55:31 +00:00
}
2018-12-13 12:39:50 +00:00
return new ServiceNotFoundException ( $id , end ( $this -> loading ) ? : null , null , array (), implode ( ' ' , $msg ));
2018-08-16 09:54:12 +01:00
}
private function createCircularReferenceException ( string $id , array $path ) : ContainerExceptionInterface
{
return new ServiceCircularReferenceException ( $id , $path );
2017-12-06 20:55:31 +00:00
}
private function formatAlternatives ( array $alternatives = null , $separator = 'and' )
{
$format = '"%s"%s' ;
if ( null === $alternatives ) {
if ( ! $alternatives = array_keys ( $this -> factories )) {
return 'is empty...' ;
}
2018-07-26 09:45:46 +01:00
$format = sprintf ( 'only knows about the %s service%s.' , $format , 1 < \count ( $alternatives ) ? 's' : '' );
2017-12-06 20:55:31 +00:00
}
$last = array_pop ( $alternatives );
return sprintf ( $format , $alternatives ? implode ( '", "' , $alternatives ) : $last , $alternatives ? sprintf ( ' %s "%s"' , $separator , $last ) : '' );
}
2017-02-06 23:00:41 +00:00
}