diff --git a/Container.php b/Container.php new file mode 100644 index 0000000..c88b98d --- /dev/null +++ b/Container.php @@ -0,0 +1,454 @@ + Logger::class + ]; + + + /** + * @param string $id + * @return mixed + * @throws + */ + public function get(string $id): mixed + { + if ($id == ContainerInterface::class) { + return $this; + } + return $this->make($id, [], []); + } + + + /** + * @param $class + * @param array $constrict + * @param array $config + * @return mixed + * @throws + */ + public function make($class, array $constrict = [], array $config = []): mixed + { + if ($class == ContainerInterface::class) { + return $this; + } + if ($this->isInterface($class)) { + $class = $this->_interfaces[$class]; + } + if (!isset($this->_singletons[$class])) { + $this->_singletons[$class] = $this->resolve($class, $constrict, $config); + } + return $this->_singletons[$class]; + } + + + /** + * @param string $interface + * @param string $class + */ + public function mapping(string $interface, string $class) + { + $this->_interfaces[$interface] = $class; + } + + + /** + * @param $class + * @return bool + */ + public function isInterface($class): bool + { + $reflect = $this->getReflect($class); + if ($reflect->isInterface()) { + return true; + } + return false; + } + + + /** + * @param string $interface + * @param $object + */ + public function setBindings(string $interface, $object) + { + if (is_string($object)) { + $this->_interfaces[$interface] = $object; + } else { + $className = get_class($object); + $this->_interfaces[$interface] = $className; + $this->_singletons[$className] = $object; + } + } + + + /** + * @param $class + * @param array $constrict + * @param array $config + * @return object + * @throws + */ + public function create($class, array $constrict = [], array $config = []): object + { + return $this->resolve($class, $constrict, $config); + } + + + /** + * @param $class + * @param $constrict + * @param $config + * + * @return object + * @throws Exception + */ + private function resolve($class, $constrict, $config): object + { + $reflect = $this->resolveDependencies($class); + if (!$reflect->isInstantiable()) { + throw new ReflectionException('Class ' . $class . ' cannot be instantiated'); + } + + $object = $this->newInstance($reflect, $constrict); + + $this->propertyInject($reflect, $object); + + return $this->onAfterInit($object, $config); + } + + + /** + * @param ReflectionClass $reflect + * @param $dependencies + * @return object + * @throws ReflectionException + */ + private function newInstance(ReflectionClass $reflect, $dependencies): object + { + if (!isset($this->_constructs[$reflect->getName()])) { + return $reflect->newInstance(); + } + $construct = $this->_constructs[$reflect->getName()]; + if ($construct->getNumberOfParameters() < 1) { + return $reflect->newInstance(); + } + $parameters = $this->mergeParam($this->resolveMethodParameters($construct), $dependencies); + return $reflect->newInstanceArgs($parameters); + } + + + /** + * @param ReflectionClass $reflect + * @param $object + * @return mixed + * @throws Exception + */ + public function propertyInject(ReflectionClass $reflect, $object): mixed + { + foreach (NoteManager::getPropertyAnnotation($reflect) as $property => $inject) { + /** @var Inject $inject */ + $inject->execute($object, $property); + } + return $object; + } + + + /** + * @param $className + * @param $method + * @return array + */ + public function getMethodAttribute($className, $method = null): array + { + $methods = NoteManager::getMethodAnnotation($this->getReflect($className)); + if (!empty($method)) { + return $methods[$method] ?? []; + } + return $methods; + } + + + /** + * @param string $class + * @param string|null $property + * @return ReflectionProperty|ReflectionProperty[]|null + */ + public function getClassReflectionProperty(string $class, string $property = null): ReflectionProperty|null|array + { + $lists = NoteManager::getProperty($this->getReflect($class)); + if (empty($lists)) { + return null; + } + if (!empty($property)) { + return $lists[$property] ?? null; + } + return $lists; + } + + + /** + * @param $object + * @param $config + * @return mixed + */ + private function onAfterInit($object, $config): mixed + { + Kiri::configure($object, $config); + if (method_exists($object, 'init') && is_callable([$object, 'init'])) { + call_user_func([$object, 'init']); + } + return $object; + } + + + /** + * @param $class + * @return ReflectionClass + */ + private function resolveDependencies($class): ReflectionClass + { + if (isset($this->_reflection[$class])) { + return $this->_reflection[$class]; + } + $reflect = new ReflectionClass($class); + if ($reflect->isAbstract() || $reflect->isTrait() || $reflect->isInterface()) { + return $this->_reflection[$class] = $reflect; + } + $construct = NoteManager::resolveTarget($reflect); + if (!empty($construct) && $construct->getNumberOfParameters() > 0) { + $this->_constructs[$class] = $construct; + } + return $this->_reflection[$class] = $reflect; + } + + + /** + * @param ReflectionClass|string $class + * @return ReflectionMethod[] + * @throws ReflectionException + */ + public function getReflectMethods(ReflectionClass|string $class): array + { + if (is_string($class)) { + $class = $this->getReflect($class); + } + return NoteManager::getMethods($class); + } + + + /** + * @param ReflectionClass|string $class + * @param string $method + * @return ReflectionMethod|null + * @throws ReflectionException + */ + public function getReflectMethod(ReflectionClass|string $class, string $method): ?ReflectionMethod + { + return $this->getReflectMethods($class)[$method] ?? null; + } + + + /** + * @param string $className + * @param string $method + * @return array|null + * @throws ReflectionException + */ + public function getMethodParameters(string $className, string $method): ?array + { + if (isset($this->_parameters[$className]) && isset($this->_parameters[$className][$method])) { + return $this->_parameters[$className][$method]; + } + $reflectMethod = $this->getReflectMethod($this->getReflect($className), $method); + if (!($reflectMethod instanceof ReflectionMethod)) { + throw new ReflectionException("Class does not have a function $className::$method"); + } + $className = $reflectMethod->getDeclaringClass()->getName(); + if (isset($this->_parameters[$className]) && isset($this->_parameters[$className][$reflectMethod->getName()])) { + return $this->_parameters[$className][$reflectMethod->getName()]; + } + return $this->setParameters($className, $reflectMethod->getName(), $this->resolveMethodParameters($reflectMethod)); + } + + + /** + * @param $class + * @param $method + * @param $parameters + * @return mixed + */ + private function setParameters($class, $method, $parameters): mixed + { + if (!isset($this->_parameters[$class])) { + $this->_parameters[$class] = []; + } + return $this->_parameters[$class][$method] = $parameters; + } + + + /** + * @param Closure $reflectionMethod + * @return array + * @throws ReflectionException + */ + public function getFunctionParameters(Closure $reflectionMethod): array + { + return $this->resolveMethodParameters(new ReflectionFunction($reflectionMethod)); + } + + + /** + * @param ReflectionMethod|ReflectionFunction $reflectionMethod + * @return array + */ + private function resolveMethodParameters(ReflectionMethod|ReflectionFunction $reflectionMethod): array + { + if ($reflectionMethod->getNumberOfParameters() < 1) { + return []; + } + $params = []; + foreach ($reflectionMethod->getParameters() as $key => $parameter) { + if ($parameter->isDefaultValueAvailable()) { + $params[$key] = $parameter->getDefaultValue(); + } else if ($parameter->getType() === null) { + $params[$key] = $parameter->getType(); + } else { + $type = $parameter->getType()->getName(); + if (is_string($type) && class_exists($type) || isset($this->_interfaces[$type])) { + $type = Kiri::getDi()->get($type); + } + $params[$key] = match ($parameter->getType()) { + 'string' => '', + 'int', 'float' => 0, + '', null, 'object', 'mixed' => NULL, + 'bool' => false, + default => $type + }; + } + } + return $params; + } + + + /** + * @param $class + * @return ReflectionClass|null + */ + public function getReflect($class): ?ReflectionClass + { + if (!isset($this->_reflection[$class])) { + return $this->resolveDependencies($class); + } + return $this->_reflection[$class]; + } + + /** + * @param $class + */ + public function unset($class) + { + if (is_array($class) && isset($class['class'])) { + $class = $class['class']; + } else if (is_object($class)) { + $class = $class::class; + } + unset( + $this->_reflection[$class], $this->_singletons[$class], $this->_constructs[$class] + ); + } + + /** + * @return $this + */ + public function flush(): static + { + $this->_reflection = []; + $this->_singletons = []; + $this->_constructs = []; + return $this; + } + + /** + * @param $old + * @param $newParam + * + * @return mixed + */ + private function mergeParam($old, $newParam): array + { + if (empty($old)) { + return $newParam; + } else if (empty($newParam)) { + return $old; + } + foreach ($newParam as $key => $val) { + $old[$key] = $val; + } + return $old; + } + + /** + * @param string $id + * @return bool + */ + public function has(string $id): bool + { + return isset($this->_singletons[$id]) || isset($this->_interfaces[$id]); + } +} diff --git a/LocalService.php b/LocalService.php new file mode 100644 index 0000000..625e952 --- /dev/null +++ b/LocalService.php @@ -0,0 +1,88 @@ +_components[$name]); + + $this->_definition[$name] = $define; + if (is_object($define) || $define instanceof \Closure) { + $this->_components[$name] = $define; + } + } + + + /** + * @throws \Exception + */ + public function get(string $name, $throwException = true) + { + if (isset($this->_components[$name])) { + return $this->_components[$name]; + } + if (isset($this->_definition[$name])) { + $definition = $this->_definition[$name]; + if (is_object($definition) && !$definition instanceof \Closure) { + return $this->_components[$name] = $definition; + } + return $this->_components[$name] = Kiri::createObject($definition); + } else if ($throwException) { + throw new \Exception("Unknown component ID: $name"); + } + return null; + } + + + /** + * @param array $components + */ + public function setComponents(array $components) + { + foreach ($components as $name => $component) { + $this->set($name, $component); + } + } + + + /** + * @param $id + * @return bool + */ + public function has($id): bool + { + return isset($this->_components[$id]) || isset($this->_definition[$id]); + } + + + /** + * @param $id + */ + public function remove($id): void + { + unset($this->_components[$id], $this->_definition[$id]); + } + + +} diff --git a/NoteManager.php b/NoteManager.php new file mode 100644 index 0000000..6347232 --- /dev/null +++ b/NoteManager.php @@ -0,0 +1,303 @@ +getName(); + if (!isset(static::$_classTarget[$className])) { + static::$_classTarget[$className] = []; + } + foreach ($class->getAttributes() as $attribute) { + if (!class_exists($attribute->getName())) { + continue; + } + + $instance = $attribute->newInstance(); + + static::$_classTarget[$className][] = $instance; + + self::setMappingClass($attribute, $className); + } + } + + + /** + * @param ReflectionAttribute $attribute + * @param string $class + */ + public static function setMappingClass(ReflectionAttribute $attribute, string $class) + { + if (!isset(static::$_mapping[$attribute->getName()])) { + static::$_mapping[$attribute->getName()] = []; + } + if (!isset(static::$_mapping[$attribute->getName()][$class])) { + static::$_mapping[$attribute->getName()][$class] = []; + } + } + + + /** + * @param ReflectionAttribute $attribute + * @param string $class + * @param string $method + * @param mixed $instance + */ + public static function setMappingMethod(ReflectionAttribute $attribute, string $class, string $method, mixed $instance) + { + self::setMappingClass($attribute, $class); + + if (!isset(static::$_mapping[$attribute->getName()][$class]['method'])) { + static::$_mapping[$attribute->getName()][$class]['method'] = []; + } + static::$_mapping[$attribute->getName()][$class]['method'][] = [$method => $instance]; + } + + + /** + * @param ReflectionAttribute $attribute + * @param string $class + * @param string $property + * @param $instance + */ + public static function setMappingProperty(ReflectionAttribute $attribute, string $class, string $property, $instance) + { + self::setMappingClass($attribute, $class); + + $mapping = static::$_mapping[$attribute->getName()][$class]; + if (!isset($mapping['property'])) { + $mapping['property'] = []; + } + $mapping['property'][] = [$property => $instance]; + static::$_mapping[$attribute->getName()][$class] = $mapping; + } + + + /** + * @param mixed $class + * @return array + */ + public static function getTargetAnnotation(mixed $class): array + { + if (!is_string($class)) { + $class = $class::class; + } + return static::$_classTarget[$class] ?? []; + } + + + /** + * @param ReflectionClass $class + */ + public static function setMethodAnnotation(ReflectionClass $class) + { + $className = $class->getName(); + static::$_classMethodAnnotation[$className] = static::$_classMethod[$className] = []; + foreach ($class->getMethods() as $ReflectionMethod) { + static::$_classMethod[$className][$ReflectionMethod->getName()] = $ReflectionMethod; + static::$_classMethodAnnotation[$className][$ReflectionMethod->getName()] = []; + foreach ($ReflectionMethod->getAttributes() as $attribute) { + if (!class_exists($attribute->getName())) { + continue; + } + $instance = $attribute->newInstance(); + + static::$_classMethodAnnotation[$className][$ReflectionMethod->getName()][] = $instance; + + self::setMappingMethod($attribute, $className, $ReflectionMethod->getName(), $instance); + } + } + } + + + /** + * @param string $class + * @param string $method + * @return bool + */ + public static function hasMethod(string $class, string $method): bool + { + return isset(static::$_classMethod[$class]) && isset(static::$_classMethod[$class][$method]); + } + + + /** + * @param ReflectionClass $class + * @return array + */ + #[Pure] public static function getMethodAnnotation(ReflectionClass $class): array + { + return static::$_classMethodAnnotation[$class->getName()] ?? []; + } + + + /** + * @param \ReflectionClass $reflect + * @return \ReflectionMethod|null + */ + public static function resolveTarget(ReflectionClass $reflect): ?\ReflectionMethod + { + NoteManager::setPropertyAnnotation($reflect); + NoteManager::setTargetAnnotation($reflect); + NoteManager::setMethodAnnotation($reflect); + + return $reflect->getConstructor(); + } + + + /** + * @param ReflectionClass $class + */ + public static function setPropertyAnnotation(ReflectionClass $class) + { + $className = $class->getName(); + static::$_classProperty[$className] = static::$_classPropertyAnnotation[$className] = []; + foreach ($class->getProperties(ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_PUBLIC | + ReflectionProperty::IS_PROTECTED) as $ReflectionMethod) { + static::$_classProperty[$className][$ReflectionMethod->getName()] = $ReflectionMethod; + foreach ($ReflectionMethod->getAttributes() as $attribute) { + if (!class_exists($attribute->getName())) { + continue; + } + + $instance = $attribute->newInstance(); + + static::$_classPropertyAnnotation[$className][$ReflectionMethod->getName()] = $instance; + + self::setMappingProperty($attribute, $className, $ReflectionMethod->getName(), $instance); + } + } + } + + + /** + * @param string $attribute + * @param string|null $class + * @return array[] + */ + public static function getAttributeTrees(string $attribute, string $class = null): array + { + $mapping = static::$_mapping[$attribute] ?? []; + if (empty($mapping) || empty($class)) { + return $mapping; + } + return $mapping[$class] ?? []; + } + + + /** + * @param string $attribute + * @param string $class + * @param string|null $method + * @return array + */ + public static function getSpecify_annotation(string $attribute, string $class, string $method = null): mixed + { + $class = self::getAttributeTrees($attribute, $class); + if (empty($class) || !isset($class['method'])) { + return null; + } + if (empty($method)) { + return $class['method']; + } + foreach ($class['method'] as $value) { + $key = key($value); + if ($method == $key) { + return $value[$key]; + } + } + return null; + } + + + /** + * @param string $attribute + * @param string $class + * @param string $method + * @return mixed + */ + public static function getPropertyByAnnotation(string $attribute, string $class, string $method): mixed + { + $class = self::getAttributeTrees($attribute, $class); + if (empty($class) || !isset($class['property'])) { + return []; + } + foreach ($class['property'] as $value) { + $key = key($value); + if ($method == $key) { + return $value[$key]; + } + } + return null; + } + + + /** + * @param ReflectionClass|string $class + * @return array + * @throws \ReflectionException + */ + public static function getMethods(ReflectionClass|string $class): array + { + if (is_string($class)) { + $class = self::getReflect($class); + } + return static::$_classMethod[$class->getName()] ?? []; + } + + + /** + * @param ReflectionClass $class + * @return ReflectionProperty[] + */ + #[Pure] public static function getProperty(ReflectionClass $class): array + { + return static::$_classProperty[$class->getName()] ?? []; + } + + + /** + * @param ReflectionClass $class + * @return array + */ + #[Pure] public static function getPropertyAnnotation(ReflectionClass $class): array + { + return static::$_classPropertyAnnotation[$class->getName()] ?? []; + } + + +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a7d4f45 --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "game-worker/kiri-container", + "description": "kiri-grpc", + "authors": [ + { + "name": "XiangLin", + "email": "as2252258@163.com" + } + ], + "license": "MIT", + "require": { + "php": ">=8.0", + "ext-json": "*", + "grpc/grpc": "*", + "google/protobuf": "^3.3" + }, + "autoload": { + "psr-4": { + "Kiri\\Di\\": "./" + } + }, + "require-dev": { + } +}