From 2d1f83cf092bacbd75c9e5dc335ff5865e987916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=C2=B7x?= Date: Mon, 22 Feb 2021 17:44:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Annotation/Annotation.php | 122 +++++++++++---- Annotation/Event.php | 18 ++- Annotation/IAnnotation.php | 5 +- Annotation/Inject.php | 43 +++++ Annotation/Model/Get.php | 21 ++- Annotation/RequestValidator.php | 12 +- Annotation/Route/After.php | 36 +++-- Annotation/Route/Document.php | 15 +- Annotation/Route/Interceptor.php | 23 ++- Annotation/Route/Limits.php | 23 ++- Annotation/Route/Middleware.php | 25 ++- Annotation/Route/Route.php | 14 +- Annotation/Route/Socket.php | 14 +- Database/ActiveRecord.php | 6 + Database/Base/AbstractCollection.php | 21 ++- Database/Base/BaseActiveRecord.php | 148 +++++++++--------- Database/Base/CollectionIterator.php | 11 +- Database/Collection.php | 11 +- Database/Command.php | 6 +- Gii/GiiConsole.php | 87 ++++++++++ HttpServer/Command.php | 26 +++ HttpServer/Events/OnWorkerStart.php | 1 + HttpServer/Route/Node.php | 12 +- HttpServer/Server.php | 2 - System/Abstracts/BaseApplication.php | 12 ++ System/Abstracts/Pool.php | 11 +- System/Application.php | 7 +- System/Event.php | 57 ++++--- System/Exception/NotFindPropertyException.php | 35 +++++ System/Pool/Connection.php | 11 +- System/Pool/ObjectPool.php | 58 +++++++ System/Pool/Pool.php | 11 +- System/Pool/Redis.php | 13 +- function.php | 37 +++++ 34 files changed, 732 insertions(+), 222 deletions(-) create mode 100644 Annotation/Inject.php create mode 100644 Gii/GiiConsole.php create mode 100644 System/Exception/NotFindPropertyException.php create mode 100644 System/Pool/ObjectPool.php diff --git a/Annotation/Annotation.php b/Annotation/Annotation.php index a5de04f0..3d60b82b 100644 --- a/Annotation/Annotation.php +++ b/Annotation/Annotation.php @@ -4,13 +4,12 @@ namespace Annotation; -use Annotation\Model\Get; -use Database\ActiveRecord; use ReflectionAttribute; use ReflectionClass; use ReflectionException; use ReflectionMethod; use Snowflake\Abstracts\Component; +use Snowflake\Exception\NotFindPropertyException; use Snowflake\Snowflake; /** @@ -29,17 +28,40 @@ class Annotation extends Component private array $_targets = []; + private array $_methods = []; + + + /** + * @param array $handler + * @param $name + */ + public function addMethodAttribute(array $handler, $name) + { + $this->_methods[get_class($handler[0])][$name] = $handler; + } + + + /** + * @param string $className + * @return array 根据类名获取注解 + * 根据类名获取注解 + */ + public function getMethods(string $className): array + { + return $this->_methods[$className] ?? []; + } + + /** * @param string $path * @param string $namespace * @param string $alias * @return $this - * @throws ReflectionException + * @throws ReflectionException|NotFindPropertyException */ public function readControllers(string $path, string $namespace, string $alias = 'root'): static { - $this->scanDir(glob($path . '*'), $namespace, $alias); - return $this; + return $this->scanDir(glob($path . '*'), $namespace, $alias); } @@ -56,25 +78,12 @@ class Annotation extends Component } - /** - * @param string $target - * @return mixed - */ - public function getTarget(string $target): mixed - { - if (!isset($this->_targets[$target])) { - return []; - } - return $this->_targets[$target]; - } - - /** * @param array $paths * @param string $namespace * @param string $alias * @return $this - * @throws ReflectionException + * @throws ReflectionException|NotFindPropertyException */ private function scanDir(array $paths, string $namespace, string $alias): static { @@ -104,26 +113,74 @@ class Annotation extends Component * @param string $alias * @return array * @throws ReflectionException + * @throws NotFindPropertyException */ private function getReflect(string $class, string $alias): array { $reflect = $this->reflectClass($class); - if ($reflect->isInstantiable()) { - $object = $reflect->newInstance(); - foreach ($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - if ($method->class != $class) { - continue; - } + if (!$reflect->isInstantiable()) { + return $this->targets($reflect); + } + $object = $reflect->newInstance(); + $this->resolveMethod($reflect, $class, $alias, $object); + return $this->targets($reflect); + } - $tmp = $this->resolveAnnotations($method, $alias, $object); - if (empty($tmp)) { - continue; - } - $this->_classes[$reflect->getName()][$method->getName()] = $tmp; + /** + * @param ReflectionClass $reflect + * @param $class + * @param $alias + * @param $object + * @throws NotFindPropertyException + */ + private function resolveMethod(ReflectionClass $reflect, $class, $alias, $object) + { + foreach ($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if ($method->class != $class) { + continue; + } + + $tmp = $this->resolveAnnotations($method, $alias, $object); + if (empty($tmp)) { + continue; + } + + $this->_classes[$reflect->getName()][$method->getName()] = $tmp; + } + $this->resolveProperty($reflect, $object); + } + + + /** + * @param ReflectionClass $reflectionClass + * @param $object + * @throws NotFindPropertyException + */ + private function resolveProperty(ReflectionClass $reflectionClass, $object) + { + $property = $reflectionClass->getProperties(); + foreach ($property as $value) { + $attributes = $value->getAttributes(); + if (count($attributes) < 1) { + continue; + } + foreach ($attributes as $attribute) { + /** @var IAnnotation $annotation */ + $annotation = $attribute->newInstance()->execute([$object, $value->getName()]); + if ($value->isStatic()) { + $object::{$value->getName()} = $annotation; + } else if ($value->isPublic()) { + $object->{$value->getName()} = $annotation; + } else { + $name = 'set' . ucfirst($value->getName()); + if (!method_exists($object, $name)) { + throw new NotFindPropertyException('set property need method ' . $name); + } + $object->$name($annotation); + } } } - return $this->targets($reflect); } @@ -153,11 +210,12 @@ class Annotation extends Component $names = []; foreach ($attributes as $attribute) { + /** @var IAnnotation $class */ $class = $this->instance($attribute); if ($class === null) { continue; } - $names[$attribute->getName()] = $class; + $names[$attribute->getName()] = $class->execute([$object, $method->getName()]); } $tmp['handler'] = [$object, $method->getName()]; diff --git a/Annotation/Event.php b/Annotation/Event.php index cf826ce4..8a466153 100644 --- a/Annotation/Event.php +++ b/Annotation/Event.php @@ -4,6 +4,7 @@ namespace Annotation; +use Exception; use Snowflake\Exception\ComponentException; use Snowflake\Snowflake; @@ -12,7 +13,7 @@ use Snowflake\Snowflake; * Class Event * @package Annotation */ -#[\Attribute(\Attribute::TARGET_METHOD)] class Event +#[\Attribute(\Attribute::TARGET_METHOD)] class Event implements IAnnotation { @@ -20,11 +21,24 @@ use Snowflake\Snowflake; * Event constructor. * @param string $name * @param array $params - * @throws ComponentException */ public function __construct(public string $name, public array $params = []) { + } + + + /** + * @param array $handler + * @return \Snowflake\Event + * @throws ComponentException + * @throws Exception + */ + public function execute(array $handler): \Snowflake\Event + { + // TODO: Implement execute() method. $event = Snowflake::app()->getEvent(); + $event->on($this->name, $handler, $this->params); + return $event; } } diff --git a/Annotation/IAnnotation.php b/Annotation/IAnnotation.php index 55b857be..fae00476 100644 --- a/Annotation/IAnnotation.php +++ b/Annotation/IAnnotation.php @@ -9,12 +9,11 @@ use Closure; interface IAnnotation { - /** - * @param array|Closure $handler + * @param array $handler * @return mixed */ - public function setHandler(array|Closure $handler): mixed; + public function execute(array $handler): mixed; } diff --git a/Annotation/Inject.php b/Annotation/Inject.php new file mode 100644 index 00000000..47a68fda --- /dev/null +++ b/Annotation/Inject.php @@ -0,0 +1,43 @@ +has($this->className)) { + return Snowflake::app()->get($this->className); + } + return Snowflake::createObject($this->className, $this->args); + } +} diff --git a/Annotation/Model/Get.php b/Annotation/Model/Get.php index 19b46596..ee884314 100644 --- a/Annotation/Model/Get.php +++ b/Annotation/Model/Get.php @@ -4,15 +4,19 @@ namespace Annotation\Model; +use Annotation\Annotation; +use Annotation\IAnnotation; use Attribute; use Database\ActiveRecord; +use Snowflake\Exception\ComponentException; +use Snowflake\Snowflake; /** * Class Get * @package Annotation\Model */ -#[Attribute(Attribute::TARGET_METHOD)] class Get +#[Attribute(Attribute::TARGET_METHOD)] class Get implements IAnnotation { @@ -27,4 +31,19 @@ use Database\ActiveRecord; } + /** + * @param array $handler + * @return Annotation + * @throws ComponentException + */ + public function execute(array $handler): Annotation + { + // TODO: Implement execute() method. + $annotation = Snowflake::app()->getAttributes(); + $annotation->addMethodAttribute($handler, $this->name); + + return $annotation; + } + + } diff --git a/Annotation/RequestValidator.php b/Annotation/RequestValidator.php index 5e2ed4d7..2db460fb 100644 --- a/Annotation/RequestValidator.php +++ b/Annotation/RequestValidator.php @@ -11,7 +11,7 @@ use validator\Validator; * Class RequestValidator * @package Annotation */ -#[\Attribute(\Attribute::TARGET_METHOD)] class RequestValidator +#[\Attribute(\Attribute::TARGET_METHOD)] class RequestValidator implements IAnnotation { /** @@ -24,4 +24,14 @@ use validator\Validator; } + /** + * @param array $handler + * @return bool + */ + public function execute(array $handler): bool + { + return true; + } + + } diff --git a/Annotation/Route/After.php b/Annotation/Route/After.php index 82d1988e..0422ca2e 100644 --- a/Annotation/Route/After.php +++ b/Annotation/Route/After.php @@ -4,13 +4,16 @@ namespace Annotation\Route; +use Annotation\IAnnotation; +use ReflectionException; +use Snowflake\Exception\NotFindClassException; use Snowflake\Snowflake; /** * Class Interceptor * @package Annotation\Route */ -#[\Attribute(\Attribute::TARGET_METHOD)] class After +#[\Attribute(\Attribute::TARGET_METHOD)] class After implements IAnnotation { @@ -18,27 +21,30 @@ use Snowflake\Snowflake; * Interceptor constructor. * @param \HttpServer\IInterface\After|\HttpServer\IInterface\After[] $after * @throws - * if ($object instanceof Interceptor) { - $classes[$key] = [$object, 'Interceptor']; - } - if ($object instanceof Limits) { - $classes[$key] = [$object, 'next']; - } - if ($object instanceof After) { - $classes[$key] = [$object, 'onHandler']; - } - if ($object instanceof Middleware) { - $classes[$key] = [$object, 'onHandler']; - } */ public function __construct(public string|array $after) { - if (is_string($this->after)) { - $this->after = [$this->after]; + if (!is_string($this->after)) { + return; } + $this->after = [$this->after]; + } + + + /** + * @param array $handler + * @return array|string + * @throws ReflectionException + * @throws NotFindClassException + */ + public function execute(array $handler): array|string + { + // TODO: Implement execute() method. foreach ($this->after as $key => $item) { $this->after[$key] = [Snowflake::createObject($item), 'onHandler']; } + return $this->after; } + } diff --git a/Annotation/Route/Document.php b/Annotation/Route/Document.php index a486ba07..a63aad90 100644 --- a/Annotation/Route/Document.php +++ b/Annotation/Route/Document.php @@ -4,11 +4,13 @@ namespace Annotation\Route; +use Annotation\IAnnotation; + /** * Class Document * @package Annotation\Route */ -#[\Attribute(\Attribute::TARGET_METHOD)] class Document +#[\Attribute(\Attribute::TARGET_METHOD)] class Document implements IAnnotation { const INTEGER = 'int'; @@ -31,4 +33,15 @@ namespace Annotation\Route; { } + + /** + * @param array $handler + * @return array + */ + public function execute(array $handler): array + { + // TODO: Implement execute() method. + return [$this->request, $this->response]; + } + } diff --git a/Annotation/Route/Interceptor.php b/Annotation/Route/Interceptor.php index d99af0ff..ec2229d9 100644 --- a/Annotation/Route/Interceptor.php +++ b/Annotation/Route/Interceptor.php @@ -4,13 +4,16 @@ namespace Annotation\Route; +use Annotation\IAnnotation; +use ReflectionException; +use Snowflake\Exception\NotFindClassException; use Snowflake\Snowflake; /** * Class Interceptor * @package Annotation\Route */ -#[\Attribute(\Attribute::TARGET_METHOD)] class Interceptor +#[\Attribute(\Attribute::TARGET_METHOD)] class Interceptor implements IAnnotation { @@ -21,12 +24,26 @@ use Snowflake\Snowflake; */ public function __construct(public string|array $interceptor) { - if (is_string($this->interceptor)) { - $this->interceptor = [$this->interceptor]; + if (!is_string($this->interceptor)) { + return; } + $this->interceptor = [$this->interceptor]; + } + + + /** + * @param array $handler + * @return array|string + * @throws ReflectionException + * @throws NotFindClassException + */ + public function execute(array $handler): array|string + { + // TODO: Implement execute() method. foreach ($this->interceptor as $key => $item) { $this->interceptor[$key] = [Snowflake::createObject($item), 'Interceptor']; } + return $this->interceptor; } } diff --git a/Annotation/Route/Limits.php b/Annotation/Route/Limits.php index 1e0358d8..2e174b9c 100644 --- a/Annotation/Route/Limits.php +++ b/Annotation/Route/Limits.php @@ -4,13 +4,16 @@ namespace Annotation\Route; +use Annotation\IAnnotation; +use ReflectionException; +use Snowflake\Exception\NotFindClassException; use Snowflake\Snowflake; /** * Class Limits * @package Annotation\Route */ -#[\Attribute(\Attribute::TARGET_METHOD)] class Limits +#[\Attribute(\Attribute::TARGET_METHOD)] class Limits implements IAnnotation { @@ -21,12 +24,26 @@ use Snowflake\Snowflake; */ public function __construct(public string|array $limits) { - if (is_string($this->limits)) { - $this->limits = [$this->limits]; + if (!is_string($this->limits)) { + return; } + $this->limits = [$this->limits]; + } + + + /** + * @param array $handler + * @return array|string + * @throws ReflectionException + * @throws NotFindClassException + */ + public function execute(array $handler): array|string + { + // TODO: Implement execute() method. foreach ($this->limits as $key => $item) { $this->limits[$key] = [Snowflake::createObject($item), 'next']; } + return $this->limits; } diff --git a/Annotation/Route/Middleware.php b/Annotation/Route/Middleware.php index df504e76..23a069d9 100644 --- a/Annotation/Route/Middleware.php +++ b/Annotation/Route/Middleware.php @@ -4,13 +4,17 @@ namespace Annotation\Route; +use Annotation\IAnnotation; +use JetBrains\PhpStorm\Pure; +use ReflectionException; +use Snowflake\Exception\NotFindClassException; use Snowflake\Snowflake; /** * Class Middleware * @package Annotation\Route */ -#[\Attribute(\Attribute::TARGET_METHOD)] class Middleware +#[\Attribute(\Attribute::TARGET_METHOD)] class Middleware implements IAnnotation { @@ -21,12 +25,27 @@ use Snowflake\Snowflake; */ public function __construct(public string|array $middleware) { - if (is_string($this->middleware)) { - $this->middleware = [$this->middleware]; + if (!is_string($this->middleware)) { + return; } + $this->middleware = [$this->middleware]; + } + + + /** + * @param array $handler + * @return array|string + * @throws ReflectionException + * @throws NotFindClassException + */ + public function execute(array $handler): array|string + { + // TODO: Implement execute() method. foreach ($this->middleware as $key => $item) { $this->middleware[$key] = [Snowflake::createObject($item), 'onHandler']; } + return $this->middleware; } + } diff --git a/Annotation/Route/Route.php b/Annotation/Route/Route.php index 43fb9604..211211cd 100644 --- a/Annotation/Route/Route.php +++ b/Annotation/Route/Route.php @@ -7,6 +7,7 @@ namespace Annotation\Route; use Closure; use Exception; use HttpServer\Route\Node; +use HttpServer\Route\Router; use Snowflake\Exception\ComponentException; use Snowflake\Exception\ConfigException; use Snowflake\Snowflake; @@ -29,18 +30,19 @@ use Annotation\IAnnotation; /** - * @param array|Closure $handler - * @return ?Node + * @param array $handler + * @return Router * @throws ComponentException * @throws ConfigException - * @throws Exception */ - public function setHandler(array|Closure $handler): ?Node + public function execute(array $handler): Router { - $router = Snowflake::app()->getRouter(); // TODO: Implement setHandler() method. + $router = Snowflake::app()->getRouter(); - return $router->addRoute($this->uri, $handler, $this->method); + $router->addRoute($this->uri, $handler, $this->method); + + return $router; } diff --git a/Annotation/Route/Socket.php b/Annotation/Route/Socket.php index 8fab9f02..2d1f06d0 100644 --- a/Annotation/Route/Socket.php +++ b/Annotation/Route/Socket.php @@ -8,6 +8,7 @@ use Annotation\IAnnotation; use Closure; use Exception; use HttpServer\Route\Node; +use HttpServer\Route\Router; use ReflectionException; use Snowflake\Exception\ComponentException; use Snowflake\Exception\ConfigException; @@ -39,20 +40,21 @@ use Snowflake\Snowflake; /** - * @param array|Closure $handler - * @return Node|null + * @param array $handler + * @return Router * @throws ComponentException * @throws ConfigException - * @throws Exception */ - public function setHandler(array|Closure $handler): ?Node + public function execute(array $handler): Router { - $router = Snowflake::app()->getRouter(); // TODO: Implement setHandler() method. + $router = Snowflake::app()->getRouter(); $method = $this->event . '::' . (is_null($this->uri) ? 'event' : $this->uri); - return $router->addRoute($method, $handler, 'sw::socket'); + $router->addRoute($method, $handler, 'sw::socket'); + + return $router; } } diff --git a/Database/ActiveRecord.php b/Database/ActiveRecord.php index 351f333f..64c5f7f3 100644 --- a/Database/ActiveRecord.php +++ b/Database/ActiveRecord.php @@ -17,6 +17,7 @@ use Snowflake\Core\ArrayAccess; use Snowflake\Error\Logger; use Snowflake\Exception\ComponentException; use Snowflake\Snowflake; +use Swoole\Coroutine; defined('SAVE_FAIL') or define('SAVE_FAIL', 3227); defined('FIND_OR_CREATE_MESSAGE') or define('FIND_OR_CREATE_MESSAGE', 'Create a new model, but the data cannot be empty.'); @@ -314,6 +315,11 @@ class ActiveRecord extends BaseActiveRecord */ public function toArray(): array { + $class = $this; + Coroutine::defer(function () use ($class) { + $object = Snowflake::app()->getObject(); + $object->release(get_called_class(), $class); + }); $data = $this->_attributes; foreach ($this->getAnnotation() as $key => $item) { if (!isset($data[$key])) { diff --git a/Database/Base/AbstractCollection.php b/Database/Base/AbstractCollection.php index 58660ca4..79ee3503 100644 --- a/Database/Base/AbstractCollection.php +++ b/Database/Base/AbstractCollection.php @@ -13,10 +13,14 @@ namespace Database\Base; use ArrayIterator; use Database\ActiveQuery; +use Exception; +use JetBrains\PhpStorm\Pure; use ReflectionException; use Snowflake\Abstracts\Component; use Database\ActiveRecord; +use Snowflake\Exception\ComponentException; use Snowflake\Exception\NotFindClassException; +use Snowflake\Snowflake; use Traversable; /** @@ -55,7 +59,7 @@ abstract class AbstractCollection extends Component implements \IteratorAggregat /** * @return int */ - public function getLength(): int + #[Pure] public function getLength(): int { return count($this->_item); } @@ -88,14 +92,25 @@ abstract class AbstractCollection extends Component implements \IteratorAggregat /** * @return Traversable|CollectionIterator|ArrayIterator - * @throws ReflectionException - * @throws NotFindClassException + * @throws Exception */ public function getIterator(): Traversable|CollectionIterator|ArrayIterator { return new CollectionIterator($this->model, $this->query, $this->_item); } + + /** + * @return mixed + * @throws ComponentException + * @throws Exception + */ + public function getModel(): ActiveRecord + { + return Snowflake::app()->getObject()->getConnection([$this->model], false); + } + + /** * @param mixed $offset * @return bool diff --git a/Database/Base/BaseActiveRecord.php b/Database/Base/BaseActiveRecord.php index b8d405fb..6f03a4c5 100644 --- a/Database/Base/BaseActiveRecord.php +++ b/Database/Base/BaseActiveRecord.php @@ -10,6 +10,8 @@ declare(strict_types=1); namespace Database\Base; +use Annotation\Event; +use Annotation\Inject; use Annotation\Model\Get; use ArrayAccess; use HttpServer\Http\Context; @@ -28,6 +30,7 @@ use Snowflake\Exception\NotFindClassException; use validator\Validator; use Database\IOrm; use Snowflake\Snowflake; +use Snowflake\Event as SEvent; /** * Class BOrm @@ -41,6 +44,14 @@ use Snowflake\Snowflake; abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess { + const AFTER_SAVE = 'after::save'; + const BEFORE_SAVE = 'before::save'; + + + #[Inject(SEvent::class)] + protected ?SEvent $event; + + /** @var array */ protected array $_attributes = []; @@ -65,6 +76,17 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess protected ?Relation $_relation; + + /** + * object init + */ + public function clean() + { + $this->_attributes = []; + $this->_oldAttributes = []; + } + + /** * @throws Exception */ @@ -76,56 +98,10 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess } else { $this->_relation = Context::getContext(Relation::class); } - $this->parseAnnotation(); + $this->_annotations = annotation()->getMethods(get_called_class()); } - /** - * @throws ComponentException - */ - private function parseAnnotation(): void - { - $attributes = Snowflake::app()->getAttributes(); - $annotations = $attributes->getByClass(static::class); - - $response = []; - foreach ($annotations as $annotation) { - if (!isset($annotation['attributes'])) { - continue; - } - foreach ($annotation['attributes'] as $attribute) { - if (!($attribute instanceof Get)) { - continue; - } - $response[$attribute->name] = $annotation['handler']; - } - } - $this->_annotations = $response; - } - - - /** - * @param string $column - * @param int $value - * @return void - * @throws Exception - */ - public function incrBy(string $column, int $value) - { - throw new Exception('Undefined function incrBy in ' . get_called_class()); - } - - /** - * @param string $column - * @param int $value - * @return void - * @throws Exception - */ - public function decrBy(string $column, int $value) - { - throw new Exception('Undefined function decrBy in ' . get_called_class()); - } - /** * @return array */ @@ -223,7 +199,7 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess /** * @param $param * @param null $db - * @return $this + * @return BaseActiveRecord|null * @throws NotFindClassException * @throws ReflectionException * @throws Exception @@ -234,18 +210,30 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess return null; } if (is_numeric($param)) { - $primary = static::getColumns()->getPrimaryKeys(); - if (empty($primary)) { - throw new Exception('Primary key cannot be empty.'); - } - if (is_array($primary)) { - $primary = current($primary); - } - $param = [$primary => $param]; + $param = static::getPrimaryCondition($param); } return static::find()->where($param)->first(); } + + /** + * @param $param + * @return array + * @throws Exception + */ + private static function getPrimaryCondition($param): array + { + $primary = static::getColumns()->getPrimaryKeys(); + if (empty($primary)) { + throw new Exception('Primary key cannot be empty.'); + } + if (is_array($primary)) { + $primary = current($primary); + } + return [$primary => $param]; + } + + /** * @param null $field * @return ActiveRecord|null @@ -374,15 +362,6 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess return $this; } - /** - * @return bool - * @throws Exception - */ - public function beforeSave() - { - return true; - } - /** * @param $attributes * @param $param @@ -401,13 +380,13 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess $trance = $dbConnection->beginTransaction(); try { $commandExec = $dbConnection->createCommand($sqlBuilder, $param); - if (($lastId = $commandExec->save(true, $this->hasAutoIncrement())) === false) { + if (($lastId = $commandExec->save(true, $this)) === false) { throw new Exception('保存失败.' . $sqlBuilder); } $trance->commit(); $this->setPrimary($lastId, $param); - $this->afterSave($attributes, $param); - $lastId = $this->refresh(); + + $this->event->dispatch(self::AFTER_SAVE, [$attributes, $param]); } catch (\Throwable $exception) { $trance->rollback(); $lastId = $this->addError($exception, 'mysql'); @@ -455,13 +434,13 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess $sql = $change->update(static::getTable(), $attributes, $condition, $param); $trance = $command->beginTransaction(); - if (!($command = $command->createCommand($sql, $param)->save(false, $this->hasAutoIncrement()))) { + if (!($command = $command->createCommand($sql, $param)->save(false, $this))) { $trance->rollback(); $result = false; } else { $trance->commit(); - $result = $this->refresh(); - $this->afterSave($attributes, $param); + + $result = $this->event->dispatch(self::AFTER_SAVE, [$attributes, $param]); } return $result; } @@ -476,7 +455,7 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess if (is_array($data)) { $this->_attributes = array_merge($this->_attributes, $data); } - if (!$this->validator($this->rules()) || !$this->beforeSave()) { + if (!$this->validator($this->rules())) { return false; } @@ -599,9 +578,9 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess /** - * @return Relation + * @return Relation|null */ - public function getRelation() + public function getRelation(): ?Relation { return $this->_relation; } @@ -647,7 +626,7 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess } if (empty($table)) { - $class = preg_replace('/model\\\/', '', get_called_class()); + $class = preg_replace('/model\\\\/', '', get_called_class()); $table = lcfirst($class); } @@ -662,10 +641,24 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess /** * @param $attributes * @param $changeAttributes + * @return bool * @throws Exception */ - public function afterSave($attributes, $changeAttributes): void + #[Event(static::AFTER_SAVE)] + public function afterSave($attributes, $changeAttributes): bool { + return true; + } + + + /** + * @param $model + * @return bool + */ + #[Event(static::BEFORE_SAVE)] + public function beforeSave($model): bool + { + return true; } /** @@ -874,7 +867,8 @@ abstract class BaseActiveRecord extends Component implements IOrm, ArrayAccess */ public static function populate(array $data): static { - $model = new static(); + $object = Snowflake::app()->getObject(); + $model = $object->getConnection([static::class], false); $model->setAttributes($data); $model->setIsCreate(false); $model->refresh(); diff --git a/Database/Base/CollectionIterator.php b/Database/Base/CollectionIterator.php index 1a60ab03..1b484820 100644 --- a/Database/Base/CollectionIterator.php +++ b/Database/Base/CollectionIterator.php @@ -32,15 +32,11 @@ class CollectionIterator extends \ArrayIterator * @param $query * @param array $array * @param int $flags - * @throws NotFindClassException - * @throws ReflectionException + * @throws Exception */ public function __construct($model, $query, $array = array(), $flags = 0) { $this->model = $model; - if (is_string($model)) { - $this->model = Snowflake::createObject($model); - } $this->query = $query; parent::__construct($array, $flags); } @@ -53,7 +49,10 @@ class CollectionIterator extends \ArrayIterator */ protected function newModel($current): ActiveRecord { - return (clone $this->model)->setAttributes($current); + $object = Snowflake::app()->getObject(); + $model = $object->getConnection([$this->model], false); + + return $model->setAttributes($current); } diff --git a/Database/Collection.php b/Database/Collection.php index 23dc2849..337406ca 100644 --- a/Database/Collection.php +++ b/Database/Collection.php @@ -11,6 +11,7 @@ namespace Database; use Database\Base\AbstractCollection; use Exception; +use JetBrains\PhpStorm\Pure; /** * Class Collection @@ -79,7 +80,7 @@ class Collection extends AbstractCollection * * @return array */ - public function slice($start = 0, $length = 20): array + #[Pure] public function slice($start = 0, $length = 20): array { if (empty($this->_item) || !is_array($this->_item)) { return []; @@ -127,7 +128,7 @@ class Collection extends AbstractCollection /** * @return ActiveRecord|array */ - public function current(): ActiveRecord|array + #[Pure] public function current(): ActiveRecord|array { return current($this->_item); } @@ -135,7 +136,7 @@ class Collection extends AbstractCollection /** * @return int */ - public function size(): int + #[Pure] public function size(): int { return (int)count($this->_item); } @@ -163,7 +164,7 @@ class Collection extends AbstractCollection */ public function delete(): bool { - $model = $this->model; + $model = $this->getModel(); if (!$model->hasPrimary()) { return false; } @@ -175,7 +176,7 @@ class Collection extends AbstractCollection if (empty($ids)) { return false; } - return $this->model::find() + return $model::find() ->in($model->getPrimary(), $ids) ->delete(); } diff --git a/Database/Command.php b/Database/Command.php index df2ae2b7..5e88f66f 100644 --- a/Database/Command.php +++ b/Database/Command.php @@ -57,7 +57,7 @@ class Command extends Component * @return int|bool|array|string|null * @throws Exception */ - public function save($isInsert = TRUE, $hasAutoIncrement = true): int|bool|array|string|null + public function save($isInsert = TRUE, $hasAutoIncrement = null): int|bool|array|string|null { return $this->execute(static::EXECUTE, $isInsert, $hasAutoIncrement); } @@ -187,7 +187,7 @@ class Command extends Component * @return int|bool|array|string|null * @throws Exception */ - private function execute($type, $isInsert = null, $hasAutoIncrement = true): int|bool|array|string|null + private function execute($type, $isInsert = null, $hasAutoIncrement = null): int|bool|array|string|null { try { $time = microtime(true); @@ -251,7 +251,7 @@ class Command extends Component return true; } $result = $connection->lastInsertId(); - if ($result == 0 && $hasAutoIncrement) { + if ($result == 0 && $hasAutoIncrement->hasAutoIncrement()) { return $this->addError($connection->errorInfo()[2], 'mysql'); } return $result; diff --git a/Gii/GiiConsole.php b/Gii/GiiConsole.php new file mode 100644 index 00000000..0536e8c4 --- /dev/null +++ b/Gii/GiiConsole.php @@ -0,0 +1,87 @@ +setName('sw:gii') + ->setDescription('create default file.') + ->setHelp('make=model|controller|task|interceptor|limits|middleware name=xxxx'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @throws \ReflectionException + * @throws NotFindClassException + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->gii = Snowflake::createObject(Gii::class); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + * @throws ComponentException + * @throws ConfigException + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('start generate. Please waite...'); + $connections = Snowflake::app()->get('db'); + if ($input->getArgument('databases')) { + $array = $this->gii->run($connections->get($input->getArgument('databases')), $input); + } else { + $array = $this->batchCreate($input, $this->gii, $connections); + } + $output->writeln('create file [' . Json::encode($array) . ']'); + return 0; + } + + + /** + * @param InputInterface $input + * @param $gii + * @param $connections + * @return array + * @throws ConfigException + */ + private function batchCreate(InputInterface $input, $gii, $connections): array + { + $array = []; + foreach (Config::get('databases') as $key => $connection) { + $array[$key] = $gii->run($connections->get($key), $input); + } + return $array; + } + + +} diff --git a/HttpServer/Command.php b/HttpServer/Command.php index 91118a1d..71453dad 100644 --- a/HttpServer/Command.php +++ b/HttpServer/Command.php @@ -5,8 +5,12 @@ namespace HttpServer; use Exception; +use ReflectionException; use Snowflake\Abstracts\Input; +use Snowflake\Event; +use Snowflake\Exception\ComponentException; use Snowflake\Exception\ConfigException; +use Snowflake\Exception\NotFindPropertyException; use Snowflake\Snowflake; /** @@ -48,7 +52,29 @@ class Command extends \Console\Command if ($dtl->get('action') == 'stop') { return 'shutdown success.'; } + + listen(Event::SERVER_BEFORE_START, [$this, 'scan_system_annotation']); + return $manager->start(); } + + /** + * @throws ReflectionException + * @throws ComponentException + * @throws NotFindPropertyException + */ + public function scan_system_annotation() + { + $annotation = Snowflake::app()->getAttributes(); + $annotation->readControllers(__DIR__ . '/../Console', 'Console', 'system'); + $annotation->readControllers(__DIR__ . '/../Database', 'Database', 'system'); + $annotation->readControllers(__DIR__ . '/../Gii', 'Gii', 'system'); + $annotation->readControllers(__DIR__ . '/../HttpServer', 'HttpServer', 'system'); + $annotation->readControllers(__DIR__ . '/../Kafka', 'Kafka', 'system'); + $annotation->readControllers(__DIR__ . '/../System', 'Snowflake', 'system'); + $annotation->readControllers(__DIR__ . '/../Validator', 'Validator', 'system'); + } + + } diff --git a/HttpServer/Events/OnWorkerStart.php b/HttpServer/Events/OnWorkerStart.php index 50753045..d3e35cf6 100644 --- a/HttpServer/Events/OnWorkerStart.php +++ b/HttpServer/Events/OnWorkerStart.php @@ -55,6 +55,7 @@ class OnWorkerStart extends Callback $this->debug(sprintf('Worker #%d is start.....', $worker_id)); $event = Snowflake::app()->getEvent(); $event->trigger(Event::SERVER_WORKER_START, [$worker_id]); + $event->trigger(Event::SERVER_AFTER_WORKER_START, [$worker_id]); } catch (\Throwable $exception) { write($exception->getMessage(), 'worker'); } diff --git a/HttpServer/Route/Node.php b/HttpServer/Route/Node.php index 6f8cb899..1419920c 100644 --- a/HttpServer/Route/Node.php +++ b/HttpServer/Route/Node.php @@ -14,6 +14,7 @@ use JetBrains\PhpStorm\Pure; use ReflectionException; use Snowflake\Core\Json; use Snowflake\Event; +use Snowflake\Exception\ComponentException; use Snowflake\Exception\NotFindClassException; use Snowflake\Snowflake; use Swoole\Coroutine; @@ -56,6 +57,16 @@ class Node extends HttpService private array $_after = []; private array $_limits = []; + + /** + * @throws ComponentException + * @throws Exception + */ + public function afterInit() + { + listen(Event::SERVER_AFTER_WORKER_START, [$this, 'restructure']); + } + /** * @param $handler * @return Node @@ -450,7 +461,6 @@ class Node extends HttpService */ public function dispatch(): mixed { - $this->restructure(); if (empty($this->callback)) { return Json::to(404, $node->_error ?? 'Page not found.'); } diff --git a/HttpServer/Server.php b/HttpServer/Server.php index 418e0034..49dff73f 100644 --- a/HttpServer/Server.php +++ b/HttpServer/Server.php @@ -140,8 +140,6 @@ class Server extends HttpService $configs = Config::get('servers', true); Snowflake::clearWorkerId(); - fire(Event::SERVER_BEFORE_START); - $baseServer = $this->initCore($configs); if (!$baseServer) { return 'ok'; diff --git a/System/Abstracts/BaseApplication.php b/System/Abstracts/BaseApplication.php index 40a2b757..56898182 100644 --- a/System/Abstracts/BaseApplication.php +++ b/System/Abstracts/BaseApplication.php @@ -33,6 +33,7 @@ use Snowflake\Exception\ComponentException; use Snowflake\Exception\InitException; use Snowflake\Jwt\Jwt; use Snowflake\Pool\Connection; +use Snowflake\Pool\ObjectPool; use Snowflake\Pool\Redis as SRedis; use Snowflake\Snowflake; use Snowflake\Event; @@ -404,6 +405,16 @@ abstract class BaseApplication extends Service } + /** + * @return ObjectPool + * @throws ComponentException + */ + public function getObject(): ObjectPool + { + return $this->get('object'); + } + + /** * @throws Exception */ @@ -424,6 +435,7 @@ abstract class BaseApplication extends Service 'redis' => ['class' => Redis::class], 'jwt' => ['class' => Jwt::class], 'async' => ['class' => Async::class], + 'object' => ['class' => ObjectPool::class], 'goto' => ['class' => BaseGoto::class], 'http2' => ['class' => Http2::class], ]); diff --git a/System/Abstracts/Pool.php b/System/Abstracts/Pool.php index fc04f353..23158f78 100644 --- a/System/Abstracts/Pool.php +++ b/System/Abstracts/Pool.php @@ -49,11 +49,12 @@ abstract class Pool extends Component } - /** - * @param $name - * @return array - * @throws Exception - */ + /** + * @param $name + * @param array $callback + * @return array + * @throws Exception + */ protected function get($name, array $callback): mixed { if (!Context::inCoroutine()) { diff --git a/System/Application.php b/System/Application.php index 9409a747..6c276643 100644 --- a/System/Application.php +++ b/System/Application.php @@ -97,13 +97,14 @@ class Application extends BaseApplication * @param Input $argv * @return bool|string * @throws Exception - * @throws NotFindClassException - * @throws \ReflectionException */ public function start(Input $argv): bool|string { - $this->set('input', $argv); try { + fire(Event::SERVER_BEFORE_START); + + $this->set('input', $argv); + $manager = Snowflake::app()->get('console'); $manager->setParameters($argv); $class = $manager->search(); diff --git a/System/Event.php b/System/Event.php index b6ece62d..05bbb813 100644 --- a/System/Event.php +++ b/System/Event.php @@ -6,6 +6,7 @@ namespace Snowflake; use Exception; +use JetBrains\PhpStorm\Pure; use Snowflake\Abstracts\BaseObject; use Snowflake\Core\ArrayAccess; @@ -45,6 +46,7 @@ class Event extends BaseObject const SERVER_MANAGER_STOP = 'SERVER:EVENT:MANAGER:START'; const SERVER_WORKER_STOP = 'SERVER:EVENT:WORKER:STOP'; const SERVER_WORKER_START = 'SERVER:EVENT:WORKER:START'; + const SERVER_AFTER_WORKER_START = 'SERVER:EVENT:AFTER:WORKER:START'; const SERVER_BEFORE_START = 'SERVER:EVENT:BEFORE:START'; const SERVER_TASK_START = 'SERVER:EVENT:TASK:START'; const SERVER_WORKER_EXIT = 'SERVER:EVENT:WORKER:EXIT'; @@ -193,31 +195,14 @@ class Event extends BaseObject return false; } if (!empty($handler) && $this->exists($name, $handler)) { - [$handler, $defaultParameter] = $this->get($name, $handler); - if (!empty($parameter)) { - $defaultParameter = ArrayAccess::merge($defaultParameter, $parameter); - } - if (!is_array($defaultParameter)) { - $defaultParameter = [$defaultParameter]; - } - $result = call_user_func($handler, ...$defaultParameter); - if ($is_remove) { - $this->of($name, $handler); - } - return $result; + $events = [$this->get($name, $handler)]; + } else { + $events = $this->_events[$name]; } - foreach ($this->_events[$name] as $index => $event) { - try { - [$handler, $defaultParameter] = $event; - if (!empty($parameter)) { - $defaultParameter = ArrayAccess::merge($defaultParameter, $parameter); - } - if (!is_array($defaultParameter)) { - $defaultParameter = [$defaultParameter]; - } - call_user_func($handler, ...$defaultParameter); - } catch (\Throwable $exception) { - $this->error($exception); + foreach ($events as $index => $event) { + $meta = $this->mergeParams($event[1], $parameter); + if (call_user_func($event[0], ...$meta) === false) { + return false; } } if ($is_remove) { @@ -227,4 +212,28 @@ class Event extends BaseObject } + /** + * @param $defaultParameter + * @param $parameter + * @return array + */ + #[Pure] private function mergeParams($defaultParameter, $parameter = []): array + { + if (empty($defaultParameter)) { + $defaultParameter = $parameter; + } else { + if (!is_array($parameter)) { + $parameter = []; + } + foreach ($parameter as $key => $value) { + $defaultParameter[] = $value; + } + } + if (!is_array($defaultParameter)) { + $defaultParameter = [$defaultParameter]; + } + return $defaultParameter; + } + + } diff --git a/System/Exception/NotFindPropertyException.php b/System/Exception/NotFindPropertyException.php new file mode 100644 index 00000000..346e9e68 --- /dev/null +++ b/System/Exception/NotFindPropertyException.php @@ -0,0 +1,35 @@ +printClients($config['cds'], $name, true); // TODO: Implement createClient() method. diff --git a/System/Pool/ObjectPool.php b/System/Pool/ObjectPool.php new file mode 100644 index 00000000..8b3b4ebc --- /dev/null +++ b/System/Pool/ObjectPool.php @@ -0,0 +1,58 @@ +get($config[0], $config); + } + + + /** + * @param string $name + * @param array $config + * @return mixed + * @throws ReflectionException + * @throws NotFindClassException + */ + public function createClient(string $name, array $config): mixed + { + // TODO: Implement createClient() method. + return Snowflake::createObject(array_shift($config)); + } + + + /** + * @param string $name + * @param $object + */ + public function release(string $name, mixed $object) + { + if (method_exists($object, 'clean')) { + $object->clean(); + } + $this->push($name, $object); + } + +} diff --git a/System/Pool/Pool.php b/System/Pool/Pool.php index 7d846cd6..25c1991b 100644 --- a/System/Pool/Pool.php +++ b/System/Pool/Pool.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Snowflake\Pool; - use Snowflake\Exception\ComponentException; use Snowflake\Snowflake; @@ -38,9 +37,9 @@ class Pool extends \Snowflake\Abstracts\Pool } - public function createClient(string $name, array $config): mixed - { - // TODO: Implement createClient() method. - return null; - } + public function createClient(string $name, array $config): mixed + { + // TODO: Implement createClient() method. + return null; + } } diff --git a/System/Pool/Redis.php b/System/Pool/Redis.php index 84c565bc..b80f931b 100644 --- a/System/Pool/Redis.php +++ b/System/Pool/Redis.php @@ -49,12 +49,13 @@ class Redis extends Pool } - /** - * @param array $config - * @return SRedis - * @throws RedisConnectException - */ - public function createClient(string $name, array $config): mixed + /** + * @param string $name + * @param array $config + * @return SRedis + * @throws RedisConnectException + */ + public function createClient(string $name, array $config): SRedis { $this->printClients($config['host'], $name, true); $redis = new SRedis(); diff --git a/function.php b/function.php index 511f58bb..aa1b4e42 100644 --- a/function.php +++ b/function.php @@ -2,6 +2,7 @@ defined('APP_PATH') or define('APP_PATH', __DIR__ . '/../../'); +use Annotation\Annotation; use HttpServer\Http\HttpParams; use HttpServer\Http\Request; use HttpServer\Http\Response; @@ -38,6 +39,22 @@ if (!function_exists('make')) { } +if (!function_exists('annotation')) { + + + /** + * @return Annotation + * @throws ComponentException + */ + function annotation(): Annotation + { + return Snowflake::app()->getAttributes(); + } + + +} + + if (!function_exists('isUrl')) { @@ -395,6 +412,26 @@ if (!function_exists('storage')) { } +if (!function_exists('listen')) { + + + /** + * @param $name + * @param $callback + * @param $params + * @param $isAppend + * @throws ComponentException + * @throws Exception + */ + function listen($name, $callback, $params = [], $isAppend = true) + { + $event = Snowflake::app()->getEvent(); + $event->on($name, $callback, $params, $isAppend); + } + +} + + if (!function_exists('alias')) { /**