├── .env.example ├── .gitignore ├── react ├── WebSocket │ ├── WsUser.php │ ├── Dispatcher.php │ ├── MiddlewareInterface.php │ ├── MessageRoute.php │ └── Event.php ├── Annotations │ ├── Command.php │ ├── Service.php │ ├── RequestMethod.php │ ├── Middleware.php │ ├── Middlewares.php │ ├── AnnotationFactory.php │ └── RequestMapping.php ├── Providers │ ├── ServiceProviderInterface.php │ ├── WebSocketServiceProvider.php │ ├── RegisterServiceProvider.php │ ├── BusinessServiceProvider.php │ ├── GatewayServiceProvider.php │ ├── ConfigServiceProvider.php │ ├── HttpServiceProvider.php │ ├── EnvServiceProvider.php │ ├── ConsoleServiceProvider.php │ ├── DispatcherServiceProvider.php │ └── RouteServiceProvider.php ├── Helper │ ├── Functions.php │ ├── RouteHelper.php │ └── DirectoryHelper.php ├── Factorys │ ├── CreateWorkermenResponse.php │ └── CreateWorkermenRequest.php ├── ReactApp.php ├── Http │ ├── Server.php │ └── ServerRequest.php ├── Middlewares │ └── ResponseFactoryMiddleware.php └── Protocols │ └── BusinessWorker.php ├── config ├── ws.php └── config.php ├── bin ├── define.php ├── server └── bootstrap.php ├── app ├── WebSocket │ ├── User.php │ ├── Controllers │ │ └── HelloWorldController.php │ └── Events │ │ └── WsEvent.php ├── App.php ├── Annotations │ └── MessageMapping.php ├── Commands │ ├── Http │ │ ├── HttpCommand.php │ │ ├── StopCommand.php │ │ └── StartCommand.php │ └── WebSocket │ │ ├── WebSocketCommand.php │ │ ├── StopCommand.php │ │ ├── StartDebugCommand.php │ │ └── StartCommand.php ├── Http │ └── Controllers │ │ ├── LoginController.php │ │ └── HelloWorldController.php ├── Providers │ └── EmailServiceProvider.php └── Middlewares │ ├── WhoopsMiddleware.php │ ├── LoginMiddleware.php │ └── CorsMiddleware.php ├── debug.php ├── README.md └── composer.json /.env.example: -------------------------------------------------------------------------------- 1 | HTTP_SOCKET="http://0.0.0.0:8080" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .buildpath 2 | .settings/ 3 | .project 4 | *.patch 5 | .idea/ 6 | .git/ 7 | runtime/ 8 | vendor/ 9 | temp/ 10 | *.lock 11 | .phpintel/ 12 | .env 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /react/WebSocket/WsUser.php: -------------------------------------------------------------------------------- 1 | [ 11 | /** 12 | * 全局中间件 13 | */ 14 | 'middleware' => [ 15 | 16 | ], 17 | 18 | 19 | 20 | ], 21 | ]; -------------------------------------------------------------------------------- /bin/define.php: -------------------------------------------------------------------------------- 1 | run(); 18 | -------------------------------------------------------------------------------- /bin/bootstrap.php: -------------------------------------------------------------------------------- 1 | route = $values['route']??$values['value']??''; 24 | } 25 | 26 | public function getRoute() 27 | { 28 | return $this->route; 29 | } 30 | } -------------------------------------------------------------------------------- /react/Annotations/Service.php: -------------------------------------------------------------------------------- 1 | name = $values["name"]??$values["value"]; 25 | $this->sort = $values["sort"]??100; 26 | } 27 | } -------------------------------------------------------------------------------- /react/Helper/Functions.php: -------------------------------------------------------------------------------- 1 | run(); -------------------------------------------------------------------------------- /react/Providers/WebSocketServiceProvider.php: -------------------------------------------------------------------------------- 1 | getQueryParams(),$request->getParsedBody()]; 26 | } 27 | } -------------------------------------------------------------------------------- /app/Providers/EmailServiceProvider.php: -------------------------------------------------------------------------------- 1 | handleUnconditionally(true); 24 | $run->pushHandler($PrettyPageHandler); 25 | 26 | parent::__construct($run, $system); 27 | } 28 | } -------------------------------------------------------------------------------- /react/Factorys/CreateWorkermenResponse.php: -------------------------------------------------------------------------------- 1 | call = $call; 22 | } 23 | 24 | public function getClosure() 25 | { 26 | return $this->call; 27 | } 28 | 29 | public function addMiddleware(MiddlewareInterface $middleware) 30 | { 31 | $this->middlewares[] = $middleware; 32 | } 33 | 34 | public function getMiddleware() 35 | { 36 | return $this->middlewares; 37 | } 38 | } -------------------------------------------------------------------------------- /app/Commands/Http/StopCommand.php: -------------------------------------------------------------------------------- 1 | setName('http:stop') 26 | ->setDescription('关闭http') 27 | ; 28 | } 29 | 30 | protected function execute(InputInterface $input, OutputInterface $output) 31 | { 32 | $this->setWorker('stop'); 33 | } 34 | } -------------------------------------------------------------------------------- /app/Commands/WebSocket/StopCommand.php: -------------------------------------------------------------------------------- 1 | setName('ws:stop') 27 | ->setDescription('关闭所有worker') 28 | ; 29 | } 30 | 31 | protected function execute(InputInterface $input, OutputInterface $output) 32 | { 33 | $this->setWorker("stop"); 34 | } 35 | } -------------------------------------------------------------------------------- /react/Annotations/Middleware.php: -------------------------------------------------------------------------------- 1 | class = $values['value']; 30 | } 31 | if (isset($values['class'])) { 32 | $this->class = $values['class']; 33 | } 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | public function getClass(): string 40 | { 41 | return $this->class; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/Middlewares/LoginMiddleware.php: -------------------------------------------------------------------------------- 1 | handle($request); 30 | } 31 | } -------------------------------------------------------------------------------- /react/WebSocket/MessageRoute.php: -------------------------------------------------------------------------------- 1 | call = $call; 21 | } 22 | 23 | public function getClosure() 24 | { 25 | return $this->call; 26 | } 27 | 28 | public function setRoute($route) 29 | { 30 | $this->route = $route; 31 | } 32 | 33 | public function getRoute() 34 | { 35 | return $this->route; 36 | } 37 | 38 | public function addMiddleware(MiddlewareInterface $middleware) 39 | { 40 | $this->middleware[] = $middleware; 41 | } 42 | 43 | public function getMiddleware() 44 | { 45 | return $this->middleware; 46 | } 47 | } -------------------------------------------------------------------------------- /app/Commands/WebSocket/StartDebugCommand.php: -------------------------------------------------------------------------------- 1 | setName('ws:debug') 29 | ->setDescription('开启business服务') 30 | ; 31 | } 32 | 33 | protected function execute(InputInterface $input, OutputInterface $output) 34 | { 35 | App::getService("business")->listen(); 36 | 37 | $this->setWorker("start"); 38 | } 39 | } -------------------------------------------------------------------------------- /react/Providers/RegisterServiceProvider.php: -------------------------------------------------------------------------------- 1 | withQueryParams($date['get']); 30 | $request = $request->withParsedBody($date['post']); 31 | $request = $request->withUploadedFiles($date['files']); 32 | 33 | return $request; 34 | } 35 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 这是对workermen封装、规范、友好化的项目,对所有功能组件化,不重复造轮子,所有组件全部使用已存在的、热门的composer包 2 | 。 3 | 4 |

