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 ;
use Psr\Container\ContainerInterface as PsrContainerInterface ;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException ;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException ;
/**
* @ author Robin Chalas < robin . chalas @ gmail . com >
* @ author Nicolas Grekas < p @ tchwork . com >
*/
class ServiceLocator implements PsrContainerInterface
{
private $factories ;
2019-01-16 09:39:14 +00:00
private $loading = [];
2017-12-06 20:55:31 +00:00
private $externalId ;
private $container ;
2017-02-06 23:00:41 +00:00
/**
* @ param callable [] $factories
*/
public function __construct ( array $factories )
{
$this -> factories = $factories ;
}
/**
* { @ inheritdoc }
*/
public function has ( $id )
{
return isset ( $this -> factories [ $id ]);
}
/**
* { @ inheritdoc }
*/
public function get ( $id )
{
if ( ! isset ( $this -> factories [ $id ])) {
2019-01-16 09:39:14 +00:00
throw new ServiceNotFoundException ( $id , end ( $this -> loading ) ? : null , null , [], $this -> createServiceNotFoundMessage ( $id ));
2017-02-06 23:00:41 +00:00
}
2017-12-06 20:55:31 +00:00
if ( isset ( $this -> loading [ $id ])) {
$ids = array_values ( $this -> loading );
2018-07-26 09:45:46 +01:00
$ids = \array_slice ( $this -> loading , array_search ( $id , $ids ));
2017-12-06 20:55:31 +00:00
$ids [] = $id ;
throw new ServiceCircularReferenceException ( $id , $ids );
2017-02-06 23:00:41 +00:00
}
2017-12-06 20:55:31 +00:00
$this -> loading [ $id ] = $id ;
2017-03-16 18:01:53 +00:00
try {
2017-12-06 20:55:31 +00:00
return $this -> factories [ $id ]();
2017-03-16 18:01:53 +00:00
} finally {
2017-12-06 20:55:31 +00:00
unset ( $this -> loading [ $id ]);
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 ;
}
private function createServiceNotFoundMessage ( $id )
{
if ( $this -> loading ) {
return sprintf ( 'The service "%s" has a dependency on a non-existent service "%s". This locator %s' , end ( $this -> loading ), $id , $this -> formatAlternatives ());
}
$class = debug_backtrace ( DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS , 3 );
2018-07-26 09:45:46 +01:00
$class = isset ( $class [ 2 ][ 'object' ]) ? \get_class ( $class [ 2 ][ 'object' ]) : null ;
2017-12-06 20:55:31 +00:00
$externalId = $this -> externalId ? : $class ;
2019-01-16 09:39:14 +00:00
$msg = [];
2018-12-08 11:49:49 +00:00
$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-08 11:49:49 +00:00
return implode ( ' ' , $msg );
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
}