initial commit

This commit is contained in:
whale
2020-03-02 16:14:20 +08:00
parent 517082d159
commit 769b87af6a
132 changed files with 5424 additions and 2866 deletions

View File

@@ -0,0 +1,29 @@
<?php
namespace ZM\Annotation;
use Closure;
abstract class AnnotationBase
{
public $method;
public $class;
public function __toString() {
$str = __CLASS__ . ": ";
foreach ($this as $k => $v) {
$str .= "\n\t" . $k . " => ";
if (is_string($v)) $str .= "\"$v\"";
elseif (is_numeric($v)) $str .= $v;
elseif (is_bool($v)) $str .= ($v ? "TRUE" : "FALSE");
elseif (is_array($v)) $str .= json_encode($v, JSON_UNESCAPED_UNICODE);
elseif ($v instanceof Closure) $str .= "@AnonymousFunction";
elseif (is_null($v)) $str .= "NULL";
else $str .= "@Unknown";
}
return $str;
}
}

View File

@@ -0,0 +1,223 @@
<?php
namespace ZM\Annotation;
use Doctrine\Common\Annotations\AnnotationException;
use Doctrine\Common\Annotations\AnnotationReader;
use Framework\ZMBuf;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ZM\Annotation\CQ\{CQAfter, CQBefore, CQCommand, CQMessage, CQMetaEvent, CQNotice, CQRequest};
use ZM\Annotation\Http\Controller;
use ZM\Annotation\Http\RequestMapping;
use ZM\Annotation\Interfaces\CustomAnnotation;
use ZM\Annotation\Interfaces\Level;
use ZM\Annotation\Module\Closed;
use ZM\Annotation\Module\SaveBuffer;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Annotation\Interfaces\Rule;
use ZM\Connection\WSConnection;
use ZM\Utils\DataProvider;
class AnnotationParser
{
/**
* 注册各个模块类的注解和模块level的排序
* @throws ReflectionException
* @throws AnnotationException
*/
public static function registerMods() {
self::loadAnnotationClasses();
$all_class = getAllClasses(WORKING_DIR . "/src/Module/", "Module");
$reader = new AnnotationReader();
foreach ($all_class as $v) {
$reflection_class = new ReflectionClass($v);
$class_prefix = '';
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
$class_annotations = $reader->getClassAnnotations($reflection_class);
foreach ($class_annotations as $vs) {
if ($vs instanceof Closed) {
continue 2;
} elseif ($vs instanceof Controller) {
$class_prefix = $vs->prefix;
} elseif ($vs instanceof SaveBuffer) {
DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
}
}
foreach ($methods as $vs) {
$method_annotations = $reader->getMethodAnnotations($vs);
foreach ($method_annotations as $vss) {
if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class);
else $vss = self::registerMethod($vss, $vs, $reflection_class);
if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
elseif ($vss instanceof CQMessage) ZMBuf::$events[CQMessage::class][] = $vss;
elseif ($vss instanceof CQNotice) ZMBuf::$events[CQNotice::class][] = $vss;
elseif ($vss instanceof CQRequest) ZMBuf::$events[CQRequest::class][] = $vss;
elseif ($vss instanceof CQMetaEvent) ZMBuf::$events[CQMetaEvent::class][] = $vss;
elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
elseif ($vss instanceof RequestMapping) self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $vss;
elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
}
}
}
//给支持level的排个序
foreach (ZMBuf::$events as $class_name => $v) {
if ((new $class_name()) instanceof Level) {
for ($i = 0; $i < count(ZMBuf::$events[$class_name]) - 1; ++$i) {
for ($j = 0; $j < count(ZMBuf::$events[$class_name]) - $i - 1; ++$j) {
$l1 = ZMBuf::$events[$class_name][$j]->level;
$l2 = ZMBuf::$events[$class_name][$j + 1]->level;
if ($l1 < $l2) {
$t = ZMBuf::$events[$class_name][$j + 1];
ZMBuf::$events[$class_name][$j + 1] = ZMBuf::$events[$class_name][$j];
ZMBuf::$events[$class_name][$j] = $t;
}
}
}
}
}
}
private static function getRuleCallback($rule_str) {
$func = null;
$rule = $rule_str;
if ($rule != "") {
$asp = explode(":", $rule);
$asp_name = array_shift($asp);
$rest = implode(":", $asp);
//Swoole 事件时走此switch
switch ($asp_name) {
case "connectType": //websocket连接类型
$func = function (WSConnection $connection) use ($rest) {
return $connection->getType() == $rest ? true : false;
};
break;
case "containsGet": //handle http request事件时才能用
case "containsPost":
$get_list = explode(",", $rest);
if ($asp_name == "containsGet")
$func = function ($request) use ($get_list) {
foreach ($get_list as $v) if (!isset($request->get[$v])) return false;
return true;
};
else
$func = function ($request) use ($get_list) {
foreach ($get_list as $v) if (!isset($request->post[$v])) return false;
return true;
};
/*
if ($controller_prefix != '') {
$p = ZMBuf::$req_mapping_node;
$prefix_exp = explode("/", $controller_prefix);
foreach ($prefix_exp as $k => $v) {
if ($v == "" || $v == ".." || $v == ".") {
unset($prefix_exp[$k]);
}
}
while (($shift = array_shift($prefix_exp)) !== null) {
$p->addRoute($shift, new MappingNode($shift));
$p = $p->getRoute($shift);
}
if ($p->getNodeName() != "/") {
$p->setMethod($method->getName());
$p->setClass($class->getName());
$p->setRule($func);
return "mapped";
}
}*/
break;
case "containsJson": //handle http request事件时才能用
$json_list = explode(",", $rest);
$func = function ($json) use ($json_list) {
foreach ($json_list as $v) if (!isset($json[$v])) return false;
return true;
};
break;
case "dataEqual": //handle websocket message事件时才能用
$func = function ($data) use ($rest) { return $data == $rest; };
break;
}
switch ($asp_name) {
case "msgMatch": //handle cq message事件时才能用
$func = function ($msg) use ($rest) { return matchPattern($rest, $msg); };
break;
case "msgEqual": //handle cq message事件时才能用
$func = function ($msg) use ($rest) { return trim($msg) == $rest; };
break;
}
}
return $func;
}
private static function registerRuleEvent(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
$vss->callback = self::getRuleCallback($vss->getRule());
$vss->method = $method->getName();
$vss->class = $class->getName();
return $vss;
}
private static function registerMethod(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
$vss->method = $method->getName();
$vss->class = $class->getName();
return $vss;
}
private static function registerRequestMapping(RequestMapping $vss, ReflectionMethod $method, ReflectionClass $class, string $prefix) {
$prefix_exp = explode("/", $prefix);
$route_exp = explode("/", $vss->route);
foreach ($prefix_exp as $k => $v) {
if ($v == "" || $v == ".." || $v == ".") {
unset($prefix_exp[$k]);
}
}
foreach ($route_exp as $k => $v) {
if ($v == "" || $v == ".." || $v == ".") {
unset($route_exp[$k]);
}
}
$a = ZMBuf::$req_mapping_node;
$p = $a;
if ($prefix_exp == [] && $route_exp == []) {
$p->setMethod($method->getName());
$p->setClass($class->getName());
$p->setRequestMethod($vss->request_method);
return;
}
while (($shift = array_shift($prefix_exp)) !== null) {
$p->addRoute($shift, new MappingNode($shift));
$p = $p->getRoute($shift);
}
while (($shift = array_shift($route_exp)) !== null) {
if (mb_substr($shift, 0, 1) == "{" && mb_substr($shift, -1, 1) == "}") {
$p->removeAllRoute();
}
$p->addRoute($shift, new MappingNode($shift));
$p = $p->getRoute($shift);
}
$p->setMethod($method->getName());
$p->setClass($class->getName());
$p->setRequestMethod($vss->request_method);
}
private static function loadAnnotationClasses() {
$class = getAllClasses(WORKING_DIR . "/src/ZM/Annotation/", "ZM\\Annotation");
foreach ($class as $v) {
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
require_once $s;
}
$class = getAllClasses(WORKING_DIR . "/src/Custom/Annotation/", "Custom\\Annotation");
foreach ($class as $v) {
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
require_once $s;
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace ZM\Annotation\CQ;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
* Class CQAfter
* @Annotation
* @Target("METHOD")
* @package ZM\Annotation\CQ
*/
class CQAfter extends AnnotationBase
{
/**
* @var string
* @Required()
*/
public $cq_event;
}

View File

@@ -0,0 +1,42 @@
<?php
namespace ZM\Annotation\CQ;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
/**
* Class CQBefore
* @Annotation
* @Target("METHOD")
* @package ZM\Annotation\CQ
*/
class CQBefore extends AnnotationBase implements Level
{
/**
* @var string
* @Required()
*/
public $cq_event;
public $level = 20;
/**
* @return mixed
*/
public function getLevel() {
return $this->level;
}
/**
* @param mixed $level
*/
public function setLevel($level): void {
$this->level = $level;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace ZM\Annotation\CQ;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
/**
* Class CQCommand
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\CQ
*/
class CQCommand extends AnnotationBase implements Level
{
/** @var string */
public $match = "";
/** @var string */
public $regexMatch = "";
/** @var int */
public $level = 20;
/**
* @return int
*/
public function getLevel(): int { return $this->level; }
/**
* @param int $level
*/
public function setLevel(int $level) { $this->level = $level; }
}

View File

@@ -0,0 +1,41 @@
<?php
namespace ZM\Annotation\CQ;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
/**
* Class CQMessage
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\CQ
*/
class CQMessage extends AnnotationBase implements Level
{
/**
* @var string
*/
public $message_type = "";
/** @var int */
public $user_id = 0;
/** @var int */
public $group_id = 0;
/** @var int */
public $discuss_id = 0;
/** @var string */
public $message = "";
/** @var string */
public $raw_message = "";
/** @var int */
public $level = 20;
public function getLevel() { return $this->level; }
public function setLevel(int $level) {
$this->level = $level;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace ZM\Annotation\CQ;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
/**
* Class CQMetaEvent
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\CQ
*/
class CQMetaEvent extends AnnotationBase implements Level
{
/**
* @var string
* @Required()
*/
public $meta_event_type = '';
/** @var string */
public $sub_type = '';
/** @var int */
public $level;
/**
* @return mixed
*/
public function getLevel() { return $this->level; }
/**
* @param int $level
*/
public function setLevel(int $level): void {
$this->level = $level;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace ZM\Annotation\CQ;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
/**
* Class CQNotice
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\CQ
*/
class CQNotice extends AnnotationBase implements Level
{
/** @var string */
public $notice_type = "";
/** @var string */
public $sub_type = "";
/** @var int */
public $group_id = 0;
/** @var int */
public $operator_id = 0;
/** @var int */
public $level = 20;
/**
* @return int
*/
public function getLevel(): int {
return $this->level;
}
/**
* @param int $level
*/
public function setLevel(int $level): void {
$this->level = $level;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace ZM\Annotation\CQ;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
/**
* Class CQRequest
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\CQ
*/
class CQRequest extends AnnotationBase implements Level
{
/** @var string */
public $request_type = "";
/** @var string */
public $sub_type = "";
/** @var int */
public $user_id = 0;
/** @var string */
public $comment = "";
/** @var int */
public $level = 20;
/**
* @return int
*/
public function getLevel(): int {
return $this->level;
}
/**
* @param int $level
*/
public function setLevel(int $level): void {
$this->level = $level;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace ZM\Annotation\Http;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
* Class Controller
* @Annotation
* @Target("CLASS")
* @package ZM\Annotation\Http
*/
class Controller extends AnnotationBase
{
/**
* @var string
* @Required()
*/
public $prefix = '';
}

View File

@@ -0,0 +1,39 @@
<?php
namespace ZM\Annotation\Http;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
* Class RequestMapping
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\Http
*/
class RequestMapping extends AnnotationBase
{
/**
* @var string
* @Required()
*/
public $route = '';
/**
* @var string
*/
public $name = '';
/**
* @var array
*/
public $request_method = [RequestMethod::GET, RequestMethod::POST];
/**
* Routing path params binding. eg. {"id"="\d+"}
* @var array
*/
public $params = [];
}

View File

@@ -0,0 +1,30 @@
<?php
namespace ZM\Annotation\Http;
use Doctrine\Common\Annotations\Annotation\Required;
use ZM\Annotation\AnnotationBase;
/**
* Class RequestMethod
* @Annotation
*
* @package ZM\Annotation\Http
*/
class RequestMethod extends AnnotationBase
{
/**
* @var string
* @Required()
*/
public $method = self::GET;
public const GET = 'GET';
public const POST = 'POST';
public const PUT = 'PUT';
public const PATCH = 'PATCH';
public const DELETE = 'DELETE';
public const OPTIONS = 'OPTIONS';
public const HEAD = 'HEAD';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace ZM\Annotation\Interfaces;
interface CustomAnnotation
{
}

View File

@@ -0,0 +1,12 @@
<?php
namespace ZM\Annotation\Interfaces;
interface Level
{
public function getLevel();
public function setLevel(int $level);
}

View File

@@ -0,0 +1,10 @@
<?php
namespace ZM\Annotation\Interfaces;
interface Rule
{
public function getRule();
}

View File

@@ -0,0 +1,103 @@
<?php
namespace ZM\Annotation;
use Closure;
class MappingNode
{
private $node;
/** @var MappingNode[] */
private $route = [];
private $method = null;
private $class = null;
private $request_method = [];
/** @var Closure|null */
private $rule = null;
public function __construct(string $node_name) { $this->node = $node_name; }
public function addRoute(string $route_name, MappingNode $route_node) { $this->route[$route_name] = $route_node; }
/**
* @param string $shift
* @return MappingNode|null
*/
public function getRoute(string $shift) {
return $this->route[$shift] ?? null;
}
public function getRealRoute(string $shift, array &$bind_params) {
if (mb_substr(key($this->route), 0, 1) == "{" && mb_substr(key($this->route), -1, 1) == "}") {
$param_name = mb_substr(current($this->route)->getNodeName(), 1, -1);
$bind_params[$param_name] = $shift;
return current($this->route);
} else return $this->route[$shift] ?? null;
}
public function setMethod($method) { $this->method = $method; }
public function setClass($class) { $this->class = $class; }
public function setRequestMethod($method) {
if (is_string($method)) $this->request_method = [$method];
else $this->request_method = $method;
}
public function getNodeName() { return $this->node; }
public function getRule() { return $this->rule; }
public function setRule(Closure $rule): void { $this->rule = $rule; }
public function removeAllRoute() { $this->route = []; }
/**
* @return null
*/
public function getMethod() {
return $this->method;
}
/**
* @return array
*/
public function getRequestMethod(): array {
return $this->request_method;
}
/**
* @return null
*/
public function getClass() {
return $this->class;
}
/**
* @return string
*/
public function getNode(): string {
return $this->node;
}
public function __toString() {
$str = "[" . $this->node . "] => ";
if ($this->class != "" && $this->class != null)
$str .= "\n\t" . $this->class . "->" . $this->method . ": " . implode(", ", $this->request_method);
$str .= "\n\t[Route] => [";
foreach ($this->route as $k => $v) {
$r = $v;
$r = explode("\n", $r);
foreach ($r as $ks => $vs) {
$r[$ks] = "\t" . $r[$ks];
}
$r = implode("\n", $r);
$str .= "\n\t" . $r;
}
$str .= "\n]";
return $str;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace ZM\Annotation\Module;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
* Class Closed
* @Annotation
* @Target("CLASS")
* @package ZM\Annotation\Module
*/
class Closed extends AnnotationBase
{
}

View File

@@ -0,0 +1,25 @@
<?php
namespace ZM\Annotation\Module;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* Class SaveBuffer
* @Annotation
* @Target("CLASS")
* @package ZM\Annotation\Module
*/
class SaveBuffer
{
/**
* @var string
*@Required()
*/
public $buf_name;
/** @var string|null $sub_folder */
public $sub_folder = null;
}

View File

@@ -0,0 +1,76 @@
<?php
namespace ZM\Annotation\Swoole;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
use ZM\Annotation\Interfaces\Rule;
/**
* Class SwooleEventAfter
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\Swoole
*/
class SwooleEventAfter extends AnnotationBase implements Rule, Level
{
/**
* @var string
* @Required
*/
public $type;
/** @var string */
public $rule = "";
/** @var int */
public $level = 20;
/**
* @return string
*/
public function getType(): string {
return $this->type;
}
/**
* @param string $type
*/
public function setType(string $type): void {
$this->type = $type;
}
/**
* @return string
*/
public function getRule(): string {
return $this->rule;
}
/**
* @param string $rule
*/
public function setRule(string $rule): void {
$this->rule = $rule;
}
/**
* @return int
*/
public function getLevel(): int {
return $this->level;
}
/**
* @param int $level
*/
public function setLevel(int $level): void {
$this->level = $level;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace ZM\Annotation\Swoole;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Level;
use ZM\Annotation\Interfaces\Rule;
/**
* Class SwooleEventAt
* @Annotation
* @Target("ALL")
* @package ZM\Annotation\Swoole
*/
class SwooleEventAt extends AnnotationBase implements Rule, Level
{
/**
* @var string
* @Required
*/
public $type;
/** @var string */
public $rule = "";
/** @var int */
public $level = 20;
public $callback = null;
/**
* @return string
*/
public function getType(): string {
return $this->type;
}
/**
* @param string $type
*/
public function setType(string $type): void {
$this->type = $type;
}
/**
* @return string
*/
public function getRule(): string {
return $this->rule;
}
/**
* @param string $rule
*/
public function setRule(string $rule): void {
$this->rule = $rule;
}
/**
* @return int
*/
public function getLevel(): int {
return $this->level;
}
/**
* @param int $level
*/
public function setLevel(int $level): void {
$this->level = $level;
}
}