|
Server : LiteSpeed System : Linux srv526460274 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : kerao9884 ( 1082) PHP Version : 8.0.30 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, Directory : /home/kera789.org/public_html/wp-content/plugins/amp/src/Infrastructure/Injector/ |
Upload File : |
<?php
/**
* Final class SimpleInjector.
*
* @package AmpProject\AmpWP
*/
namespace AmpProject\AmpWP\Infrastructure\Injector;
use AmpProject\AmpWP\Exception\FailedToMakeInstance;
use AmpProject\AmpWP\Infrastructure\Injector;
use AmpProject\AmpWP\Infrastructure\Instantiator;
use Exception;
use ReflectionClass;
use ReflectionParameter;
/**
* A simplified implementation of a dependency injector.
*
* @since 2.0
* @internal
*/
final class SimpleInjector implements Injector {
/**
* Special-case index key for handling globally defined named arguments.
*
* @var string
*/
const GLOBAL_ARGUMENTS = '__global__';
/** @var array<string> */
private $mappings = [];
/** @var array<object|null> */
private $shared_instances = [];
/** @var array<callable> */
private $delegates = [];
/** @var array[] */
private $argument_mappings = [
self::GLOBAL_ARGUMENTS => [],
];
/** @var Instantiator */
private $instantiator;
/**
* Instantiate a SimpleInjector object.
*
* @param Instantiator|null $instantiator Optional. Instantiator to use.
*/
public function __construct( ?Instantiator $instantiator = null ) {
$this->instantiator = null !== $instantiator
? $instantiator
: new FallbackInstantiator();
}
/**
* Make an object instance out of an interface or class.
*
* @param string $interface_or_class Interface or class to make an object
* instance out of.
* @param array $arguments Optional. Additional arguments to pass
* to the constructor. Defaults to an
* empty array.
* @return object Instantiated object.
*/
public function make( $interface_or_class, $arguments = [] ) {
$injection_chain = $this->resolve(
new InjectionChain(),
$interface_or_class
);
$class = $injection_chain->get_class();
if ( $this->has_shared_instance( $class ) ) {
return $this->get_shared_instance( $class );
}
if ( $this->has_delegate( $class ) ) {
$delegate = $this->get_delegate( $class );
$object = $delegate( $class );
} else {
$reflection = $this->get_class_reflection( $class );
$this->ensure_is_instantiable( $reflection );
$dependencies = $this->get_dependencies_for(
$injection_chain,
$reflection,
$arguments
);
$object = $this->instantiator->instantiate( $class, $dependencies );
}
if ( \array_key_exists( $class, $this->shared_instances ) ) {
$this->shared_instances[ $class ] = $object;
}
return $object;
}
/**
* Bind a given interface or class to an implementation.
*
* Note: The implementation can be an interface as well, as long as it can
* be resolved to an instantiatable class at runtime.
*
* @param string $from Interface or class to bind an implementation to.
* @param string $to Interface or class that provides the implementation.
* @return Injector
*/
public function bind( $from, $to ) {
$this->mappings[ $from ] = $to;
return $this;
}
/**
* Bind an argument for a class to a specific value.
*
* @param string $interface_or_class Interface or class to bind an argument
* for.
* @param string $argument_name Argument name to bind a value to.
* @param mixed $value Value to bind the argument to.
*
* @return Injector
*/
public function bind_argument(
$interface_or_class,
$argument_name,
$value
) {
$this->argument_mappings[ $interface_or_class ][ $argument_name ] = $value;
return $this;
}
/**
* Always reuse and share the same instance for the provided interface or
* class.
*
* @param string $interface_or_class Interface or class to reuse.
* @return Injector
*/
public function share( $interface_or_class ) {
$this->shared_instances[ $interface_or_class ] = null;
return $this;
}
/**
* Delegate instantiation of an interface or class to a callable.
*
* @param string $interface_or_class Interface or class to delegate the
* instantiation of.
* @param callable $callable Callable to use for instantiation.
* @return Injector
*/
public function delegate( $interface_or_class, callable $callable ) {
$this->delegates[ $interface_or_class ] = $callable;
return $this;
}
/**
* Make an object instance out of an interface or class.
*
* @param InjectionChain $injection_chain Injection chain to track
* resolutions.
* @param string $interface_or_class Interface or class to make an
* object instance out of.
* @return object Instantiated object.
*/
private function make_dependency(
InjectionChain $injection_chain,
$interface_or_class
) {
$injection_chain = $this->resolve(
$injection_chain,
$interface_or_class
);
$class = $injection_chain->get_class();
if ( $this->has_shared_instance( $class ) ) {
return $this->get_shared_instance( $class );
}
if ( $this->has_delegate( $class ) ) {
$delegate = $this->get_delegate( $class );
return $delegate( $class );
}
$reflection = $this->get_class_reflection( $class );
$this->ensure_is_instantiable( $reflection );
$dependencies = $this->get_dependencies_for(
$injection_chain,
$reflection
);
$object = $this->instantiator->instantiate( $class, $dependencies );
if ( \array_key_exists( $class, $this->shared_instances ) ) {
$this->shared_instances[ $class ] = $object;
}
return $object;
}
/**
* Recursively resolve an interface to the class it should be bound to.
*
* @param InjectionChain $injection_chain Injection chain to track
* resolutions.
* @param string $interface_or_class Interface or class to resolve.
* @return InjectionChain Modified Injection chain
* @throws FailedToMakeInstance If a circular reference was detected.
*/
private function resolve(
InjectionChain $injection_chain,
$interface_or_class
) {
if ( $injection_chain->is_in_chain( $interface_or_class ) ) {
// Circular reference detected, aborting.
throw FailedToMakeInstance::for_circular_reference(
$interface_or_class,
$injection_chain
);
}
$injection_chain = $injection_chain->add_resolution( $interface_or_class );
if ( \array_key_exists( $interface_or_class, $this->mappings ) ) {
return $this->resolve(
$injection_chain,
$this->mappings[ $interface_or_class ]
);
}
return $injection_chain->add_to_chain( $interface_or_class );
}
/**
* Get the array of constructor dependencies for a given reflected class.
*
* @param InjectionChain $injection_chain Injection chain to track
* resolutions.
* @param ReflectionClass $reflection Reflected class to get the
* dependencies for.
* @param array $arguments Associative array of directly
* provided arguments.
* @return array Array of dependencies that represent the arguments for the
* class' constructor.
*/
private function get_dependencies_for(
InjectionChain $injection_chain,
ReflectionClass $reflection,
$arguments = []
) {
$constructor = $reflection->getConstructor();
$class = $reflection->getName();
if ( null === $constructor ) {
return [];
}
return \array_map(
function ( ReflectionParameter $parameter ) use ( $injection_chain, $class, $arguments ) {
return $this->resolve_argument(
$injection_chain,
$class,
$parameter,
$arguments
);
},
$constructor->getParameters()
);
}
/**
* Ensure that a given reflected class is instantiable.
*
* @param ReflectionClass $reflection Reflected class to check.
* @return void
* @throws FailedToMakeInstance If the interface could not be resolved.
*/
private function ensure_is_instantiable( ReflectionClass $reflection ) {
if ( ! $reflection->isInstantiable() ) {
throw FailedToMakeInstance::for_unresolved_interface( $reflection->getName() );
}
}
/**
* Resolve a given reflected argument.
*
* @param InjectionChain $injection_chain Injection chain to track
* resolutions.
* @param string $class Name of the class to
* resolve the arguments for.
* @param ReflectionParameter $parameter Parameter to resolve.
* @param array $arguments Associative array of
* directly provided
* arguments.
* @return mixed Resolved value of the argument.
*/
private function resolve_argument(
InjectionChain $injection_chain,
$class,
ReflectionParameter $parameter,
$arguments
) {
if ( ! $parameter->hasType() ) {
return $this->resolve_argument_by_name(
$class,
$parameter,
$arguments
);
}
$type = $parameter->getType();
if ( null === $type ||
( is_a( $type, 'ReflectionType' ) && method_exists( $type, 'isBuiltin' ) && $type->isBuiltin() )
) {
return $this->resolve_argument_by_name(
$class,
$parameter,
$arguments
);
}
$type = $type instanceof \ReflectionNamedType
? $type->getName()
: (string) $type;
return $this->make_dependency( $injection_chain, $type );
}
/**
* Resolve a given reflected argument by its name.
*
* @param string $class Class to resolve the argument for.
* @param ReflectionParameter $parameter Argument to resolve by name.
* @param array $arguments Associative array of directly
* provided arguments.
* @return mixed Resolved value of the argument.
* @throws FailedToMakeInstance If the argument could not be resolved.
*/
private function resolve_argument_by_name(
$class,
ReflectionParameter $parameter,
$arguments
) {
$name = $parameter->getName();
// The argument was directly provided to the make() call.
if ( \array_key_exists( $name, $arguments ) ) {
return $arguments[ $name ];
}
// Check if we have mapped this argument for the specific class.
if ( \array_key_exists( $class, $this->argument_mappings )
&& \array_key_exists( $name, $this->argument_mappings[ $class ] ) ) {
$value = $this->argument_mappings[ $class ][ $name ];
// Closures are immediately resolved, to provide lazy resolution.
if ( is_callable( $value ) ) {
$value = $value( $class, $parameter, $arguments );
}
return $value;
}
// No argument found for the class, check if we have a global value.
if ( \array_key_exists( $name, $this->argument_mappings[ self::GLOBAL_ARGUMENTS ] ) ) {
return $this->argument_mappings[ self::GLOBAL_ARGUMENTS ][ $name ];
}
// No provided argument found, check if it has a default value.
try {
if ( $parameter->isDefaultValueAvailable() ) {
return $parameter->getDefaultValue();
}
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Just fall through into the FailedToMakeInstance exception.
}
// Out of options, fail with an exception.
throw FailedToMakeInstance::for_unresolved_argument( $name, $class );
}
/**
* Check whether a shared instance exists for a given class.
*
* @param string $class Class to check for a shared instance.
* @return bool Whether a shared instance exists.
*/
private function has_shared_instance( $class ) {
return \array_key_exists( $class, $this->shared_instances )
&& null !== $this->shared_instances[ $class ];
}
/**
* Get the shared instance for a given class.
*
* @param string $class Class to get the shared instance for.
* @return object Shared instance.
* @throws FailedToMakeInstance If an uninstantiated shared instance is
* requested.
*/
private function get_shared_instance( $class ) {
if ( ! $this->has_shared_instance( $class ) ) {
throw FailedToMakeInstance::for_uninstantiated_shared_instance( $class );
}
return (object) $this->shared_instances[ $class ];
}
/**
* Check whether a delegate exists for a given class.
*
* @param string $class Class to check for a delegate.
* @return bool Whether a delegate exists.
*/
private function has_delegate( $class ) {
return \array_key_exists( $class, $this->delegates );
}
/**
* Get the delegate for a given class.
*
* @param string $class Class to get the delegate for.
* @return callable Delegate.
* @throws FailedToMakeInstance If an invalid delegate is requested.
*/
private function get_delegate( $class ) {
if ( ! $this->has_delegate( $class ) ) {
throw FailedToMakeInstance::for_invalid_delegate( $class );
}
return $this->delegates[ $class ];
}
/**
* Get the reflection for a class or throw an exception.
*
* @param string $class Class to get the reflection for.
* @return ReflectionClass Class reflection.
* @throws FailedToMakeInstance If the class could not be reflected.
*/
private function get_class_reflection( $class ) {
try {
return new ReflectionClass( $class );
} catch ( Exception $exception ) {
throw FailedToMakeInstance::for_unreflectable_class( $class );
}
}
}