2017-02-21 22:24:03 +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\Compiler ;
use Symfony\Component\DependencyInjection\ContainerInterface ;
use Symfony\Component\DependencyInjection\Definition ;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException ;
use Symfony\Component\DependencyInjection\Reference ;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface ;
use Symfony\Component\DependencyInjection\TypedReference ;
/**
* Compiler pass to register tagged services that require a service locator .
*
* @ author Nicolas Grekas < p @ tchwork . com >
*/
class RegisterServiceSubscribersPass extends AbstractRecursivePass
{
protected function processValue ( $value , $isRoot = false )
{
if ( ! $value instanceof Definition || $value -> isAbstract () || $value -> isSynthetic () || ! $value -> hasTag ( 'container.service_subscriber' )) {
return parent :: processValue ( $value , $isRoot );
}
$serviceMap = array ();
2017-04-04 18:35:51 +01:00
$autowire = $value -> isAutowired ();
2017-02-21 22:24:03 +00:00
foreach ( $value -> getTag ( 'container.service_subscriber' ) as $attributes ) {
if ( ! $attributes ) {
2017-04-04 18:35:51 +01:00
$autowire = true ;
2017-02-21 22:24:03 +00:00
continue ;
}
ksort ( $attributes );
if ( array () !== array_diff ( array_keys ( $attributes ), array ( 'id' , 'key' ))) {
throw new InvalidArgumentException ( sprintf ( 'The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "%s" given for service "%s".' , implode ( '", "' , array_keys ( $attributes )), $this -> currentId ));
}
if ( ! array_key_exists ( 'id' , $attributes )) {
throw new InvalidArgumentException ( sprintf ( 'Missing "id" attribute on "container.service_subscriber" tag with key="%s" for service "%s".' , $attributes [ 'key' ], $this -> currentId ));
}
if ( ! array_key_exists ( 'key' , $attributes )) {
$attributes [ 'key' ] = $attributes [ 'id' ];
}
if ( isset ( $serviceMap [ $attributes [ 'key' ]])) {
continue ;
}
$serviceMap [ $attributes [ 'key' ]] = new Reference ( $attributes [ 'id' ]);
}
$class = $value -> getClass ();
2018-01-30 21:38:46 +00:00
if ( ! $r = $this -> container -> getReflectionClass ( $class )) {
throw new InvalidArgumentException ( sprintf ( 'Class "%s" used for service "%s" cannot be found.' , $class , $this -> currentId ));
}
if ( ! $r -> isSubclassOf ( ServiceSubscriberInterface :: class )) {
2017-02-21 22:24:03 +00:00
throw new InvalidArgumentException ( sprintf ( 'Service "%s" must implement interface "%s".' , $this -> currentId , ServiceSubscriberInterface :: class ));
}
2018-01-30 21:38:46 +00:00
$class = $r -> name ;
2017-02-21 22:24:03 +00:00
$subscriberMap = array ();
2017-04-06 09:48:39 +01:00
$declaringClass = ( new \ReflectionMethod ( $class , 'getSubscribedServices' )) -> class ;
2017-02-21 22:24:03 +00:00
foreach ( $class :: getSubscribedServices () as $key => $type ) {
if ( ! is_string ( $type ) || ! preg_match ( '/^\??[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/' , $type )) {
2017-04-06 09:48:39 +01:00
throw new InvalidArgumentException ( sprintf ( '"%s::getSubscribedServices()" must return valid PHP types for service "%s" key "%s", "%s" returned.' , $class , $this -> currentId , $key , is_string ( $type ) ? $type : gettype ( $type )));
2017-02-21 22:24:03 +00:00
}
if ( $optionalBehavior = '?' === $type [ 0 ]) {
$type = substr ( $type , 1 );
$optionalBehavior = ContainerInterface :: IGNORE_ON_INVALID_REFERENCE ;
}
if ( is_int ( $key )) {
$key = $type ;
}
if ( ! isset ( $serviceMap [ $key ])) {
2017-04-04 18:35:51 +01:00
if ( ! $autowire ) {
2017-04-06 09:48:39 +01:00
throw new InvalidArgumentException ( sprintf ( 'Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by "%s::getSubscribedServices()".' , $this -> currentId , $key , $class ));
2017-04-04 18:35:51 +01:00
}
2017-02-21 22:24:03 +00:00
$serviceMap [ $key ] = new Reference ( $type );
}
2017-04-06 09:48:39 +01:00
$subscriberMap [ $key ] = new TypedReference (( string ) $serviceMap [ $key ], $type , $declaringClass , $optionalBehavior ? : ContainerInterface :: EXCEPTION_ON_INVALID_REFERENCE );
2017-02-21 22:24:03 +00:00
unset ( $serviceMap [ $key ]);
}
if ( $serviceMap = array_keys ( $serviceMap )) {
2017-03-27 19:07:18 +01:00
$message = sprintf ( 1 < count ( $serviceMap ) ? 'keys "%s" do' : 'key "%s" does' , str_replace ( '%' , '%%' , implode ( '", "' , $serviceMap )));
2017-04-06 09:48:39 +01:00
throw new InvalidArgumentException ( sprintf ( 'Service %s not exist in the map returned by "%s::getSubscribedServices()" for service "%s".' , $message , $class , $this -> currentId ));
2017-02-21 22:24:03 +00:00
}
2017-12-06 20:55:31 +00:00
$value -> addTag ( 'container.service_subscriber.locator' , array ( 'id' => ( string ) ServiceLocatorTagPass :: register ( $this -> container , $subscriberMap , $this -> currentId )));
2017-02-21 22:24:03 +00:00
2017-04-17 18:50:34 +01:00
return parent :: processValue ( $value );
2017-02-21 22:24:03 +00:00
}
}