Added callable reflection to `add_with_auto_arguments`
This allows the dependencies of the callable function to be automatically resolved out of the container too!
This commit is contained in:
parent
408295720c
commit
811ac747e6
|
@ -39,11 +39,11 @@ abstract class AbstractServiceProvider extends \League\Container\ServiceProvider
|
||||||
protected function add_with_auto_arguments( string $class_name, $concrete = null, bool $shared = false ) : DefinitionInterface {
|
protected function add_with_auto_arguments( string $class_name, $concrete = null, bool $shared = false ) : DefinitionInterface {
|
||||||
$definition = new Definition( $class_name, $concrete );
|
$definition = new Definition( $class_name, $concrete );
|
||||||
|
|
||||||
$constructor = $this->verify_class_and_concrete( $class_name, $concrete );
|
$function = $this->reflect_class_or_callable( $class_name, $concrete );
|
||||||
|
|
||||||
if ( ! is_null( $constructor ) ) {
|
if ( ! is_null( $function ) ) {
|
||||||
$constructor_arguments = $constructor->getParameters();
|
$arguments = $function->getParameters();
|
||||||
foreach ( $constructor_arguments as $argument ) {
|
foreach ( $arguments as $argument ) {
|
||||||
if ( $argument->isDefaultValueAvailable() ) {
|
if ( $argument->isDefaultValueAvailable() ) {
|
||||||
$default_value = $argument->getDefaultValue();
|
$default_value = $argument->getDefaultValue();
|
||||||
$definition->addArgument( new RawArgument( $default_value ) );
|
$definition->addArgument( new RawArgument( $default_value ) );
|
||||||
|
@ -72,10 +72,10 @@ abstract class AbstractServiceProvider extends \League\Container\ServiceProvider
|
||||||
* @param string $class_name The class name to check.
|
* @param string $class_name The class name to check.
|
||||||
* @param mixed $concrete The concrete to check.
|
* @param mixed $concrete The concrete to check.
|
||||||
*
|
*
|
||||||
* @return \ReflectionMethod|null A ReflectionMethod representing the class constructor, if $concrete is null or a class name; null otherwise.
|
* @return \ReflectionFunctionAbstract|null A reflection instance for the $class_name constructor or $concrete constructor or callable; null otherwise.
|
||||||
* @throws ContainerException Class has a private constructor, or can't reflect class, or the concrete is invalid.
|
* @throws ContainerException Class has a private constructor, can't reflect class, or the concrete is invalid.
|
||||||
*/
|
*/
|
||||||
private function verify_class_and_concrete( string $class_name, $concrete ) {
|
private function reflect_class_or_callable( string $class_name, $concrete ) {
|
||||||
if ( ! isset( $concrete ) || is_string( $concrete ) && class_exists( $concrete ) ) {
|
if ( ! isset( $concrete ) || is_string( $concrete ) && class_exists( $concrete ) ) {
|
||||||
try {
|
try {
|
||||||
$class = $concrete ?? $class_name;
|
$class = $concrete ?? $class_name;
|
||||||
|
@ -88,8 +88,12 @@ abstract class AbstractServiceProvider extends \League\Container\ServiceProvider
|
||||||
} catch ( \ReflectionException $ex ) {
|
} catch ( \ReflectionException $ex ) {
|
||||||
throw new ContainerException( "AbstractServiceProvider::add_with_auto_arguments: error when reflecting class '$class': {$ex->getMessage()}" );
|
throw new ContainerException( "AbstractServiceProvider::add_with_auto_arguments: error when reflecting class '$class': {$ex->getMessage()}" );
|
||||||
}
|
}
|
||||||
} elseif ( ! is_object( $concrete ) && ! is_callable( $concrete ) ) {
|
} elseif ( is_callable( $concrete ) ) {
|
||||||
throw new ContainerException( 'AbstractServiceProvider::add_with_auto_arguments: concrete must be a valid class name, function name, object, or callable.' );
|
try {
|
||||||
|
return new \ReflectionFunction( $concrete );
|
||||||
|
} catch ( \ReflectionException $ex ) {
|
||||||
|
throw new ContainerException( "AbstractServiceProvider::add_with_auto_arguments: error when reflecting callable: {$ex->getMessage()}" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -69,12 +69,13 @@ class AbstractServiceProviderTest extends \WC_Unit_Test_Case {
|
||||||
*/
|
*/
|
||||||
public static function setUpBeforeClass() {
|
public static function setUpBeforeClass() {
|
||||||
/**
|
/**
|
||||||
* Return a new instance of DependencyClass.
|
* Return a new instance of ClassWithDependencies.
|
||||||
*
|
*
|
||||||
* @return DependencyClass The new instance.
|
* @param DependencyClass $dependency The dependency to inject.
|
||||||
|
* @return ClassWithDependencies The new instance.
|
||||||
*/
|
*/
|
||||||
function get_new_dependency_class() {
|
function get_new_dependency_class( DependencyClass $dependency ) {
|
||||||
return new DependencyClass();
|
return new ClassWithDependencies( $dependency );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,21 +89,6 @@ class AbstractServiceProviderTest extends \WC_Unit_Test_Case {
|
||||||
$this->sut->add_with_auto_arguments( 'foobar' );
|
$this->sut->add_with_auto_arguments( 'foobar' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'add_with_auto_arguments' should throw an exception if the passed concrete is neither an existing class name, an existing function, an object, or a callback.
|
|
||||||
*
|
|
||||||
* @testWith ["foobar"]
|
|
||||||
* [1234]
|
|
||||||
*
|
|
||||||
* @param mixed $concrete The concrete to use to register the class.
|
|
||||||
*/
|
|
||||||
public function test_add_with_auto_arguments_throws_on_non_class_passed_as_concrete( $concrete ) {
|
|
||||||
$this->expectException( ContainerException::class );
|
|
||||||
$this->expectExceptionMessage( 'AbstractServiceProvider::add_with_auto_arguments: concrete must be a valid class name, function name, object, or callable.' );
|
|
||||||
|
|
||||||
$this->sut->add_with_auto_arguments( get_class( $this ), $concrete );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @testdox 'add_with_auto_arguments' should throw an exception if the passed class has a private constructor.
|
* @testdox 'add_with_auto_arguments' should throw an exception if the passed class has a private constructor.
|
||||||
*/
|
*/
|
||||||
|
@ -199,26 +185,29 @@ class AbstractServiceProviderTest extends \WC_Unit_Test_Case {
|
||||||
* @testdox 'add_with_auto_arguments' should properly register the supplied class when a concrete that is a closure is passed.
|
* @testdox 'add_with_auto_arguments' should properly register the supplied class when a concrete that is a closure is passed.
|
||||||
*/
|
*/
|
||||||
public function test_add_with_auto_arguments_works_as_expected_when_concrete_is_a_closure() {
|
public function test_add_with_auto_arguments_works_as_expected_when_concrete_is_a_closure() {
|
||||||
$callable = function() {
|
$this->container->share( DependencyClass::class );
|
||||||
return new DependencyClass();
|
$callable = function( DependencyClass $dependency ) {
|
||||||
|
return new ClassWithDependencies( $dependency );
|
||||||
};
|
};
|
||||||
|
|
||||||
$this->sut->add_with_auto_arguments( ClassWithDependencies::class, $callable );
|
$this->sut->add_with_auto_arguments( ClassWithDependencies::class, $callable );
|
||||||
|
|
||||||
$resolved = $this->container->get( ClassWithDependencies::class );
|
$resolved = $this->container->get( ClassWithDependencies::class );
|
||||||
|
|
||||||
$this->assertInstanceOf( DependencyClass::class, $resolved );
|
$this->assertInstanceOf( ClassWithDependencies::class, $resolved );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @testdox 'add_with_auto_arguments' should properly register the supplied class when a concrete that is a function name is passed.
|
* @testdox 'add_with_auto_arguments' should properly register the supplied class when a concrete that is a function name is passed.
|
||||||
*/
|
*/
|
||||||
public function test_add_with_auto_arguments_works_as_expected_when_concrete_is_a_function_name() {
|
public function test_add_with_auto_arguments_works_as_expected_when_concrete_is_a_function_name() {
|
||||||
|
$this->container->share( DependencyClass::class );
|
||||||
|
|
||||||
$this->sut->add_with_auto_arguments( ClassWithDependencies::class, __NAMESPACE__ . '\get_new_dependency_class' );
|
$this->sut->add_with_auto_arguments( ClassWithDependencies::class, __NAMESPACE__ . '\get_new_dependency_class' );
|
||||||
|
|
||||||
$resolved = $this->container->get( ClassWithDependencies::class );
|
$resolved = $this->container->get( ClassWithDependencies::class );
|
||||||
|
|
||||||
$this->assertInstanceOf( DependencyClass::class, $resolved );
|
$this->assertInstanceOf( ClassWithDependencies::class, $resolved );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue