-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Performance improvement: add reflection cache.
- The resolve_object() function has been decomposed into several small ones. - Add ignore error in the phpstan.neon.
- Loading branch information
Showing
3 changed files
with
57 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ | |
* Author Email: [email protected] | ||
* Author Site: https://wp-yoda.com/en/ | ||
* | ||
* Version: 0.2.2 | ||
* Version: 0.2.3 | ||
* Source Code: https://github.com/renakdup/simple-php-dic | ||
* | ||
* Licence: MIT License | ||
|
@@ -22,6 +22,7 @@ | |
use InvalidArgumentException; | ||
use ReflectionClass; | ||
use ReflectionException; | ||
use ReflectionParameter; | ||
|
||
use function array_key_exists; | ||
use function class_exists; | ||
|
@@ -89,6 +90,11 @@ class Container implements ContainerInterface { | |
*/ | ||
protected array $resolved = []; | ||
|
||
/** | ||
* @var ReflectionClass[] | ||
*/ | ||
protected array $reflection_cache = []; | ||
|
||
/** | ||
* Set service to the container. Allows to set configurable services | ||
* using factory "function () {}" as passed service. | ||
|
@@ -98,6 +104,7 @@ class Container implements ContainerInterface { | |
public function set( string $id, $service ): void { | ||
$this->services[ $id ] = $service; | ||
unset( $this->resolved[ $id ] ); | ||
unset( $this->reflection_cache[ $id ] ); | ||
} | ||
|
||
/** | ||
|
@@ -178,8 +185,9 @@ protected function resolve( string $id ) { | |
*/ | ||
protected function resolve_object( string $service ): object { | ||
try { | ||
$reflected_class = new ReflectionClass( $service ); | ||
$constructor = $reflected_class->getConstructor(); | ||
$reflected_class = $this->reflection_cache[ $service ] ?? new ReflectionClass( $service ); | ||
|
||
$constructor = $reflected_class->getConstructor(); | ||
|
||
if ( ! $constructor ) { | ||
return new $service(); | ||
|
@@ -191,24 +199,8 @@ protected function resolve_object( string $service ): object { | |
return new $service(); | ||
} | ||
|
||
$resolved_params = []; | ||
foreach ( $params as $param ) { | ||
if ( $param_class = $param->getClass() ) { | ||
$resolved_params[] = $this->get( $param_class->getName() ); | ||
continue; | ||
} | ||
|
||
$default_value = $param->getDefaultValue(); | ||
if ( ! $default_value && $default_value !== null ) { | ||
$message = 'Service "' . $reflected_class->getName() . '" could not be resolved,' . | ||
'because parameter of constructor "' . $param->getName() . '" has not default value.' . "\n" . | ||
"Stack trace: \n" . | ||
$this->get_stack_trace(); | ||
throw new ContainerException( $message ); | ||
} | ||
|
||
$resolved_params[] = $default_value; | ||
} | ||
$resolved_params = $this->resolve_parameters( $params ); | ||
|
||
} catch ( ReflectionException $e ) { | ||
throw new ContainerException( | ||
"Service '{$service}' could not be resolved due the reflection issue: '" . $e->getMessage() . "'\n" . | ||
|
@@ -220,14 +212,53 @@ protected function resolve_object( string $service ): object { | |
return new $service( ...$resolved_params ); | ||
} | ||
|
||
/** | ||
* @param ReflectionParameter[] $params | ||
* | ||
* @return mixed[] | ||
* @throws ContainerExceptionInterface | ||
* @throws ReflectionException | ||
*/ | ||
protected function resolve_parameters( array $params ): array { | ||
$resolved_params = []; | ||
foreach ( $params as $param ) { | ||
$resolved_params[] = $this->resolve_parameter( $param ); | ||
} | ||
|
||
return $resolved_params; | ||
} | ||
|
||
/** | ||
* @param ReflectionParameter $param | ||
* | ||
* @return mixed|object | ||
* @throws ContainerExceptionInterface | ||
* @throws ReflectionException | ||
*/ | ||
protected function resolve_parameter( ReflectionParameter $param ) { | ||
if ( $param_class = $param->getClass() ) { | ||
return $this->get( $param_class->getName() ); | ||
} | ||
|
||
if ( $param->isOptional() ) { | ||
return $param->getDefaultValue(); | ||
} | ||
|
||
// @phpstan-ignore-next-line - Cannot call method getName() on ReflectionClass|null. | ||
$message = "Parameter '{$param->getName()}' of '{$param->getDeclaringClass()->getName()}' cannot be resolved.\n" . | ||
"Stack trace: \n" . | ||
$this->get_stack_trace(); | ||
throw new ContainerException( $message ); | ||
} | ||
|
||
protected function get_stack_trace(): string { | ||
$stackTraceArray = debug_backtrace(); | ||
$stackTraceString = ''; | ||
|
||
foreach ( $stackTraceArray as $item ) { | ||
$file = $item['file'] ?? '[internal function]'; | ||
$line = $item['line'] ?? ''; | ||
$function = $item['function'] ?? ''; // @phpstan-ignore-line | ||
$function = $item['function'] ?? ''; // @phpstan-ignore-line - 'function' on array always exists and is not nullable. | ||
$class = $item['class'] ?? ''; | ||
$type = $item['type'] ?? ''; | ||
|
||
|