5 | Build Status 6 | License 7 |

8 | 9 | ## 主要特性 10 | 11 | - [ ] 注解加载,服务、路由、命令等等,自定义注解也友好 12 | - [ ] 严格遵循`psr`规范 13 | - [ ] 单一入口,控制台使用最入门的`symfony/console` 14 | - [ ] 异常捕捉,`whoops`使错误更漂亮的显示 15 | - [ ] 方便调试,可以使用`xdebug`调试程序 16 | - [ ] 内置定时任务解析器 17 | - [ ] 内置异步`task` 18 | 19 | ## 开发计划 20 | 21 | 因为开发资源有限,开发计划暂定如下: 22 | 23 | 1. 定时任务,更好的定时测试,提供界面改时间触发定时任务 24 | 2. 支持多种session启动,文件,redis等 25 | 3. 异步task封装 26 | 27 | git安装 28 | ~~~~ 29 | git clone https://github.com/ctfang/react.git 30 | cd react 31 | composer install 32 | ~~~~ 33 | 查看帮助命令 34 | ~~~~php 35 | php bin/server 36 | ~~~~ 37 | 38 | 启动http服务,如果需要使用80端口,加上 sudo 39 | ~~~~php 40 | // 守护模式 41 | php bin/server http:start --domain 42 | // 非守护模式 43 | php bin/server http:start 44 | // 关闭 45 | php bin/server http:stop 46 | ~~~~ 47 | -------------------------------------------------------------------------------- /app/WebSocket/Events/WsEvent.php: -------------------------------------------------------------------------------- 1 | [ 14 | 'socket' => env('HTTP_SOCKET', 'http://0.0.0.0:8080'), 15 | 'count' => env('HTTP_COUNT', 1), 16 | ], 17 | 18 | /** 19 | * 全局中间件 20 | */ 21 | 'middleware' => [ 22 | \App\Middlewares\WhoopsMiddleware::class, 23 | \App\Middlewares\CorsMiddleware::class 24 | ], 25 | 26 | 'register' => [ 27 | // 'socket' => env('REGISTER_SOCKET', '0.0.0.0'), 28 | 'connect' => env('REGISTER_OTHER', '127.0.0.1'), 29 | 'port' => env('REGISTER_PORT', 8060), 30 | ], 31 | 32 | 'business' => [ 33 | 'name' => env('BUSINESS_NAME', 'MyWsApp'), 34 | 'count' => env('BUSINESS_COUNT', 1), 35 | ], 36 | 37 | /** 38 | * WebSocket配置 39 | */ 40 | 'gateway' => [ 41 | 'socket' => env('GATEWAY_SOCKET', 'WebSocket://0.0.0.0:8070'), 42 | 'name' => env('GATEWAY_NAME', 'WebSocket'), 43 | 'lan_ip' => env('GATEWAY_LANIP', '0.0.0.0'), 44 | 'start_port' => env('GATEWAY_START_PORT', '4000'), 45 | 'count' => env('GATEWAY_COUNT', 1), 46 | ], 47 | ]; -------------------------------------------------------------------------------- /react/Providers/BusinessServiceProvider.php: -------------------------------------------------------------------------------- 1 | name = App::config('business.name', 'business'); 50 | // bussinessWorker进程数量 51 | $worker->count = App::isLinux() ? App::config('business.count') : 1; 52 | // 服务注册地址 53 | $worker->registerAddress = App::config('register.connect').':'.App::config('register.port'); 54 | $worker->eventHandler = WsEvent::class; 55 | } 56 | } -------------------------------------------------------------------------------- /react/Annotations/Middlewares.php: -------------------------------------------------------------------------------- 1 | middlewares = $values['value']; 35 | } 36 | if (isset($values['middlewares'])) { 37 | $this->middlewares = $values['value']; 38 | } 39 | if (isset($values['group'])) { 40 | $this->group = $values['value']; 41 | } 42 | } 43 | 44 | /** 45 | * @return array 46 | */ 47 | public function getMiddlewares(): array 48 | { 49 | return $this->middlewares; 50 | } 51 | 52 | /** 53 | * @param array $middlewares 54 | * @return Middlewares 55 | */ 56 | public function setMiddlewares($middlewares) 57 | { 58 | $this->middlewares = $middlewares; 59 | return $this; 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | public function getGroup(): string 66 | { 67 | return $this->group; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/Commands/Http/StartCommand.php: -------------------------------------------------------------------------------- 1 | setName('http:start') 33 | ->addOption( 34 | "domain", 35 | null, 36 | InputOption::VALUE_OPTIONAL, 37 | "是否守护进程运行", 38 | 0 39 | ) 40 | ->setDescription('开启http服务') 41 | ; 42 | } 43 | 44 | protected function execute(InputInterface $input, OutputInterface $output) 45 | { 46 | $domain = $input->getOption("domain"); 47 | 48 | if( $domain || $domain===null ){ 49 | $output->writeln("守护进程启动"); 50 | 51 | Worker::$daemonize = true; 52 | } 53 | 54 | /** @var HttpServiceProvider $http */ 55 | $http = App::getService("http"); 56 | 57 | $http->listen(); 58 | 59 | $this->setWorker('start'); 60 | } 61 | } -------------------------------------------------------------------------------- /react/Providers/GatewayServiceProvider.php: -------------------------------------------------------------------------------- 1 | name = App::config('gateway.name'); 50 | // gateway进程数 51 | $gateway->count = App::config('gateway.count', 1); 52 | // 本机ip,分布式部署时使用内网ip 53 | $gateway->lanIp = App::config('gateway.lan_ip'); 54 | // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000 55 | // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口 56 | $gateway->startPort = App::config('gateway.start_port'); 57 | // 服务注册地址 58 | $gateway->registerAddress = App::config('register.connect').':'.App::config('register.port'); 59 | } 60 | } -------------------------------------------------------------------------------- /app/Middlewares/CorsMiddleware.php: -------------------------------------------------------------------------------- 1 | getMethod()==RequestMethod::OPTIONS ){ 36 | $response = Factory::createResponse(); 37 | $body = $response->getBody(); 38 | $body->write(''); 39 | $response = $response->withBody($body); 40 | $response = $response->withAddedHeader("Access-Control-Allow-Headers","content-type"); 41 | }else{ 42 | $response = $handler->handle($request); 43 | } 44 | $response = $response->withAddedHeader('Access-Control-Allow-Origin', "*"); 45 | return $response; 46 | } 47 | } -------------------------------------------------------------------------------- /react/Providers/ConfigServiceProvider.php: -------------------------------------------------------------------------------- 1 | path = realpath(__ROOT_PATH__ . '/config'); 31 | $this->default = realpath($this->path . '/' . $this->default); 32 | parent::__construct($data); 33 | } 34 | 35 | /** 36 | * 加载过程触发 37 | * 38 | * @return void 39 | */ 40 | public function boot() 41 | { 42 | foreach (glob($this->path . '/*.php') as $start_file) { 43 | if (realpath($start_file) == $this->default) { 44 | break; 45 | } 46 | $arr = include $start_file; 47 | if ($arr) { 48 | $this->merge($arr); 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * 所有服务加载后,注册触发, 55 | * 56 | * @return void 57 | */ 58 | public function register() 59 | { 60 | 61 | } 62 | 63 | /** 64 | * @return array|mixed 65 | */ 66 | protected function getDefaults() 67 | { 68 | if (file_exists($this->default)) { 69 | return include $this->default; 70 | } 71 | return []; 72 | } 73 | } -------------------------------------------------------------------------------- /react/Providers/HttpServiceProvider.php: -------------------------------------------------------------------------------- 1 | socket = App::config('http.socket', 'http://0.0.0.0:8080'); 49 | $this->count = App::config('http.count'); 50 | } 51 | 52 | public function listen() 53 | { 54 | $server = new Server(function (ServerRequestInterface $request) { 55 | /** @var DispatcherServiceProvider $dispatcher */ 56 | $dispatcher = App::getService('dispatcher'); 57 | return $dispatcher->dispatch($request); 58 | }); 59 | $socket = new Worker($this->socket); 60 | 61 | if( App::isLinux() ){ 62 | $socket->count = $this->count; 63 | } 64 | 65 | $server->listen($socket); 66 | } 67 | } -------------------------------------------------------------------------------- /app/Http/Controllers/HelloWorldController.php: -------------------------------------------------------------------------------- 1 | time(), 'query' => $request->getQueryParams(), 'parsed' => $request->getParsedBody()]; 28 | } 29 | 30 | /** 31 | * 错误显示 32 | * @RequestMapping("/error")CallableHandler 33 | */ 34 | public function index() 35 | { 36 | throw new \Exception("错误界面显示"); 37 | } 38 | 39 | /** 40 | * 谷歌镜像 41 | * @RequestMapping("/google") 42 | * @param ServerRequestInterface $request 43 | * @throws \Exception 44 | */ 45 | public function google(ServerRequestInterface $request) 46 | { 47 | $google = "www.baidu.com"; 48 | 49 | $conToGoogle = new AsyncTcpConnection("tcp://{$google}:443"); 50 | $conToGoogle->transport = 'ssl'; 51 | $conToGoogle->onConnect = function ($conToGoogle)use($google) { 52 | $conToGoogle->send("GET / HTTP/1.1\r\nHost: {$google}\r\nConnection: keep-alive\r\n\r\n"); 53 | }; 54 | $conToGoogle->onMessage = function ($conToGoogle, $http_buffer) use ($request) { 55 | $connection = $request->getAttribute('connection'); 56 | $connection->send($http_buffer,true); 57 | }; 58 | $conToGoogle->connect(); 59 | } 60 | } -------------------------------------------------------------------------------- /app/Commands/WebSocket/StartCommand.php: -------------------------------------------------------------------------------- 1 | setName('ws:start') 30 | ->addOption( 31 | "domain", 32 | null, 33 | InputOption::VALUE_OPTIONAL, 34 | "是否守护进程运行", 35 | 0 36 | ) 37 | ->addOption( 38 | 'debug', 39 | null, 40 | InputOption::VALUE_OPTIONAL, 41 | '是否启用隔离调试', 42 | 0 43 | ) 44 | ->setDescription('开启WebSocket服务') 45 | ; 46 | } 47 | 48 | protected function execute(InputInterface $input, OutputInterface $output) 49 | { 50 | $domain = $input->getOption("domain"); 51 | $debug = $input->getOption("debug"); 52 | 53 | if( $domain || $domain===null ){ 54 | $output->writeln("守护进程启动"); 55 | 56 | Worker::$daemonize = true; 57 | } 58 | 59 | App::getService("register")->listen(); 60 | App::getService("gateway")->listen(); 61 | 62 | if( $debug===null || $debug ){ 63 | // 启用隔离 64 | $output->writeln("开启隔离;需要另外开启ws:debug处理"); 65 | }else{ 66 | App::getService("business")->listen(); 67 | } 68 | 69 | $this->setWorker("debug"); 70 | } 71 | } -------------------------------------------------------------------------------- /react/ReactApp.php: -------------------------------------------------------------------------------- 1 | get($key,$default); 89 | } 90 | } -------------------------------------------------------------------------------- /react/Providers/EnvServiceProvider.php: -------------------------------------------------------------------------------- 1 | load(); 35 | 36 | if (isset($_ENV)) { 37 | foreach ($_ENV as $key => $value) { 38 | switch (strtolower($value)) { 39 | case 'true': 40 | case '(true)': 41 | $value = true; 42 | break; 43 | case 'false': 44 | case '(false)': 45 | $value = false; 46 | break; 47 | case 'empty': 48 | case '(empty)': 49 | $value = ''; 50 | break; 51 | case 'null': 52 | case '(null)': 53 | $value = null; 54 | break; 55 | default: 56 | if( ($strLen = strlen($value)) > 1 && $value{0}==='"' && $value{$strLen}==='"' ){ 57 | $value = substr($value,1,$strLen-1); 58 | } 59 | break; 60 | } 61 | $_ENV[$key] = $value; 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * 所有服务加载后,注册触发, 68 | * 69 | * @return void 70 | */ 71 | public function register() 72 | { 73 | // TODO: Implement register() method. 74 | } 75 | } -------------------------------------------------------------------------------- /react/Http/Server.php: -------------------------------------------------------------------------------- 1 | requestHandler = $requestHandler; 34 | } 35 | 36 | /** 37 | * @param Worker $socket 38 | * @throws \Exception 39 | */ 40 | public function listen(Worker $socket) 41 | { 42 | $socket->onMessage = function (TcpConnection $connection, $data) { 43 | 44 | $request = CreateWorkermenRequest::create($data); 45 | 46 | $request = $request->withAttribute('connection',$connection); 47 | 48 | /** @var ResponseInterface $response */ 49 | $response = ($this->requestHandler)($request); 50 | 51 | $this->handleResponse($connection,$response); 52 | }; 53 | 54 | $socket->listen(); 55 | } 56 | 57 | /** 58 | * @param TcpConnection $connection 59 | * @param ResponseInterface $response 60 | */ 61 | public function handleResponse(TcpConnection $connection, ResponseInterface $response) 62 | { 63 | $body = $response->getBody(); 64 | 65 | $headers = $response->getHeaders(); 66 | 67 | foreach ($headers as $key=>$value){ 68 | Http::header($key.":".implode(";",$value)); 69 | } 70 | 71 | $code = $response->getStatusCode(); 72 | 73 | Http::header('Http-Code:',false,$code); 74 | 75 | if( $body->getSize() ){ 76 | $connection->send($body); 77 | }else{ 78 | $connection->send(''); 79 | $connection->close(); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /react/Providers/ConsoleServiceProvider.php: -------------------------------------------------------------------------------- 1 | setLoader($loader); 43 | $directory->setScanNamespace($this->namespaces); 44 | $reader = new AnnotationReader(); 45 | 46 | foreach ($directory->scanClass() as $class) { 47 | if (class_exists($class)) { 48 | 49 | $reflectionClass = new \ReflectionClass($class); 50 | $classAnnotations = $reader->getClassAnnotations($reflectionClass); 51 | foreach ($classAnnotations AS $annotation) { 52 | if( $annotation instanceof \ReactApp\Annotations\Command){ 53 | $this->commands[] = $class; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | /** 61 | * 所有服务加载后,注册触发, 62 | * 63 | * @return void 64 | */ 65 | public function register() 66 | { 67 | $application = new Application("workermen http and ws server","0.1.2"); 68 | 69 | foreach ($this->commands as $command){ 70 | $class = new $command(); 71 | if( $class instanceof Command){ 72 | $application->add(new $command()); 73 | } 74 | } 75 | 76 | $this->application = $application; 77 | unset($this->commands); 78 | } 79 | 80 | public function run() 81 | { 82 | $this->application->run(); 83 | } 84 | } -------------------------------------------------------------------------------- /react/Middlewares/ResponseFactoryMiddleware.php: -------------------------------------------------------------------------------- 1 | getAttribute("request-handler"), [$request,$handler]); 42 | 43 | if ($return instanceof ResponseInterface) { 44 | $response = $return; 45 | $return = ''; 46 | } elseif ( is_array($return) ){ 47 | $response = Factory::createResponse(); 48 | $response = $response->withAddedHeader("Content-Type",["application/json","charset=utf-8"]); 49 | $return = json_encode($return); 50 | }elseif (is_null($return) || is_scalar($return) || (is_object($return) && method_exists($return, '__toString'))) { 51 | $response = Factory::createResponse(); 52 | } else { 53 | throw new \UnexpectedValueException( 54 | 'The value returned must be scalar or an object with __toString method' 55 | ); 56 | } 57 | 58 | while (ob_get_level() >= $level) { 59 | $return = ob_get_clean().$return; 60 | } 61 | 62 | $body = $response->getBody(); 63 | 64 | if ($return !== '' && $body->isWritable()) { 65 | $body->write($return); 66 | } 67 | 68 | return $response; 69 | } catch (\Exception $exception) { 70 | while (ob_get_level() >= $level) { 71 | ob_end_clean(); 72 | } 73 | 74 | throw $exception; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /react/Annotations/AnnotationFactory.php: -------------------------------------------------------------------------------- 1 | setLoader($loader); 36 | $directory->setScanNamespace($namespaces); 37 | 38 | AnnotationRegistry::registerLoader(array($loader, "loadClass")); 39 | 40 | self::scanAnnotation($directory); 41 | } 42 | 43 | /** 44 | * 索引所有注解 45 | * @param DirectoryHelper $directory 46 | * @throws \Doctrine\Common\Annotations\AnnotationException 47 | * @throws \ReflectionException 48 | */ 49 | public static function scanAnnotation(DirectoryHelper $directory) 50 | { 51 | $reader = new AnnotationReader(); 52 | $serviceAnnotations = []; 53 | foreach ($directory->scanClass() as $class) { 54 | if (class_exists($class)) { 55 | $reflectionClass = new ReflectionClass($class); 56 | $classAnnotations = $reader->getClassAnnotations($reflectionClass); 57 | 58 | foreach ($classAnnotations AS $annotation) { 59 | if ($annotation instanceof Service) { 60 | if( isset($serviceAnnotations[$annotation->name]) ) continue; 61 | $annotation->className = $class; 62 | $serviceAnnotations[$annotation->name] = $annotation; 63 | } 64 | } 65 | } 66 | } 67 | 68 | foreach (App::$before as $i=>$name){ 69 | $annotation = $serviceAnnotations[$name]; 70 | $annotation->sort = 90000+$i; 71 | } 72 | 73 | foreach ($serviceAnnotations as $name=>$annotation){ 74 | $serviceAnnotations[($annotation->sort+10000).$name] = $annotation; 75 | unset($serviceAnnotations[$name]); 76 | } 77 | 78 | ksort($serviceAnnotations); 79 | 80 | foreach ($serviceAnnotations as $annotation){ 81 | $class = $annotation->className; 82 | $object = new $class(); 83 | $object->boot(); 84 | App::setService($annotation->name,$object); 85 | } 86 | 87 | foreach ($serviceAnnotations as $annotation){ 88 | App::getService($annotation->name)->register(); 89 | } 90 | unset($serviceAnnotations); 91 | } 92 | } -------------------------------------------------------------------------------- /react/Annotations/RequestMapping.php: -------------------------------------------------------------------------------- 1 | route = $values['value']; 54 | } 55 | 56 | if (isset($values['route'])) { 57 | $this->route = $values['route']; 58 | } 59 | 60 | if (isset($values['method'])) { 61 | $method = $values['method']; 62 | $this->method = (array)$method; 63 | $this->method = array_change_key_case($this->method,CASE_UPPER); 64 | } 65 | 66 | if (isset($values['params'])) { 67 | $this->params = $values['params']; 68 | } 69 | 70 | if (isset($values['defaults'])) { 71 | $this->defaults = $values['defaults']; 72 | } 73 | } 74 | 75 | /** 76 | * 获取路由 77 | * 78 | * @return string 79 | */ 80 | public function getRoute(): string 81 | { 82 | return $this->route; 83 | } 84 | 85 | 86 | /** 87 | * 获取路由 88 | * 89 | * @param string $route 90 | * @return string 91 | */ 92 | public function setRoute(string $route): string 93 | { 94 | return $this->route = $route; 95 | } 96 | 97 | /** 98 | * 获取方法集合 99 | * 100 | * @return array 101 | */ 102 | public function getMethod(): array 103 | { 104 | return $this->method; 105 | } 106 | 107 | /** 108 | * @return array 109 | */ 110 | public function getParams(): array 111 | { 112 | return $this->params; 113 | } 114 | 115 | /** 116 | * @param array $params 117 | */ 118 | public function setParams(array $params) 119 | { 120 | $this->params = $params; 121 | } 122 | 123 | /** 124 | * @return array 125 | */ 126 | public function getDefaults(): array 127 | { 128 | return $this->defaults; 129 | } 130 | 131 | /** 132 | * @param array $defaults 133 | */ 134 | public function setDefaults(array $defaults) 135 | { 136 | $this->defaults = $defaults; 137 | } 138 | } -------------------------------------------------------------------------------- /react/Protocols/BusinessWorker.php: -------------------------------------------------------------------------------- 1 | _gatewayAddresses = array(); 41 | foreach ($addresses as $addr) { 42 | $this->_gatewayAddresses[$addr] = $addr; 43 | } 44 | $this->checkGatewayConnections($addresses); 45 | break; 46 | default: 47 | echo "Receive bad event:$event from Register.\n"; 48 | } 49 | } 50 | 51 | 52 | /** 53 | * 尝试连接 Gateway 内部通讯地址 54 | * 55 | * @param string $addr 56 | * @throws \Exception 57 | */ 58 | public function tryToConnectGateway($addr) 59 | { 60 | if (!isset($this->gatewayConnections[$addr]) && !isset($this->_connectingGatewayAddresses[$addr]) && isset($this->_gatewayAddresses[$addr])) { 61 | 62 | $conAddr = str_replace('0.0.0.0','127.0.0.1',$addr); 63 | 64 | $gateway_connection = new AsyncTcpConnection("GatewayProtocol://$conAddr"); 65 | $gateway_connection->remoteAddress = $addr; 66 | $gateway_connection->onConnect = array($this, 'onConnectGateway'); 67 | $gateway_connection->onMessage = array($this, 'onGatewayMessage'); 68 | $gateway_connection->onClose = array($this, 'onGatewayClose'); 69 | $gateway_connection->onError = array($this, 'onGatewayError'); 70 | $gateway_connection->maxSendBufferSize = $this->sendToGatewayBufferSize; 71 | if (TcpConnection::$defaultMaxSendBufferSize == $gateway_connection->maxSendBufferSize) { 72 | $gateway_connection->maxSendBufferSize = 50 * 1024 * 1024; 73 | } 74 | $gateway_data = GatewayProtocol::$empty; 75 | $gateway_data['cmd'] = GatewayProtocol::CMD_WORKER_CONNECT; 76 | $gateway_data['body'] = json_encode(array( 77 | 'worker_key' =>"{$this->name}:{$this->id}", 78 | 'secret_key' => $this->secretKey, 79 | )); 80 | $gateway_connection->send($gateway_data); 81 | $gateway_connection->connect(); 82 | $this->_connectingGatewayAddresses[$addr] = $addr; 83 | } 84 | unset($this->_waitingConnectGatewayAddresses[$addr]); 85 | } 86 | } -------------------------------------------------------------------------------- /react/Http/ServerRequest.php: -------------------------------------------------------------------------------- 1 | serverParams = $serverParams; 48 | } 49 | 50 | public function getServerParams() 51 | { 52 | return $this->serverParams; 53 | } 54 | 55 | public function getCookieParams() 56 | { 57 | return $this->cookies; 58 | } 59 | 60 | public function withCookieParams(array $cookies) 61 | { 62 | $new = clone $this; 63 | $new->cookies = $cookies; 64 | return $new; 65 | } 66 | 67 | public function getQueryParams() 68 | { 69 | return $this->queryParams; 70 | } 71 | 72 | public function withQueryParams(array $query) 73 | { 74 | $new = clone $this; 75 | $new->queryParams = $query; 76 | return $new; 77 | } 78 | 79 | public function getUploadedFiles() 80 | { 81 | return $this->fileParams; 82 | } 83 | 84 | public function withUploadedFiles(array $uploadedFiles) 85 | { 86 | $new = clone $this; 87 | $new->fileParams = $uploadedFiles; 88 | return $new; 89 | } 90 | 91 | public function getParsedBody() 92 | { 93 | return $this->parsedBody; 94 | } 95 | 96 | public function withParsedBody($data) 97 | { 98 | $new = clone $this; 99 | $new->parsedBody = $data; 100 | return $new; 101 | } 102 | 103 | public function getAttributes() 104 | { 105 | return $this->attributes; 106 | } 107 | 108 | public function getAttribute($name, $default = null) 109 | { 110 | if (!array_key_exists($name, $this->attributes)) { 111 | return $default; 112 | } 113 | return $this->attributes[$name]; 114 | } 115 | 116 | public function withAttribute($name, $value) 117 | { 118 | $new = clone $this; 119 | $new->attributes[$name] = $value; 120 | return $new; 121 | } 122 | 123 | public function withoutAttribute($name) 124 | { 125 | $new = clone $this; 126 | unset($new->attributes[$name]); 127 | return $new; 128 | } 129 | } -------------------------------------------------------------------------------- /react/Providers/DispatcherServiceProvider.php: -------------------------------------------------------------------------------- 1 | responseFactory = new ResponseFactoryMiddleware(); 55 | $this->responder = function ($request, $next) { 56 | return Factory::createResponse(); 57 | }; 58 | } 59 | 60 | /** 61 | * 所有服务加载后,注册触发, 62 | * 63 | * @return void 64 | */ 65 | public function register() 66 | { 67 | foreach (App::config('middleware') as $middleware) { 68 | $this->defaultMiddleware[] = new $middleware(); 69 | } 70 | /** @var RouteServiceProvider $router */ 71 | $router = App::getService('route'); 72 | $this->route = $router->getDispatcher(); 73 | } 74 | 75 | /** 76 | * 调度处理 77 | * 78 | * @param ServerRequestInterface $request 79 | * @return ResponseInterface 80 | */ 81 | public function dispatch(ServerRequestInterface $request): ResponseInterface 82 | { 83 | $route = $this->route->dispatch($request->getMethod(), $request->getUri()->getPath()); 84 | 85 | if ($route[0] === Dispatcher::NOT_FOUND) { 86 | return Factory::createResponse(404); 87 | } 88 | 89 | foreach ($route[2] as $name => $value) { 90 | $request = $request->withAttribute($name, $value); 91 | } 92 | 93 | /** @var RouteHelper $routeHelper */ 94 | $routeHelper = $route[1]; 95 | $queue = $this->defaultMiddleware; 96 | $queue = array_merge($queue, $routeHelper->getMiddleware()); 97 | $queue[] = $this->responseFactory; 98 | $queue[] = $this->responder; 99 | $relay = new Relay($queue); 100 | $request = $this->setHandler($request, $routeHelper->getClosure()); 101 | return $relay->handle($request); 102 | } 103 | 104 | /** 105 | * Set the handler reference on the request. 106 | * 107 | * @param ServerRequestInterface $request 108 | * @param mixed $handler 109 | * @return ServerRequestInterface 110 | */ 111 | protected function setHandler(ServerRequestInterface $request, $handler): ServerRequestInterface 112 | { 113 | return $request->withAttribute($this->attribute, $handler); 114 | } 115 | } -------------------------------------------------------------------------------- /react/Helper/DirectoryHelper.php: -------------------------------------------------------------------------------- 1 | dirBindNameSpace[$directory] = $nameSpace; 36 | } 37 | 38 | /** 39 | * 设置搜索文件的扩展名 40 | * 41 | * @param $extensions 42 | */ 43 | public function setExtensions($extensions) 44 | { 45 | $this->extensions = $extensions; 46 | } 47 | 48 | /** 49 | * 遍历目录下所有类 50 | * 51 | * @return \Generator 52 | */ 53 | public function scanClass() 54 | { 55 | foreach ($this->dirBindNameSpace as $directory => $nameSpace) { 56 | foreach ($this->scanNameSpace($directory, $nameSpace) as $class) { 57 | yield $class; 58 | } 59 | } 60 | } 61 | 62 | /** 63 | * 遍历命名空间 64 | * 65 | * @param $directory 66 | * @param $nameSpace 67 | * @return \Generator 68 | */ 69 | private function scanNameSpace($directory, $nameSpace) 70 | { 71 | if ($dh = opendir($directory)) { 72 | while (($file = readdir($dh)) !== false) { 73 | if (in_array($file, ['.', '..'])) { 74 | continue; 75 | } elseif (is_dir($directory . DIRECTORY_SEPARATOR . $file)) { 76 | foreach ($this->scanNameSpace($directory . DIRECTORY_SEPARATOR . $file, $nameSpace . $file . "\\") as $arrValue) { 77 | yield $arrValue; 78 | } 79 | } elseif (substr($file, -4) == $this->extensions) { 80 | yield $nameSpace . pathinfo($file, PATHINFO_FILENAME); 81 | } 82 | } 83 | closedir($dh); 84 | } 85 | } 86 | 87 | /** 88 | * 设置类加载器 89 | * @param ClassLoader $loader 90 | */ 91 | public function setLoader(ClassLoader $loader) 92 | { 93 | $this->loader = $loader; 94 | } 95 | 96 | /** 97 | * @return ClassLoader 98 | */ 99 | public function getLoader() 100 | { 101 | return $this->loader; 102 | } 103 | 104 | /** 105 | * @param $namespaces 106 | */ 107 | public function setScanNamespace($namespaces) 108 | { 109 | foreach ($namespaces as $namespace){ 110 | foreach ($this->findDirectoryWithNamespace($namespace) as $dir){ 111 | $this->setScanDirectory($dir,$namespace); 112 | } 113 | } 114 | } 115 | 116 | private function findDirectoryWithNamespace(string $namespace) 117 | { 118 | $loader = $this->loader; 119 | $prePsr4 = $loader->getPrefixesPsr4(); 120 | 121 | $arrNamespace = explode('\\',$namespace); 122 | $lastName = ''; 123 | foreach ($arrNamespace as $key=>$name){ 124 | unset($arrNamespace[$key]); 125 | if (!$name) break; 126 | $name = $lastName.$name."\\"; 127 | if( isset($prePsr4[$name]) ){ 128 | foreach ($prePsr4[$name] as $dir){ 129 | yield realpath($dir.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR,$arrNamespace)); 130 | } 131 | break; 132 | } 133 | $lastName = $name; 134 | } 135 | return ''; 136 | } 137 | } -------------------------------------------------------------------------------- /react/WebSocket/Event.php: -------------------------------------------------------------------------------- 1 | scanClass() as $class) { 42 | if (class_exists($class)) { 43 | $reflectionClass = new ReflectionClass($class); 44 | $classAnnotations = $reader->getClassAnnotations($reflectionClass); 45 | 46 | $queue = []; 47 | foreach ($classAnnotations AS $annotation) { 48 | if ($annotation instanceof Middleware) { 49 | $queue[] = $annotation->getClass();; 50 | } elseif ($annotation instanceof Middlewares) { 51 | /** @var Middleware $middleware */ 52 | foreach ($annotation->getMiddlewares() as $middleware) { 53 | $queue[] = $middleware->getClass(); 54 | } 55 | } 56 | } 57 | 58 | foreach ($reflectionClass->getMethods() as $reflectionMethod) { 59 | $methodAnnotations = $reader->getMethodAnnotations($reflectionMethod); 60 | 61 | foreach ($methodAnnotations AS $annotation) { 62 | $key = "{$class}/{$reflectionMethod->getName()}"; 63 | 64 | if (!isset($routes[$key])) { 65 | $routeHelper = new MessageRoute(); 66 | foreach ($queue as $mid) { 67 | $routeHelper->addMiddleware(self::getMiddleware($mid)); 68 | } 69 | $routes[$key] = $routeHelper; 70 | } else { 71 | /** @var MessageRoute $routeHelper */ 72 | $routeHelper = $routes[$key]; 73 | } 74 | 75 | if ($annotation instanceof MessageMapping) { 76 | $routeHelper->setRoute($annotation->getRoute()); 77 | $routeHelper->setClosure([new $class(), $reflectionMethod->getName()]); 78 | } elseif ($annotation instanceof Middleware) { 79 | $mid = $annotation->getClass(); 80 | $routeHelper->addMiddleware(self::getMiddleware($mid)); 81 | } elseif ($annotation instanceof Middlewares) { 82 | /** @var Middleware $middleware */ 83 | foreach ($annotation->getMiddlewares() as $middleware) { 84 | $mid = $middleware->getClass(); 85 | $routeHelper->addMiddleware(self::getMiddleware($mid)); 86 | } 87 | } 88 | 89 | $routes[$key] = $routeHelper; 90 | } 91 | } 92 | } 93 | } 94 | self::$middleware = null; 95 | return $routes; 96 | } 97 | 98 | private static function getMiddleware($mid) 99 | { 100 | if (!isset(self::$middleware[$mid])) { 101 | self::$middleware[$mid] = new $mid(); 102 | } 103 | return self::$middleware[$mid]; 104 | } 105 | 106 | 107 | /** 108 | * 获取目录操作 109 | * 110 | * @param array $namespaces 需要搜索的命名空间 111 | * @return DirectoryHelper 112 | */ 113 | protected static function getDirectoryHelper(array $namespaces) 114 | { 115 | $loader = App::getLoader(); 116 | $directory = new DirectoryHelper(); 117 | $directory->setLoader($loader); 118 | $directory->setScanNamespace($namespaces); 119 | return $directory; 120 | } 121 | } -------------------------------------------------------------------------------- /react/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | dispatcher = \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $route) { 46 | foreach ($this->scanRoute() as $arr) { 47 | /** @var RequestMapping $requestMapping */ 48 | $requestMapping = $arr[0]; 49 | /** @var RouteHelper $routeHelper */ 50 | $routeHelper = $arr[1]; 51 | $route->addRoute($requestMapping->getMethod(), $requestMapping->getRoute(), $routeHelper); 52 | } 53 | }); 54 | } 55 | 56 | /** 57 | * 所有服务加载后,注册触发, 58 | * 59 | * @return void 60 | */ 61 | public function register() 62 | { 63 | 64 | } 65 | 66 | /** 67 | * @return mixed 68 | */ 69 | public function getDispatcher() 70 | { 71 | return $this->dispatcher; 72 | } 73 | 74 | private function scanRoute() 75 | { 76 | $loader = App::getLoader(); 77 | $directory = new DirectoryHelper(); 78 | $directory->setLoader($loader); 79 | $directory->setScanNamespace($this->namespaces); 80 | $reader = new AnnotationReader(); 81 | foreach ($directory->scanClass() as $class) { 82 | if (class_exists($class)) { 83 | $reflectionClass = new ReflectionClass($class); 84 | $classAnnotations = $reader->getClassAnnotations($reflectionClass); 85 | 86 | $prefix = ""; 87 | $queue = []; 88 | foreach ($classAnnotations AS $annotation) { 89 | if ($annotation instanceof Middleware) { 90 | $queue[] = $annotation->getClass();; 91 | } elseif ($annotation instanceof Middlewares) { 92 | /** @var Middleware $middleware */ 93 | foreach ($annotation->getMiddlewares() as $middleware) { 94 | $queue[] = $middleware->getClass(); 95 | } 96 | } 97 | } 98 | 99 | foreach ($reflectionClass->getMethods() as $reflectionMethod) { 100 | $methodAnnotations = $reader->getMethodAnnotations($reflectionMethod); 101 | 102 | foreach ($methodAnnotations AS $annotation) { 103 | $key = "{$prefix}/{$class}/{$reflectionMethod->getName()}"; 104 | 105 | if (!isset($this->routes[$key])) { 106 | $routeHelper = new RouteHelper(); 107 | foreach ($queue as $mid) { 108 | $routeHelper->addMiddleware($this->getMiddleware($mid)); 109 | } 110 | $this->routes[$key] = $routeHelper; 111 | } else { 112 | /** @var RouteHelper $routeHelper */ 113 | $routeHelper = $this->routes[$key]; 114 | } 115 | 116 | if ($annotation instanceof RequestMapping) { 117 | if (!$annotation->getRoute()) { 118 | $tem_1 = explode("\\", $class); 119 | $controllerName = end($tem_1); 120 | $tem_2 = explode('Controller', $controllerName); 121 | $className = reset($tem_2); 122 | $className = strtolower($className); 123 | $annotation->setRoute("{$prefix}/{$className}/{$reflectionMethod->getName()}"); 124 | } 125 | $routeHelper->setClosure([new $class(), $reflectionMethod->getName()]); 126 | $this->requestMappingAnnotations[$key] = $annotation; 127 | } elseif ($annotation instanceof Middleware) { 128 | $mid = $annotation->getClass(); 129 | $routeHelper->addMiddleware($this->getMiddleware($mid)); 130 | } elseif ($annotation instanceof Middlewares) { 131 | /** @var Middleware $middleware */ 132 | foreach ($annotation->getMiddlewares() as $middleware) { 133 | $mid = $middleware->getClass(); 134 | $routeHelper->addMiddleware($this->getMiddleware($mid)); 135 | } 136 | } 137 | 138 | $this->routes[$key] = $routeHelper; 139 | } 140 | } 141 | } 142 | } 143 | 144 | unset($this->middleware); 145 | 146 | foreach ($this->routes as $key => $routeHelper) { 147 | if (!isset($this->requestMappingAnnotations[$key])) break; 148 | yield [$this->requestMappingAnnotations[$key], $routeHelper]; 149 | } 150 | } 151 | 152 | private function getMiddleware($mid) 153 | { 154 | if( !isset($this->middleware[$mid]) ){ 155 | $this->middleware[$mid] = new $mid(); 156 | } 157 | return $this->middleware[$mid]; 158 | } 159 | } --------------------------------------------------------------------------------