This commit is contained in:
xl
2023-05-25 16:59:17 +08:00
parent f7768bbe40
commit 75bae59d62
2 changed files with 424 additions and 379 deletions
+333 -290
View File
@@ -22,349 +22,392 @@ use ReflectionParameter;
class Container implements ContainerInterface class Container implements ContainerInterface
{ {
/** /**
* @var array * @var array
* *
* instance class by className * instance class by className
*/ */
private array $_singletons = []; private array $_singletons = [];
/** /**
* @var array * @var array
* *
* implements \ReflectClass * implements \ReflectClass
*/ */
private array $_reflection = []; private array $_reflection = [];
/** /**
* @var array * @var array
*/ */
private array $_parameters = []; private array $_parameters = [];
/** /**
* @var array * @var array
*/ */
private array $_interfaces = []; private array $_interfaces = [];
private static self|null $container = null; private static self|null $container = null;
private function __construct() private function __construct()
{ {
} }
/** /**
* @return static * @return static
*/ */
public static function instance(): static public static function instance(): static
{ {
if (static::$container === null) { if (static::$container === null) {
static::$container = new Container(); static::$container = new Container();
} }
return static::$container; return static::$container;
} }
/** /**
* @param string $id * @param string $id
* @return mixed * @return mixed
* @throws ReflectionException * @throws ReflectionException
* @throws \Exception * @throws \Exception
*/ */
public function get(string $id): mixed public function get(string $id): mixed
{ {
if ($id === ContainerInterface::class) { if ($id === ContainerInterface::class) {
return $this; return $this;
} }
if (!isset($this->_singletons[$id])) { if (!isset($this->_singletons[$id])) {
if (isset($this->_interfaces[$id])) { if (isset($this->_interfaces[$id])) {
$id = $this->_interfaces[$id]; $id = $this->_interfaces[$id];
} }
$this->_singletons[$id] = $this->make($id); $this->_singletons[$id] = $this->make($id);
if (!$this->_singletons[$id]) { if (!$this->_singletons[$id]) {
throw new \Exception('Class that cannot be instantiated。'); throw new \Exception('Class that cannot be instantiated。');
} }
} }
return $this->_singletons[$id]; return $this->_singletons[$id];
} }
/** /**
* @param string $id * @param string $id
* @return void * @return void
* @throws ReflectionException * @throws ReflectionException
*/ */
public function parse(string $id): void public function parse(string $id): void
{ {
if (isset($this->_singletons[$id])) { if (isset($this->_singletons[$id])) {
return; return;
} }
$this->make($id); $this->make($id);
} }
/** /**
* @param string $interface * @param string $interface
* @param string $class * @param string $class
* @return void * @return void
*/ */
public function set(string $interface, string $class): void public function set(string $interface, string $class): void
{ {
$this->_interfaces[$interface] = $class; $this->_interfaces[$interface] = $class;
} }
/** /**
* @param string $interface * @param string $interface
* @param object $object * @param object $object
* @return void * @return void
*/ */
public function bind(string $interface, object $object): void public function bind(string $interface, object $object): void
{ {
$this->_singletons[$interface] = $object; $this->_singletons[$interface] = $object;
} }
/** /**
* @param string $className * @param string $className
* @return ReflectionClass * @return ReflectionClass
* @throws ReflectionException * @throws ReflectionException
*/ */
public function getReflectionClass(string $className): ReflectionClass public function getReflectionClass(string $className): ReflectionClass
{ {
if (isset($this->_reflection[$className])) { if (isset($this->_reflection[$className])) {
return $this->_reflection[$className]; return $this->_reflection[$className];
} }
$class = new ReflectionClass($className); $class = new ReflectionClass($className);
return $this->_reflection[$className] = $class; return $this->_reflection[$className] = $class;
} }
/** /**
* @param string $className * @param string $className
* @param array $construct * @param array $construct
* @param array $config * @param array $config
* @return object|null * @return object|null
* @throws ReflectionException * @throws ReflectionException
*/ */
public function make(string $className, array $construct = [], array $config = []): ?object public function make(string $className, array $construct = [], array $config = []): ?object
{ {
$reflect = $this->getReflectionClass($className); $reflect = $this->getReflectionClass($className);
if (!$reflect->isInstantiable()) { if (!$reflect->isInstantiable()) {
throw new ReflectionException('Class ' . $className . ' cannot be instantiated'); throw new ReflectionException('Class ' . $className . ' cannot be instantiated');
} }
$constructorHandler = $reflect->getConstructor(); $constructorHandler = $reflect->getConstructor();
if (count($construct) < 1 && $constructorHandler !== null) { if (count($construct) < 1 && $constructorHandler !== null) {
$construct = $this->getMethodParams($constructorHandler); $construct = $this->getMethodParams($constructorHandler);
} }
$object = self::configure($reflect->newInstanceArgs($construct), $config); $object = self::configure($reflect->newInstanceArgs($construct), $config);
return $this->inject($object, $reflect); return $this->inject($object, $reflect);
} }
/** /**
* @param object $object * @param object $object
* @param ReflectionClass $reflect * @param ReflectionClass $reflect
* @return object * @return object
*/ */
private function inject(object $object, ReflectionClass $reflect): object private function inject(object $object, ReflectionClass $reflect): object
{ {
$targetAttributes = $reflect->getAttributes(); $targetAttributes = $reflect->getAttributes();
foreach ($targetAttributes as $attribute) { foreach ($targetAttributes as $attribute) {
if (!class_exists($attribute->getName())) { if (!class_exists($attribute->getName())) {
continue; continue;
} }
if ($object instanceof InjectProxyInterface) { if ($object instanceof InjectProxyInterface) {
$attribute->newInstance()->dispatch($reflect->getFileName(), $object); $attribute->newInstance()->dispatch($reflect->getFileName(), $object);
} else { } else {
$attribute->newInstance()->dispatch($object); $attribute->newInstance()->dispatch($object);
} }
} }
$this->resolveProperties($reflect, $object); $this->resolveProperties($reflect, $object);
if (method_exists($object, 'init') && $object::class !== 'Symfony\Component\Console\Application') { if (method_exists($object, 'init') && $object::class !== 'Symfony\Component\Console\Application') {
call_user_func([$object, 'init']); call_user_func([$object, 'init']);
} }
return $object; return $object;
} }
/** /**
* @param ReflectionClass $getReflectionClass * @param ReflectionClass $getReflectionClass
* @param object $class * @param object $class
* @return void * @return void
*/ */
public function resolveProperties(ReflectionClass $getReflectionClass, object $class): void public function resolveProperties(ReflectionClass $getReflectionClass, object $class): void
{ {
$properties = $getReflectionClass->getProperties(); $properties = $getReflectionClass->getProperties();
$filename = $getReflectionClass->getFileName(); $filename = $getReflectionClass->getFileName();
foreach ($properties as $property) { foreach ($properties as $property) {
$propertyAttributes = $property->getAttributes(); $propertyAttributes = $property->getAttributes();
foreach ($propertyAttributes as $attribute) { foreach ($propertyAttributes as $attribute) {
if (!class_exists($attribute->getName()) || if (!class_exists($attribute->getName()) ||
in_array(ValidatorInterface::class, class_implements($attribute->getName()))) { in_array(ValidatorInterface::class, class_implements($attribute->getName()))) {
continue; continue;
} }
if ($class instanceof InjectProxyInterface) { if ($class instanceof InjectProxyInterface) {
$attribute->newInstance()->dispatch($filename, $class, $property->getName()); $attribute->newInstance()->dispatch($filename, $class, $property->getName());
} else { } else {
$attribute->newInstance()->dispatch($class, $property->getName()); $attribute->newInstance()->dispatch($class, $property->getName());
} }
} }
} }
} }
/** /**
* @param string $className * @param string $className
* @param string $method * @param string $method
* @return ReflectionMethod * @return ReflectionMethod
* @throws ReflectionException * @throws ReflectionException
*/ */
public function getMethod(string $className, string $method): ReflectionMethod public function getMethod(string $className, string $method): ReflectionMethod
{ {
$reflection = $this->getReflectionClass($className); $reflection = $this->getReflectionClass($className);
return $reflection->getMethod($method); return $reflection->getMethod($method);
} }
/** /**
* @param string $className * @param string $className
* @return ReflectionMethod[] * @return ReflectionMethod[]
* @throws ReflectionException * @throws ReflectionException
*/ */
public function getMethods(string $className): array public function getMethods(string $className): array
{ {
$reflection = $this->getReflectionClass($className); $reflection = $this->getReflectionClass($className);
return $reflection->getMethods(); return $reflection->getMethods();
} }
/** /**
* @param ReflectionMethod $parameters * @param ReflectionMethod $parameters
* @return array * @return array
* @throws ReflectionException * @throws ReflectionException
*/ */
public function getMethodParams(ReflectionMethod $parameters): array public function getMethodParams(ReflectionMethod $parameters): array
{ {
$className = $parameters->getDeclaringClass()->getName(); $className = $parameters->getDeclaringClass()->getName();
$methodName = $parameters->getName(); $methodName = $parameters->getName();
if (!isset($this->_parameters[$className])) { if (!isset($this->_parameters[$className])) {
return $this->_parameters[$className][$methodName] = $this->resolveMethodParams($parameters); return $this->_parameters[$className][$methodName] = $this->resolveMethodParams($parameters);
} }
if (!isset($this->_parameters[$className][$methodName])) { if (!isset($this->_parameters[$className][$methodName])) {
$this->_parameters[$className][$methodName] = $this->resolveMethodParams($parameters); $this->_parameters[$className][$methodName] = $this->resolveMethodParams($parameters);
} }
return $this->_parameters[$className][$methodName]; return $this->_parameters[$className][$methodName];
} }
/** /**
* @param \Closure $parameters * @param \Closure $parameters
* @return array * @return array
* @throws ReflectionException * @throws ReflectionException
*/ */
public function getFunctionParams(\Closure $parameters): array public function getFunctionParams(\Closure $parameters): array
{ {
return $this->resolveMethodParams(new ReflectionFunction($parameters)); return $this->resolveMethodParams(new ReflectionFunction($parameters));
} }
/** // public function resolveMethodParams(ReflectionMethod|ReflectionFunction $parameters): array
* @param ReflectionMethod|ReflectionFunction $parameters // {
* @return array // $params = [];
* @throws ReflectionException // $numOfParameters = $parameters->getNumberOfParameters();
*/ // if ($numOfParameters < 1) {
public function resolveMethodParams(ReflectionMethod|ReflectionFunction $parameters): array // return $params;
{ // }
$params = []; //
if ($parameters->getNumberOfParameters() < 1) { // foreach ($parameters->getParameters() as $parameter) {
return $params; // $value = $this->getParameterValue($parameter);
} // $params[$parameter->getName()] = $value;
$parametersArray = $parameters->getParameters(); // }
//
$className = $parameters->getDeclaringClass()->getName(); // return $params;
foreach ($parametersArray as $parameter) { // }
$parameterAttributes = $parameter->getAttributes(); //
if (count($parameterAttributes) < 1) { // private function getParameterValue(ReflectionParameter $parameter)
if ($parameter->isDefaultValueAvailable()) { // {
$value = $parameter->getDefaultValue(); // $parameterAttributes = $parameter->getAttributes();
} else if ($parameter->getType() === null) { // if (count($parameterAttributes) > 0) {
$value = $parameter->getType(); // $attribute = $parameterAttributes[0]->newInstance();
} else { // return $attribute->dispatch($parameter->getDeclaringClass()->getName(), $parameter->getDeclaringFunction()->getName());
$value = $parameter->getType()->getName(); // }
if (class_exists($value) || interface_exists($value)) { //
$value = $this->get($value); // if ($parameter->isDefaultValueAvailable()) {
} else { // return $parameter->getDefaultValue();
$value = $this->getTypeValue($parameter); // }
} //
} // $type = $parameter->getType();
$params[$parameter->getName()] = $value; //
} else { // if ($type === null) {
$attribute = $parameterAttributes[0]->newInstance(); // return null;
// }
$params[$parameter->getName()] = $attribute->dispatch($className, $parameters->getName()); //
} // $value = $type->getName();
} // if (class_exists($value) || interface_exists($value)) {
return $params; // return $this->get($value);
} // }
//
// return $this->getTypeValue($parameter);
// }
/** /**
* @param ReflectionParameter $parameter * @param ReflectionMethod|ReflectionFunction $parameters
* @return string|int|bool|null * @return array
*/ * @throws ReflectionException
private function getTypeValue(ReflectionParameter $parameter): string|int|bool|null */
{ public function resolveMethodParams(ReflectionMethod|ReflectionFunction $parameters): array
return match ($parameter->getType()) { {
'string' => '', $params = [];
'int', 'float' => 0, if ($parameters->getNumberOfParameters() < 1) {
'', null, 'object', 'mixed' => NULL, return $params;
'bool' => false, }
'default' => null $parametersArray = $parameters->getParameters();
};
} $className = $parameters->getDeclaringClass()->getName();
foreach ($parametersArray as $parameter) {
$parameterAttributes = $parameter->getAttributes();
if (count($parameterAttributes) < 1) {
if ($parameter->isDefaultValueAvailable()) {
$value = $parameter->getDefaultValue();
} else if ($parameter->getType() === null) {
$value = $parameter->getType();
} else {
$value = $parameter->getType()->getName();
if (class_exists($value) || interface_exists($value)) {
$value = $this->get($value);
} else {
$value = $this->getTypeValue($parameter);
}
}
$params[$parameter->getName()] = $value;
} else {
$attribute = $parameterAttributes[0]->newInstance();
$params[$parameter->getName()] = $attribute->dispatch($className, $parameters->getName());
}
}
return $params;
}
/** /**
* @param object $object * @param ReflectionParameter $parameter
* @param array $config * @return string|int|bool|null
* @return object */
*/ private function getTypeValue(ReflectionParameter $parameter): string|int|bool|null
public static function configure(object $object, array $config): object {
{ return match ($parameter->getType()) {
foreach ($config as $key => $value) { 'string' => '',
if (!property_exists($object, $key)) { 'int', 'float' => 0,
continue; '', null, 'object', 'mixed' => NULL,
} 'bool' => false,
$object->{$key} = $value; 'default' => null
} };
return $object; }
}
/** /**
* @param string $id * @param object $object
* @return bool * @param array $config
*/ * @return object
public function has(string $id): bool */
{ public static function configure(object $object, array $config): object
// TODO: Implement has() method. {
return isset($this->_singletons[$id]) && isset($this->_reflection[$id]); foreach ($config as $key => $value) {
} if (!property_exists($object, $key)) {
continue;
}
$object->{$key} = $value;
}
return $object;
}
/**
* @param string $id
* @return bool
*/
public function has(string $id): bool
{
// TODO: Implement has() method.
return isset($this->_singletons[$id]) && isset($this->_reflection[$id]);
}
} }
+91 -89
View File
@@ -7,7 +7,7 @@ namespace Kiri\Di;
use Exception; use Exception;
use Kiri\Abstracts\Component; use Kiri\Abstracts\Component;
use Kiri\Abstracts\Config; use Kiri\Config\ConfigProvider;
use Kiri\Di\Inject\Skip; use Kiri\Di\Inject\Skip;
use ReflectionException; use ReflectionException;
@@ -15,104 +15,106 @@ class Scanner extends Component
{ {
/** /**
* @var array * @var array
*/ */
private array $files = []; private array $files = [];
/** /**
* @param string $path * @param string $path
* @return void * @return void
*/ */
public function read(string $path): void public function read(string $path): void
{ {
$this->load_dir($path); $this->load_dir($path);
} }
/** /**
* @param string $namespace * @param string $namespace
* @return void * @return void
* @throws ReflectionException * @throws ReflectionException
* @throws Exception * @throws Exception
*/ */
public function parse(string $namespace): void public function parse(string $namespace): void
{ {
$container = Container::instance(); $container = Container::instance();
foreach ($this->files as $file) { foreach ($this->files as $file) {
$class = $this->rename($file); $class = $this->rename($file);
if (!class_exists($class)) { if (!class_exists($class)) {
error('Please follow the PSR-4 specification to write code.' . $class); error('Please follow the PSR-4 specification to write code.' . $class);
continue; continue;
} }
$reflect = $container->getReflectionClass($class); $reflect = $container->getReflectionClass($class);
if ($reflect->isInstantiable()) { if ($reflect->isInstantiable()) {
$data = $reflect->getAttributes(Skip::class); $data = $reflect->getAttributes(Skip::class);
if (count($data) > 0) { if (count($data) > 0) {
continue; continue;
} }
$container->parse($class); $container->parse($class);
} }
} }
} }
/** /**
* @param string $file * @param string $file
* @return string * @return string
*/ */
private function rename(string $file): string private function rename(string $file): string
{ {
$filter = array_filter(explode('/', $file), function ($value) { $filter = array_filter(explode('/', $file), function ($value) {
if (empty($value)) { if (empty($value)) {
return false; return false;
} }
return ucfirst($value); return ucfirst($value);
}); });
return ucfirst(implode('\\', $filter)); return ucfirst(implode('\\', $filter));
} }
/** /**
* @param string $path * @param string $path
* @return void * @return void
*/ * @throws ReflectionException
private function load_dir(string $path): void */
{ private function load_dir(string $path): void
$dir = new \DirectoryIterator($path); {
$skip = Config::get('scanner.skip', []); $dir = new \DirectoryIterator($path);
foreach ($dir as $value) { $skip = \config('scanner.skip', []);
if ($value->isDot() || str_starts_with($value->getFilename(), '.')) { foreach ($dir as $value) {
continue; if ($value->isDot() || str_starts_with($value->getFilename(), '.')) {
} continue;
if ($value->isDir()) { }
if (in_array($value->getRealPath() . '/', $skip)) { if ($value->isDir()) {
continue; if (in_array($value->getRealPath() . '/', $skip)) {
} continue;
$this->load_dir($value->getRealPath()); }
} else if ($value->getExtension() == 'php') { $this->load_dir($value->getRealPath());
$this->load_file($value->getRealPath()); } else if ($value->getExtension() == 'php') {
} $this->load_file($value->getRealPath());
} }
} }
}
/** /**
* @param string $path * @param string $path
* @return void * @return void
*/ * @throws ReflectionException
private function load_file(string $path): void */
{ private function load_file(string $path): void
try { {
require_once "$path"; try {
$path = str_replace($_SERVER['PWD'], '', $path); require_once "$path";
$path = str_replace('.php', '', $path); $path = str_replace($_SERVER['PWD'], '', $path);
$this->files[] = $path; $path = str_replace('.php', '', $path);
} catch (\Throwable $throwable) { $this->files[] = $path;
error($throwable); } catch (\Throwable $throwable) {
} error($throwable);
} }
}
} }