├── LICENSE ├── README.md ├── README_CN.md ├── bin └── easyswoole.php ├── composer.json ├── easyswoole.png └── src ├── AbstractInterface └── Event.php ├── Bridge ├── AbstractCommand.php ├── Bridge.php └── DefaultCommand │ ├── Crontab.php │ ├── Process.php │ └── Status.php ├── Command ├── CommandInterface.php ├── CommandRunner.php ├── DefaultCommand │ ├── Crontab.php │ ├── Install.php │ ├── Process.php │ ├── Server.php │ └── Task.php └── Utility.php ├── Config.php ├── Core.php ├── Crontab └── Crontab.php ├── Http └── Dispatcher.php ├── Logger.php ├── Resource ├── Config._php ├── EasySwooleEvent._php ├── Http │ ├── 404.html │ ├── Index._php │ ├── Router._php │ └── welcome.html ├── bootstrap._php └── easyswoole ├── ServerManager.php ├── Swoole ├── EventHelper.php └── EventRegister.php ├── SysConst.php ├── Task └── TaskManager.php ├── Trigger.php └── Utility └── DefaultTrigger.php /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English | [中文](./README_CN.md) 2 | 3 | ![](easyswoole.png) 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/easyswoole/easyswoole/v/stable)](https://packagist.org/packages/easyswoole/easyswoole) 6 | [![Total Downloads](https://poser.pugx.org/easyswoole/easyswoole/downloads)](https://packagist.org/packages/easyswoole/easyswoole) 7 | [![Latest Unstable Version](https://poser.pugx.org/easyswoole/easyswoole/v/unstable)](https://packagist.org/packages/easyswoole/easyswoole) 8 | [![License](https://poser.pugx.org/easyswoole/easyswoole/license)](https://packagist.org/packages/easyswoole/easyswoole) 9 | [![Monthly Downloads](https://poser.pugx.org/easyswoole/easyswoole/d/monthly)](https://packagist.org/packages/easyswoole/easyswoole) 10 | 11 | 12 | # EasySwoole - A High Performance Swoole Framework 13 | 14 | [EasySwoole](http://www.easyswoole.com/) is a distributed, persistent memory PHP framework based on the Swoole extension. It was created specifically for APIs to get rid of the performance penalties associated with process calls and file loading. EasySwoole highly encapsulates the Swoole Server and still maintains the original features of the Swoole server, supports simultaneous monitoring of HTTP, custom TCP, and UDP protocols, allowing developers to write multi-process, asynchronous, and highly available applications with minimal learning cost and effort. 15 | 16 | - Base on Swoole extension 17 | - Built-in HTTP, TCP, WebSocket,Udp Coroutine Server 18 | - Global dependency injection container 19 | - PSR-7 based HTTP message implementation 20 | - HTTP,TCP, WebSocket, Udp middleware support 21 | - Scalable high performance RPC 22 | - Database ORM 23 | - Mysql, Redis, RPC, HTTP Coroutine Clients 24 | - Coroutine and asynchronous task delivery 25 | - Custom user processes 26 | - RESTful supported 27 | - High performance router 28 | - Fast and flexible parameter validator 29 | - Powerful log component 30 | - Universal connection pools 31 | - Remote Console support 32 | - Crontab Rule Timer support 33 | 34 | ## Doc 35 | 36 | - [Doc](http://www.easyswoole.com) 37 | - [中文文档](http://www.easyswoole.com) 38 | - [Git For Doc](https://github.com/easy-swoole/doc-3.7) 39 | 40 | ## ab Test 41 | 42 | ```php 43 | response()->write('Hello World'); 59 | } 60 | } 61 | ``` 62 | 63 | ### 1 Core 1G RAM 64 | 65 | > command : ab -c 100 -n 10000 http://192.168.0.11:9501/ 66 | 67 | ``` 68 | Server Software: EasySwoole 69 | Server Hostname: 192.168.0.11 70 | Server Port: 9501 71 | 72 | Document Path: / 73 | Document Length: 21 bytes 74 | 75 | Concurrency Level: 100 76 | Time taken for tests: 0.652 seconds 77 | Complete requests: 10000 78 | Failed requests: 0 79 | Write errors: 0 80 | Total transferred: 1690000 bytes 81 | HTML transferred: 210000 bytes 82 | Requests per second: 15325.16 [#/sec] (mean) 83 | Time per request: 9.685 [ms] (mean) 84 | Time per request: 0.097 [ms] (mean, across all concurrent requests) 85 | Transfer rate: 2592.05 [Kbytes/sec] received 86 | ``` 87 | 88 | ### 8 Core 16G RAM 89 | 90 | > command : ab -c 100 -n 10000 http://192.168.0.4:9501/ 91 | 92 | ``` 93 | Server Software: EasySwoole 94 | Server Hostname: 192.168.0.4 95 | Server Port: 9501 96 | 97 | Document Path: / 98 | Document Length: 21 bytes 99 | 100 | Concurrency Level: 100 101 | Time taken for tests: 0.746 seconds 102 | Complete requests: 10000 103 | Failed requests: 0 104 | Write errors: 0 105 | Total transferred: 1690000 bytes 106 | HTML transferred: 210000 bytes 107 | Requests per second: 66935.97 [#/sec] (mean) 108 | Time per request: 1.149 [ms] (mean) 109 | Time per request: 0.015 [ms] (mean, across all concurrent requests) 110 | Transfer rate: 2265.40 [Kbytes/sec] received 111 | ``` 112 | 113 | ## Quick Start 114 | ``` 115 | composer require easyswoole/easyswoole=3.7.x 116 | php vendor/bin/easyswoole.php install 117 | php easyswoole.php server start 118 | ``` 119 | 120 | ## Docker 121 | ### Get Docker Image 122 | ``` 123 | docker pull easyswoolexuesi2021/easyswoole:php8.1.22-alpine3.16-swoole4.8.13 124 | ``` 125 | > More Docker images can be viewed:[Docker Hub](https://hub.docker.com/r/easyswoolexuesi2021/easyswoole) or [Git for Dockerfile](https://github.com/XueSiLf/easyswoole-docker) 126 | 127 | ### Run 128 | 129 | ``` 130 | docker run --name easyswoole \ 131 | -v /workspace/project:/var/www/project \ 132 | -p 9501:9501 -it \ 133 | --privileged -u root \ 134 | --entrypoint /bin/sh \ 135 | easyswoolexuesi2021/easyswoole:php8.1.22-alpine3.16-swoole4.8.13 136 | ``` 137 | - WorkerDir: ***/var/www*** 138 | - Run Easyswoole : 139 | ```bash 140 | composer require easyswoole/easyswoole=3.7.x 141 | php vendor/bin/easyswoole.php install 142 | php easyswoole.php server start 143 | ``` 144 | 145 | ## Others 146 | - [Git For Demo](https://github.com/easy-swoole/demo) 147 | - QQ交流群 148 | - VIP群 579434607 (本群需要付费599元) 149 | - EasySwoole官方一群 633921431(已满) 150 | - EasySwoole官方二群 709134628(已满) 151 | - EasySwoole官方三群 932625047(已满) 152 | - EasySwoole官方四群 779897753(已满) 153 | - EasySwoole官方五群 853946743(已满) 154 | - EasySwoole官方六群 524475224 155 | 156 | - 商业支持: 157 | - QQ 291323003 158 | - EMAIL admin@fosuss.com 159 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | 中文 | [English](./README.md) 2 | 3 | ![](easyswoole.png) 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/easyswoole/easyswoole/v/stable)](https://packagist.org/packages/easyswoole/easyswoole) 6 | [![Total Downloads](https://poser.pugx.org/easyswoole/easyswoole/downloads)](https://packagist.org/packages/easyswoole/easyswoole) 7 | [![Latest Unstable Version](https://poser.pugx.org/easyswoole/easyswoole/v/unstable)](https://packagist.org/packages/easyswoole/easyswoole) 8 | [![License](https://poser.pugx.org/easyswoole/easyswoole/license)](https://packagist.org/packages/easyswoole/easyswoole) 9 | [![Monthly Downloads](https://poser.pugx.org/easyswoole/easyswoole/d/monthly)](https://packagist.org/packages/easyswoole/easyswoole) 10 | 11 | 12 | # EasySwoole - 一款高性能 Swoole 框架 13 | 14 | [EasySwoole](http://www.easyswoole.com/) 是一款基于 Swoole Server 开发的常驻内存型的分布式 PHP 框架,专为 API 而生,摆脱传统 PHP 运行模式在进程唤起和文件加载上带来的性能损失。EasySwoole 高度封装了 Swoole Server 而依旧维持 Swoole Server 原有特性,支持同时混合监听 HTTP、自定义 TCP、UDP 协议,让开发者以最低的学习成本和精力编写出多进程、可异步、高可用的应用服务。 15 | 16 | - 基于 Swoole 扩展 17 | - 内置HTTP、TCP、WebSocket、UD P协程服务器 18 | - 全局依赖注入容器 19 | - 基于 PSR-7 的 HTTP 消息实现 20 | - HTTP、TCP、WebSocket、UDP 中间件支持 21 | - 可扩展的高性能 RPC 22 | - 数据库 ORM 23 | - MYSQL、Redis、RPC、HTTP 协程客户端 24 | - 协程和异步任务 25 | - 用户自定义进程 26 | - 支持 RESTful 27 | - 高性能路由 28 | - 快速灵活的参数验证器 29 | - 强大的日志组件 30 | - 通用连接池 31 | - 远程控制台支持 32 | - Crontab及定时器支持 33 | 34 | ## 文档 35 | 36 | - [英文文档](http://www.easyswoole.com) 37 | - [中文文档](http://www.easyswoole.com) 38 | - [文档 Git 仓库](https://github.com/easy-swoole/doc-3.7) 39 | 40 | ## ab 压力测试 41 | 42 | ```php 43 | response()->write('Hello World'); 59 | } 60 | } 61 | ``` 62 | 63 | ### 1 Core 1G RAM 64 | 65 | > command : ab -c 100 -n 10000 http://192.168.0.11:9501/ 66 | 67 | ``` 68 | Server Software: EasySwoole 69 | Server Hostname: 192.168.0.11 70 | Server Port: 9501 71 | 72 | Document Path: / 73 | Document Length: 21 bytes 74 | 75 | Concurrency Level: 100 76 | Time taken for tests: 0.652 seconds 77 | Complete requests: 10000 78 | Failed requests: 0 79 | Write errors: 0 80 | Total transferred: 1690000 bytes 81 | HTML transferred: 210000 bytes 82 | Requests per second: 15325.16 [#/sec] (mean) 83 | Time per request: 9.685 [ms] (mean) 84 | Time per request: 0.097 [ms] (mean, across all concurrent requests) 85 | Transfer rate: 2592.05 [Kbytes/sec] received 86 | ``` 87 | 88 | ### 8 Core 16G RAM 89 | 90 | > command : ab -c 100 -n 10000 http://192.168.0.4:9501/ 91 | 92 | ``` 93 | Server Software: EasySwoole 94 | Server Hostname: 192.168.0.4 95 | Server Port: 9501 96 | 97 | Document Path: / 98 | Document Length: 21 bytes 99 | 100 | Concurrency Level: 100 101 | Time taken for tests: 0.746 seconds 102 | Complete requests: 10000 103 | Failed requests: 0 104 | Write errors: 0 105 | Total transferred: 1690000 bytes 106 | HTML transferred: 210000 bytes 107 | Requests per second: 66935.97 [#/sec] (mean) 108 | Time per request: 1.149 [ms] (mean) 109 | Time per request: 0.015 [ms] (mean, across all concurrent requests) 110 | Transfer rate: 2265.40 [Kbytes/sec] received 111 | ``` 112 | 113 | ## 快速开始使用 114 | ``` 115 | composer require easyswoole/easyswoole=3.7.x 116 | php vendor/bin/easyswoole.php install 117 | php easyswoole.php server start 118 | ``` 119 | 120 | ## Docker 121 | ### 获取 Docker 镜像 122 | ``` 123 | docker pull easyswoolexuesi2021/easyswoole:php8.1.22-alpine3.16-swoole4.8.13 124 | ``` 125 | > 更多 Docker 镜像可查看:[Docker Hub](https://hub.docker.com/r/easyswoolexuesi2021/easyswoole) 或 [Dockerfile 仓库](https://github.com/XueSiLf/easyswoole-docker) 126 | 127 | ### 运行容器 128 | 129 | ``` 130 | docker run --name easyswoole \ 131 | -v /workspace/project:/var/www/project \ 132 | -p 9501:9501 -it \ 133 | --privileged -u root \ 134 | --entrypoint /bin/sh \ 135 | easyswoolexuesi2021/easyswoole:php8.1.22-alpine3.16-swoole4.8.13 136 | ``` 137 | - 工作目录: ***/var/www/project*** 138 | - 运行 Easyswoole : 139 | ```bash 140 | composer require easyswoole/easyswoole=3.7.x 141 | php vendor/bin/easyswoole.php install 142 | php easyswoole.php server start 143 | ``` 144 | 145 | ## 其他 146 | - [Git For Demo](https://github.com/easy-swoole/demo) 147 | - QQ交流群 148 | - VIP群 579434607 (本群需要付费599元) 149 | - EasySwoole官方一群 633921431(已满) 150 | - EasySwoole官方二群 709134628(已满) 151 | - EasySwoole官方三群 932625047(已满) 152 | - EasySwoole官方四群 779897753(已满) 153 | - EasySwoole官方五群 853946743(已满) 154 | - EasySwoole官方六群 524475224 155 | 156 | - 商业支持: 157 | - QQ 291323003 158 | - EMAIL admin@fosuss.com 159 | -------------------------------------------------------------------------------- /bin/easyswoole.php: -------------------------------------------------------------------------------- 1 | setScript(current($argv)); 28 | $caller->setCommand(next($argv)); 29 | $caller->setParams($argv); 30 | reset($argv); 31 | 32 | $ret = CommandRunner::getInstance()->run($caller); 33 | if($ret && !empty($ret->getMsg())){ 34 | echo $ret->getMsg()."\n"; 35 | } 36 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easyswoole/easyswoole", 3 | "type": "library", 4 | "description": "An efficient swoole framework", 5 | "keywords": [ 6 | "swoole", 7 | "framework", 8 | "async", 9 | "easyswoole" 10 | ], 11 | "homepage": "https://www.easyswoole.com/", 12 | "license": "Apache-2.0", 13 | "authors": [ 14 | { 15 | "name": "YF", 16 | "email": "291323003@qq.com" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.0", 21 | "ext-swoole": ">=4.4.4", 22 | "ext-json": "*", 23 | "easyswoole/component": "^2.0", 24 | "easyswoole/spl": "^2.0", 25 | "easyswoole/http-annotation": "3.x", 26 | "easyswoole/utility": "^1.0", 27 | "easyswoole/trigger": "^1.0", 28 | "easyswoole/config": "^1.0", 29 | "easyswoole/socket": "^1.2", 30 | "easyswoole/task": "^2.0", 31 | "easyswoole/command": "^1.1", 32 | "easyswoole/bridge": "^1.0", 33 | "easyswoole/phpunit": "^1.1", 34 | "easyswoole/crontab": "^1.0" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "EasySwoole\\EasySwoole\\": "src/", 39 | "App\\": "App/" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "EasySwoole\\EasySwoole\\Test\\": "tests/", 45 | "App\\": "App/" 46 | } 47 | }, 48 | "require-dev": { 49 | "easyswoole/swoole-ide-helper": "^1.2" 50 | }, 51 | "bin": [ 52 | "bin/easyswoole.php" 53 | ] 54 | } -------------------------------------------------------------------------------- /easyswoole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easy-swoole/easyswoole/ac99b1b7c84f3b5f8497e52804e4e46b59cce181/easyswoole.png -------------------------------------------------------------------------------- /src/AbstractInterface/Event.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | 7 | namespace EasySwoole\EasySwoole\Bridge; 8 | 9 | 10 | use EasySwoole\Bridge\CommandInterface; 11 | use EasySwoole\Bridge\Package; 12 | use Swoole\Coroutine\Socket; 13 | 14 | abstract class AbstractCommand implements CommandInterface 15 | { 16 | public function exec(Package $package, Package $responsePackage, Socket $socket) 17 | { 18 | $action = $package->getArgs()['action'] ?? ''; 19 | if (!method_exists($this, $action)) { 20 | $responsePackage->setStatus($responsePackage::STATUS_COMMAND_NOT_EXIST); 21 | $responsePackage->setMsg("command action:{$action} not empty"); 22 | return $responsePackage; 23 | } 24 | $this->{$action}($package, $responsePackage); 25 | } 26 | } -------------------------------------------------------------------------------- /src/Bridge/Bridge.php: -------------------------------------------------------------------------------- 1 | getCommandContainer()->set(new Crontab()); 24 | $this->getCommandContainer()->set(new Process()); 25 | $this->getCommandContainer()->set(new Status()); 26 | $mode = Core::getInstance()->runMode(); 27 | $serverName = Config::getInstance()->getConf('SERVER_NAME').".{$mode}"; 28 | $this->setSocketFile(EASYSWOOLE_TEMP_DIR . "/{$serverName}.bridge.sock"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/DefaultCommand/Crontab.php: -------------------------------------------------------------------------------- 1 | schedulerTable(); 22 | $data = []; 23 | foreach ($info as $k => $v) { 24 | $data[$k] = $v; 25 | } 26 | if (empty($data)) { 27 | $response->setMsg("crontab info is abnormal or empty Crontab register ."); 28 | $response->setStatus($response::STATUS_COMMAND_ERROR); 29 | return false; 30 | } 31 | $response->setArgs($data); 32 | } 33 | 34 | protected function stop(Package $package, Package $response) 35 | { 36 | $taskName = $package->getArgs()['taskName']; 37 | if (!$taskName) { 38 | $response->setMsg("The name of the operation plan task must be filled in"); 39 | $response->setStatus($response::STATUS_COMMAND_ERROR); 40 | return false; 41 | } 42 | $info = EasySwooleCron::getInstance()->schedulerTable(); 43 | $crontab = $info->get($taskName); 44 | if (empty($crontab)) { 45 | $response->setMsg("crontab: {$taskName} is not found."); 46 | $response->setStatus($response::STATUS_COMMAND_ERROR); 47 | return false; 48 | } 49 | if ($crontab['isStop'] == 1) { 50 | $response->setMsg("crontab: {$taskName} is already stop."); 51 | $response->setStatus($response::STATUS_COMMAND_ERROR); 52 | return false; 53 | } 54 | 55 | $info->set($taskName, ['isStop' => 1]); 56 | $response->setMsg("crontab: {$taskName} is stop suceess."); 57 | return true; 58 | } 59 | 60 | protected function resume(Package $package, Package $response) 61 | { 62 | $taskName = $package->getArgs()['taskName']; 63 | if (!$taskName) { 64 | $response->setMsg("The name of the operation plan task must be filled in"); 65 | $response->setStatus($response::STATUS_COMMAND_ERROR); 66 | return false; 67 | } 68 | $info = EasySwooleCron::getInstance()->schedulerTable(); 69 | $crontab = $info->get($taskName); 70 | if (empty($crontab)) { 71 | $response->setMsg("crontab: {$taskName} is not found."); 72 | $response->setStatus($response::STATUS_COMMAND_ERROR); 73 | return false; 74 | } 75 | if ($crontab['isStop'] == 0) { 76 | $response->setMsg("crontab: {$taskName} is running."); 77 | $response->setStatus($response::STATUS_COMMAND_ERROR); 78 | return false; 79 | } 80 | $info->set($taskName, ['isStop' => 0]); 81 | $response->setMsg("crontab: {$taskName} resume suceess."); 82 | return true; 83 | } 84 | 85 | protected function run(Package $package, Package $response) 86 | { 87 | $taskName = $package->getArgs()['taskName']; 88 | if (!$taskName) { 89 | $response->setMsg("The name of the operation plan task must be filled in"); 90 | $response->setStatus($response::STATUS_COMMAND_ERROR); 91 | return false; 92 | } 93 | $result = EasySwooleCron::getInstance()->rightNow($taskName); 94 | if (!$result || !$result instanceof Response) { 95 | $response->setMsg("crontab: server connect fail"); 96 | $response->setStatus($response::STATUS_COMMAND_ERROR); 97 | return false; 98 | } 99 | 100 | if ($result->getStatus() != Response::STATUS_OK) { 101 | $response->setMsg($result->getMsg() ?? Response::getReasonPhrase($result->getStatus())); 102 | $response->setStatus($response::STATUS_COMMAND_ERROR); 103 | return false; 104 | } 105 | 106 | $response->setMsg("crontab: {$taskName} run success"); 107 | return true; 108 | } 109 | 110 | protected function reset(Package $package, Package $response) 111 | { 112 | $taskName = $package->getArgs()['taskName']; 113 | $taskRule = $package->getArgs()['taskRule']; 114 | if (!$taskName) { 115 | $response->setMsg("The name of the operation plan task must be filled in"); 116 | $response->setStatus($response::STATUS_COMMAND_ERROR); 117 | return false; 118 | } 119 | 120 | if (!$taskRule) { 121 | $response->setMsg("The rule of the operation plan task must be filled in"); 122 | $response->setStatus($response::STATUS_COMMAND_ERROR); 123 | return false; 124 | } 125 | 126 | EasySwooleCron::getInstance()->resetJobRule($taskName, $taskRule); 127 | 128 | $response->setMsg("crontab: {$taskName} reset success"); 129 | return true; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Bridge/DefaultCommand/Process.php: -------------------------------------------------------------------------------- 1 | info(); 21 | foreach ($array as &$value){ 22 | unset($value['hash']); 23 | } 24 | $responsePackage->setArgs($array); 25 | return true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Bridge/DefaultCommand/Status.php: -------------------------------------------------------------------------------- 1 | getSwooleServer()->stats(); 25 | $data = Utility::createServerDisplayItem(Config::getInstance()) + $data; 26 | $responsePackage->setArgs($data); 27 | return true; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Command/CommandInterface.php: -------------------------------------------------------------------------------- 1 | addCommand(new Install()); 32 | CommandManager::getInstance()->addCommand(new Task()); 33 | CommandManager::getInstance()->addCommand(new Crontab()); 34 | CommandManager::getInstance()->addCommand(new Process()); 35 | CommandManager::getInstance()->addCommand(new Server()); 36 | CommandManager::getInstance()->addCommand(new PhpunitCommand()); 37 | 38 | if (class_exists(PhpunitCommand::class)) { 39 | CommandManager::getInstance()->addCommand(new PhpunitCommand()); 40 | } 41 | } 42 | 43 | private $beforeCommand; 44 | 45 | public function setBeforeCommand(callable $before) 46 | { 47 | $this->beforeCommand = $before; 48 | } 49 | 50 | public function run(CallerInterface $caller): ResultInterface 51 | { 52 | if (is_callable($this->beforeCommand)) { 53 | call_user_func($this->beforeCommand, $caller); 54 | } 55 | Utility::opCacheClear(); 56 | 57 | $msg = CommandManager::getInstance()->run($caller); 58 | 59 | $result = new Result(); 60 | $result->setMsg($msg); 61 | return $result; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Command/DefaultCommand/Crontab.php: -------------------------------------------------------------------------------- 1 | addAction('show', 'show all crontab'); 30 | $commandHelp->addAction('stop', 'stops the specified crontab'); 31 | $commandHelp->addAction('resume', 'restores the specified crontab'); 32 | $commandHelp->addAction('run', 'run the specified crontab once immediately'); 33 | $commandHelp->addAction('reset', 'rewrite scheduled task rules'); 34 | $commandHelp->addActionOpt('--name=TASK_NAME', 'the taskname to be operated on'); 35 | return $commandHelp; 36 | } 37 | 38 | public function exec(): ?string 39 | { 40 | $action = CommandManager::getInstance()->getArg(0); 41 | Core::getInstance()->initialize(); 42 | $run = new Scheduler(); 43 | $run->add(function () use (&$result, $action) { 44 | if (!is_null($action) && method_exists($this, $action) && $action != 'help') { 45 | $result = $this->{$action}(); 46 | return; 47 | } 48 | $result = CommandManager::getInstance()->displayCommandHelp($this->commandName()); 49 | }); 50 | $run->start(); 51 | return $result; 52 | } 53 | 54 | protected function stop() 55 | { 56 | $taskName = CommandManager::getInstance()->getOpt('name'); 57 | return Utility::bridgeCall($this->commandName(), function (Package $package) { 58 | $data = $package->getMsg(); 59 | return Color::success($data) . PHP_EOL . $this->show(); 60 | }, 'stop', ['taskName' => $taskName]); 61 | } 62 | 63 | 64 | protected function resume() 65 | { 66 | $taskName = CommandManager::getInstance()->getOpt('name'); 67 | return Utility::bridgeCall($this->commandName(), function (Package $package) { 68 | $data = $package->getMsg(); 69 | return Color::success($data) . PHP_EOL . $this->show(); 70 | }, 'resume', ['taskName' => $taskName]); 71 | } 72 | 73 | protected function run() 74 | { 75 | $taskName = CommandManager::getInstance()->getOpt('name'); 76 | return Utility::bridgeCall($this->commandName(), function (Package $package) { 77 | $data = $package->getMsg(); 78 | return Color::success($data) . PHP_EOL . $this->show(); 79 | }, 'run', ['taskName' => $taskName]); 80 | } 81 | 82 | protected function show() 83 | { 84 | return Utility::bridgeCall($this->commandName(), function (Package $package) { 85 | $data = $package->getArgs(); 86 | foreach ($data as $k => $v) { 87 | $v['taskNextRunTime'] = date('Y-m-d H:i:s', $v['taskNextRunTime']); 88 | $v['taskCurrentRunTime'] = date('Y-m-d H:i:s', $v['taskCurrentRunTime']); 89 | $data[$k] = array_merge(['taskName' => $k], $v); 90 | } 91 | return new ArrayToTextTable($data); 92 | }, 'show'); 93 | } 94 | 95 | protected function reset() 96 | { 97 | $taskName = CommandManager::getInstance()->getOpt('name'); 98 | $taskRule = CommandManager::getInstance()->getOpt('rule'); 99 | return Utility::bridgeCall($this->commandName(), function (Package $package) { 100 | $data = $package->getMsg(); 101 | return Color::success($data) . PHP_EOL . $this->show(); 102 | }, 'reset', ['taskName' => $taskName, 'taskRule' => $taskRule]); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/Command/DefaultCommand/Install.php: -------------------------------------------------------------------------------- 1 | updateComposerJson(); 43 | $this->execComposerDumpAutoload(); 44 | $this->checkFunctionsOpen(); 45 | return Color::success("install success,enjoy!!!\ndont forget run composer dump-autoload !!!"); 46 | } 47 | 48 | public function help(CommandHelpInterface $commandHelp): CommandHelpInterface 49 | { 50 | return $commandHelp; 51 | } 52 | 53 | protected function checkFunctionsOpen() 54 | { 55 | if (!function_exists('symlink') || !function_exists('readlink')) { 56 | echo Color::warning('Please at php.ini Open the symlink and readlink functions') . PHP_EOL; 57 | } 58 | } 59 | 60 | protected function updateComposerJson() 61 | { 62 | $arr = json_decode(file_get_contents(EASYSWOOLE_ROOT . '/composer.json'), true); 63 | $arr['autoload']['psr-4']['App\\'] = "App/"; 64 | File::createFile(EASYSWOOLE_ROOT . '/composer.json', json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); 65 | } 66 | 67 | protected function execComposerDumpAutoload() 68 | { 69 | if(function_exists('exec')){ 70 | @exec('composer dump-autoload'); 71 | }else{ 72 | echo Color::warning('exec() is forbid,you may run composer dump-autoload by manual') . PHP_EOL; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /src/Command/DefaultCommand/Process.php: -------------------------------------------------------------------------------- 1 | addAction('kill', 'kill process'); 32 | $commandHelp->addAction('killAll', 'kill all processes'); 33 | $commandHelp->addAction('show', 'show all process information'); 34 | $commandHelp->addActionOpt('--pid=PID', 'kill the specified pid'); 35 | $commandHelp->addActionOpt('--group=GROUP_NAME', 'kill the specified process group'); 36 | $commandHelp->addActionOpt('-f', 'force kill process'); 37 | return $commandHelp; 38 | } 39 | 40 | public function exec(): ?string 41 | { 42 | $action = CommandManager::getInstance()->getArg(0); 43 | Core::getInstance()->initialize(); 44 | $run = new Scheduler(); 45 | $run->add(function () use (&$result, $action) { 46 | if (method_exists($this, $action) && $action != 'help') { 47 | 48 | $package = Bridge::getInstance()->call($this->commandName(), ['action' => 'info']); 49 | if ($package->getStatus() != Package::STATUS_SUCCESS) { 50 | $result = Color::error($package->getMsg()); 51 | return; 52 | } 53 | 54 | $data = $this->processInfoHandel($package->getArgs()); 55 | $result = $this->$action($data); 56 | return; 57 | } 58 | 59 | $result = CommandManager::getInstance()->displayCommandHelp($this->commandName()); 60 | }); 61 | $run->start(); 62 | return $result; 63 | } 64 | 65 | protected function killProcess(array $list) 66 | { 67 | if (empty($list)) { 68 | return Color::error('not process was kill'); 69 | } 70 | 71 | if (CommandManager::getInstance()->issetOpt('f')) { 72 | $sig = SIGKILL; 73 | $option = 'SIGKILL'; 74 | } else { 75 | $sig = SIGTERM; 76 | $option = 'SIGTERM'; 77 | } 78 | 79 | foreach ($list as $pid => $value) { 80 | \Swoole\Process::kill($pid, $sig); 81 | $list[$pid]['option'] = $option; 82 | } 83 | return new ArrayToTextTable($list); 84 | } 85 | 86 | protected function kill($json) 87 | { 88 | $list = []; 89 | $pid = CommandManager::getInstance()->getOpt('pid'); 90 | $groupName = CommandManager::getInstance()->getOpt('group'); 91 | foreach ($json as $key => $value) { 92 | if ($value['pid'] == $pid) { 93 | $list[$key] = $value; 94 | } 95 | 96 | if ($value['group'] == $groupName) { 97 | $list[$key] = $value; 98 | } 99 | } 100 | return $this->killProcess($list); 101 | } 102 | 103 | protected function killAll($json) 104 | { 105 | return $this->killProcess($json); 106 | } 107 | 108 | protected function show($json) 109 | { 110 | return new ArrayToTextTable($json); 111 | } 112 | 113 | protected function processInfoHandel($json) 114 | { 115 | $unit = ['b', 'kb', 'mb', 'gb', 'tb', 'pb']; 116 | 117 | foreach ($json as $key => $value) { 118 | $json[$key]['memoryUsage'] = round($value['memoryUsage'] / pow(1024, ($i = floor(log($value['memoryUsage'], 1024)))), 2) . ' ' . $unit[$i]; 119 | $json[$key]['memoryPeakUsage'] = round($value['memoryPeakUsage'] / pow(1024, ($i = floor(log($value['memoryPeakUsage'], 1024)))), 2) . ' ' . $unit[$i]; 120 | $json[$key]['startUpTime'] = date('Y-m-d H:i:s',$json[$key]['startUpTime']); 121 | } 122 | 123 | return $json; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Command/DefaultCommand/Server.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | 7 | namespace EasySwoole\EasySwoole\Command\DefaultCommand; 8 | 9 | 10 | use EasySwoole\Bridge\Package; 11 | use EasySwoole\Command\AbstractInterface\CommandHelpInterface; 12 | use EasySwoole\Command\AbstractInterface\CommandInterface; 13 | use EasySwoole\Command\Color; 14 | use EasySwoole\Command\CommandManager; 15 | use EasySwoole\EasySwoole\Command\Utility; 16 | use EasySwoole\EasySwoole\Config; 17 | use EasySwoole\EasySwoole\Core; 18 | use EasySwoole\EasySwoole\SysConst; 19 | use EasySwoole\Utility\ArrayToTextTable; 20 | use Swoole\Coroutine\Scheduler; 21 | 22 | class Server implements CommandInterface 23 | { 24 | public function commandName(): string 25 | { 26 | return 'server'; 27 | } 28 | 29 | public function desc(): string 30 | { 31 | return 'Easyswoole server'; 32 | } 33 | 34 | public function help(CommandHelpInterface $commandHelp): CommandHelpInterface 35 | { 36 | $commandHelp->addAction('start', 'start-up'); 37 | $commandHelp->addAction('stop', 'stop it'); 38 | $commandHelp->addAction('reload', 'reload worker process'); 39 | $commandHelp->addAction('restart', 'restart EasySwoole server'); 40 | $commandHelp->addAction('status', 'view EasySwoole status'); 41 | $commandHelp->addActionOpt('-d', 'start as a daemon'); 42 | $commandHelp->addActionOpt('-force', 'stop by force'); 43 | $commandHelp->addActionOpt('-mode=dev', 'run mode,dev is default mode'); 44 | return $commandHelp; 45 | } 46 | 47 | public function exec(): ?string 48 | { 49 | $action = CommandManager::getInstance()->getArg(0); 50 | if (!is_null($action) && method_exists($this, $action) && $action != 'help') { 51 | Core::getInstance()->initialize(); 52 | return $this->$action(); 53 | } 54 | 55 | return CommandManager::getInstance()->displayCommandHelp($this->commandName()); 56 | } 57 | 58 | protected function start() 59 | { 60 | defined('EASYSWOOLE_RUNNING') or define('EASYSWOOLE_RUNNING', true); 61 | $conf = Config::getInstance(); 62 | // php easyswoole.php server start -d 63 | $daemonize = CommandManager::getInstance()->issetOpt('d'); 64 | if ($daemonize) { 65 | $conf->setConf("MAIN_SERVER.SETTING.daemonize", $daemonize); 66 | } 67 | 68 | if (empty($conf->getConf('MAIN_SERVER.SETTING.user'))) { 69 | $conf->setConf('MAIN_SERVER.SETTING.user',get_current_user()); 70 | } 71 | 72 | $displayItem = Utility::createServerDisplayItem(Config::getInstance()); 73 | $msg = Color::green(Utility::easySwooleLog()) . "\n"; 74 | foreach ($displayItem as $key => $value) { 75 | $msg .= Utility::displayItem($key, $value) . "\n"; 76 | } 77 | echo $msg; 78 | Core::getInstance()->createServer()->start(); 79 | } 80 | 81 | protected function stop() 82 | { 83 | $pidFile = Config::getInstance()->getConf("MAIN_SERVER.SETTING.pid_file"); 84 | $msg = ''; 85 | if (file_exists($pidFile)) { 86 | $pid = intval(file_get_contents($pidFile)); 87 | if (!\Swoole\Process::kill($pid, 0)) { 88 | $msg = Color::danger("pid :{$pid} not exist "); 89 | unlink($pidFile); 90 | } else { 91 | $force = CommandManager::getInstance()->issetOpt('force'); 92 | if ($force) { 93 | \Swoole\Process::kill($pid, SIGKILL); 94 | } else { 95 | \Swoole\Process::kill($pid); 96 | } 97 | //等待5秒 98 | $time = time(); 99 | while (true) { 100 | usleep(1000); 101 | if (!\Swoole\Process::kill($pid, 0)) { 102 | if (is_file($pidFile)) { 103 | unlink($pidFile); 104 | } 105 | $msg = "server stop for pid {$pid} at " . date("Y-m-d H:i:s"); 106 | $msg = Color::success($msg); 107 | break; 108 | } else { 109 | if (time() - $time > 15) { 110 | $msg = Color::danger("stop server fail for pid:{$pid} , try [php easyswoole.php server stop -force] again"); 111 | break; 112 | } 113 | } 114 | } 115 | } 116 | } else { 117 | $msg = Color::danger("pid file does not exist, please check whether to run in the daemon mode!"); 118 | } 119 | return $msg; 120 | } 121 | 122 | protected function reload() 123 | { 124 | $pidFile = Config::getInstance()->getConf("MAIN_SERVER.SETTING.pid_file"); 125 | if (file_exists($pidFile)) { 126 | Utility::opCacheClear(); 127 | $pid = file_get_contents($pidFile); 128 | if (!\Swoole\Process::kill($pid, 0)) { 129 | $msg = Color::danger("pid :{$pid} not exist "); 130 | } else { 131 | \Swoole\Process::kill($pid, SIGUSR1); 132 | $msg = "send server reload command to pid:{$pid} at " . date("Y-m-d H:i:s"); 133 | $msg = Color::success($msg); 134 | } 135 | } else { 136 | $msg = Color::danger("pid file does not exist, please check whether to run in the daemon mode!"); 137 | } 138 | return $msg; 139 | } 140 | 141 | protected function restart() 142 | { 143 | $msg = $this->stop(); 144 | $this->start(); 145 | return $msg; 146 | } 147 | 148 | protected function status() 149 | { 150 | $run = new Scheduler(); 151 | $run->add(function () use (&$result) { 152 | $result = Utility::bridgeCall('status', function (Package $package) { 153 | $displayItem = $package->getArgs(); 154 | $msg = Color::green(Utility::easySwooleLog()) . "\n"; 155 | foreach ($displayItem as $key => $value) { 156 | $msg .= Utility::displayItem($key, $value) . "\n"; 157 | } 158 | return $msg; 159 | }, 'call'); 160 | }); 161 | $run->start(); 162 | return $result; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/Command/DefaultCommand/Task.php: -------------------------------------------------------------------------------- 1 | addAction('status', 'status of the task'); 33 | $commandHelp->addAction('reboot', 'reboot all of task worker'); 34 | return $commandHelp; 35 | } 36 | 37 | public function exec(): ?string 38 | { 39 | $action = CommandManager::getInstance()->getArg(0); 40 | Core::getInstance()->initialize(); 41 | $run = new Scheduler(); 42 | $run->add(function () use (&$result, $action) { 43 | if (!is_null($action) && method_exists($this, $action) && $action != 'help') { 44 | $result = $this->{$action}(); 45 | return; 46 | } 47 | 48 | $result = CommandManager::getInstance()->displayCommandHelp($this->commandName()); 49 | }); 50 | $run->start(); 51 | return $result; 52 | } 53 | 54 | private function reboot() 55 | { 56 | $list = Utility::bridgeCall('status', function (Package $package) { 57 | $data = $package->getArgs(); 58 | if(empty($data)){ 59 | return 'please check config item for task worker num'; 60 | } 61 | return $data; 62 | }, 'task'); 63 | 64 | if(is_array($list)){ 65 | if (CommandManager::getInstance()->issetOpt('f')) { 66 | $sig = SIGKILL; 67 | $option = 'SIGKILL'; 68 | } else { 69 | $sig = SIGTERM; 70 | $option = 'SIGTERM'; 71 | } 72 | 73 | $ret = []; 74 | 75 | foreach ($list as $item){ 76 | \Swoole\Process::kill($item['pid'], $sig); 77 | $ret[$item['pid']] = [ 78 | "pid"=>$item['pid'], 79 | "startUpTime"=>date('Y-m-d H:i:s',$item['startUpTime']), 80 | "signalTime"=>date('Y-m-d H:i:s'), 81 | "signalType"=>$option, 82 | "3s-Status"=>"alive" 83 | ]; 84 | } 85 | 86 | //检测检测 87 | 88 | $wait = new WaitGroup(); 89 | foreach ($ret as $pid =>$item){ 90 | $wait->add(); 91 | Coroutine::create(function ()use($pid,$wait,&$ret){ 92 | $start = time(); 93 | while (1){ 94 | if(time() - $start > 3){ 95 | $wait->done(); 96 | break; 97 | }else{ 98 | if(!\Swoole\Process::kill($pid, 0)){ 99 | $ret[$pid]['3s-Status'] = "exit"; 100 | $wait->done(); 101 | break; 102 | } 103 | } 104 | Coroutine::sleep(0.01); 105 | } 106 | }); 107 | } 108 | 109 | $wait->wait(3); 110 | 111 | return new ArrayToTextTable($ret); 112 | }else{ 113 | return $list; 114 | } 115 | } 116 | 117 | protected function status() 118 | { 119 | return Utility::bridgeCall('status', function (Package $package) { 120 | $data = $package->getArgs(); 121 | if(empty($data)){ 122 | return 'please check config item for task worker num'; 123 | } 124 | foreach ($data as $key => &$datum){ 125 | $datum['workerIndex'] = $key; 126 | $datum['startUpTime'] = date('Y-m-d H:i:s',$datum['startUpTime']); 127 | } 128 | return new ArrayToTextTable($data); 129 | }, 'task'); 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /src/Command/Utility.php: -------------------------------------------------------------------------------- 1 | getConf('MAIN_SERVER.SERVER_TYPE'); 54 | $displayItem = []; 55 | switch ($serverType) { 56 | case EASYSWOOLE_SERVER: 57 | { 58 | $serverType = 'SWOOLE_SERVER'; 59 | break; 60 | } 61 | case EASYSWOOLE_WEB_SERVER: 62 | { 63 | $serverType = 'SWOOLE_WEB'; 64 | break; 65 | } 66 | case EASYSWOOLE_WEB_SOCKET_SERVER: 67 | { 68 | $serverType = 'SWOOLE_WEB_SOCKET'; 69 | break; 70 | } 71 | default: 72 | { 73 | $serverType = 'UNKNOWN'; 74 | } 75 | } 76 | $displayItem['main server'] = $serverType; 77 | $displayItem['listen address'] = $conf->getConf('MAIN_SERVER.LISTEN_ADDRESS'); 78 | $displayItem['listen port'] = $conf->getConf('MAIN_SERVER.PORT'); 79 | $data = $conf->getConf('MAIN_SERVER.SETTING'); 80 | $displayItem = $displayItem + $data; 81 | $displayItem['swoole version'] = phpversion('swoole'); 82 | $displayItem['php version'] = phpversion(); 83 | $displayItem['easyswoole version'] = SysConst::EASYSWOOLE_VERSION; 84 | $displayItem['run mode'] = Core::getInstance()->runMode(); 85 | $displayItem['temp dir'] = EASYSWOOLE_TEMP_DIR; 86 | $displayItem['log dir'] = EASYSWOOLE_LOG_DIR; 87 | return $displayItem; 88 | } 89 | 90 | public static function releaseResource($source, $destination,$confirm = false) 91 | { 92 | $filename = basename($destination); 93 | clearstatcache(); 94 | $replace = true; 95 | if (is_file($destination)) { 96 | echo Color::danger("{$filename} has already existed, do you want to replace it? [ Y / N (default) ] : "); 97 | $answer = strtolower(trim(strtoupper(fgets(STDIN)))); 98 | if (!in_array($answer, ['y', 'yes'])) { 99 | $replace = false; 100 | } 101 | } 102 | if ($replace) { 103 | if($confirm){ 104 | echo Color::danger("do you want to release {$filename}? [ Y / N (default) ] : "); 105 | $answer = strtolower(trim(strtoupper(fgets(STDIN)))); 106 | if (!in_array($answer, ['y', 'yes'])) { 107 | return; 108 | } 109 | } 110 | File::copyFile($source, $destination); 111 | } 112 | } 113 | 114 | public static function opCacheClear() 115 | { 116 | if (function_exists('apc_clear_cache')) { 117 | apc_clear_cache(); 118 | } 119 | if (function_exists('opcache_reset')) { 120 | opcache_reset(); 121 | } 122 | } 123 | 124 | public static function bridgeCall(string $commandName, callable $function, $action, $params = [], $timeout = 3) 125 | { 126 | $arg = ['action' => $action] + $params; 127 | $package = Bridge::getInstance()->call($commandName, $arg, $timeout); 128 | if ($package->getStatus() == Package::STATUS_SUCCESS) { 129 | $result = call_user_func($function, $package); 130 | } else { 131 | $result = Color::error($package->getMsg()); 132 | } 133 | return $result; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | conf = $config; 29 | } 30 | 31 | function storageHandler(AbstractConfig $config = null):AbstractConfig 32 | { 33 | if($config){ 34 | $this->conf = $config; 35 | } 36 | return $this->conf; 37 | } 38 | 39 | 40 | /** 41 | * 获取配置项 42 | * @param string $keyPath 配置项名称 支持点语法 43 | * @return array|mixed|null 44 | */ 45 | public function getConf($keyPath = '') 46 | { 47 | if ($keyPath == '') { 48 | return $this->toArray(); 49 | } 50 | return $this->conf->getConf($keyPath); 51 | } 52 | 53 | 54 | public function setConf($keyPath, $data): bool 55 | { 56 | return $this->conf->setConf($keyPath, $data); 57 | } 58 | 59 | 60 | public function toArray(): array 61 | { 62 | return $this->conf->getConf(); 63 | } 64 | 65 | 66 | public function load(array $conf): bool 67 | { 68 | return $this->conf->load($conf); 69 | } 70 | 71 | public function merge(array $conf):bool 72 | { 73 | return $this->conf->merge($conf); 74 | } 75 | 76 | /** 77 | * 载入一个文件的配置项 78 | * @param string $filePath 配置文件路径 79 | */ 80 | public function loadFile($filePath,bool $merge = true):bool 81 | { 82 | if (file_exists($filePath)) { 83 | $confData = require_once $filePath; 84 | if(is_array($confData)){ 85 | if($merge){ 86 | $this->conf->merge($confData); 87 | }else{ 88 | $this->conf->load($confData); 89 | } 90 | return true; 91 | } 92 | } 93 | return false; 94 | } 95 | 96 | /** 97 | * 载入自定义配置文件夹里的所有配置文件 98 | * @param string $dirPath 配置文件夹 99 | * @param bool $merge 是否将内容合并入主配置 100 | */ 101 | public function loadDir (string $dirPath, bool $merge = true):bool 102 | { 103 | if(is_dir($dirPath)){ 104 | $fileList = File::scanDirectory($dirPath); 105 | foreach ($fileList['files'] as $filePath){ 106 | $this->loadFile($filePath,$merge); 107 | } 108 | return true; 109 | } 110 | return false; 111 | } 112 | 113 | public function loadEnv(string $file,bool $merge = true):bool 114 | { 115 | if(file_exists($file)){ 116 | $data = parse_ini_file($file,true); 117 | if(is_array($data)){ 118 | if($merge){ 119 | $this->conf->merge($data); 120 | }else{ 121 | $this->conf->load($data); 122 | } 123 | return true; 124 | } 125 | } 126 | return false; 127 | } 128 | 129 | public function clear():bool 130 | { 131 | return $this->conf->clear(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Core.php: -------------------------------------------------------------------------------- 1 | runMode = $mode; 84 | } 85 | return $this->runMode; 86 | } 87 | 88 | function initialize() 89 | { 90 | //先加载配置文件 91 | $this->loadEnv(); 92 | //临时文件和Log目录初始化 93 | $this->sysDirectoryInit(); 94 | // 初始化initialize 95 | EasySwooleEvent::initialize(); 96 | //注册错误回调 97 | $this->registerErrorHandler(); 98 | return $this; 99 | } 100 | 101 | function createServer() 102 | { 103 | $conf = Config::getInstance()->getConf('MAIN_SERVER'); 104 | ServerManager::getInstance()->createSwooleServer( 105 | $conf['PORT'], $conf['SERVER_TYPE'], $conf['LISTEN_ADDRESS'], $conf['SETTING'], $conf['RUN_MODEL'], $conf['SOCK_TYPE'] 106 | ); 107 | EasySwooleEvent::mainServerCreate(ServerManager::getInstance()->getEventRegister()); 108 | $this->registerDefaultCallBack(ServerManager::getInstance()->getSwooleServer(), $conf['SERVER_TYPE']); 109 | $this->extraHandler(); 110 | return $this; 111 | } 112 | 113 | function start() 114 | { 115 | //给主进程也命名 116 | $serverName = Config::getInstance()->getConf('SERVER_NAME'); 117 | $this->setProcessName($serverName); 118 | 119 | //启动 120 | ServerManager::getInstance()->start(); 121 | } 122 | 123 | private function sysDirectoryInit(): void 124 | { 125 | //创建临时目录 请以绝对路径,不然守护模式运行会有问题 126 | $tempDir = Config::getInstance()->getConf('TEMP_DIR'); 127 | if (empty($tempDir)) { 128 | $tempDir = EASYSWOOLE_ROOT . '/Temp'; 129 | Config::getInstance()->setConf('TEMP_DIR', $tempDir); 130 | } else { 131 | $tempDir = rtrim($tempDir, '/'); 132 | } 133 | if (!is_dir($tempDir)) { 134 | File::createDirectory($tempDir); 135 | } 136 | defined('EASYSWOOLE_TEMP_DIR') or define('EASYSWOOLE_TEMP_DIR', $tempDir); 137 | 138 | $logDir = Config::getInstance()->getConf('LOG.dir'); 139 | if (empty($logDir)) { 140 | $logDir = EASYSWOOLE_ROOT . '/Log'; 141 | Config::getInstance()->setConf('LOG.dir', $logDir); 142 | } else { 143 | $logDir = rtrim($logDir, '/'); 144 | } 145 | if (!is_dir($logDir)) { 146 | File::createDirectory($logDir); 147 | } 148 | defined('EASYSWOOLE_LOG_DIR') or define('EASYSWOOLE_LOG_DIR', $logDir); 149 | 150 | $mode = $this->runMode(); 151 | 152 | // 设置默认文件目录值(如果自行指定了目录则优先使用指定的) 153 | if (!Config::getInstance()->getConf('MAIN_SERVER.SETTING.pid_file')) { 154 | Config::getInstance()->setConf('MAIN_SERVER.SETTING.pid_file', $tempDir . "/{$mode}.pid"); 155 | } 156 | if (!Config::getInstance()->getConf('MAIN_SERVER.SETTING.log_file')) { 157 | Config::getInstance()->setConf('MAIN_SERVER.SETTING.log_file', $logDir . "/{$mode}.swoole.log"); 158 | } 159 | } 160 | 161 | private function registerErrorHandler() 162 | { 163 | ini_set("display_errors", "On"); 164 | $level = Di::getInstance()->get(SysConst::ERROR_REPORT_LEVEL); 165 | if ($level === null) { 166 | $level = E_ALL; 167 | } 168 | error_reporting($level); 169 | 170 | //初始化配置Logger 171 | $logger = Di::getInstance()->get(SysConst::LOGGER_HANDLER); 172 | if (!$logger instanceof LoggerInterface) { 173 | $logger = Config::getInstance()->getConf('LOG.handler'); 174 | } 175 | if (!$logger instanceof LoggerInterface) { 176 | $logger = new DefaultLogger(EASYSWOOLE_LOG_DIR,$this->runMode); 177 | } 178 | $level = intval(Config::getInstance()->getConf('LOG.level')); 179 | Logger::getInstance($logger)->logLevel($level); 180 | 181 | $logConsole = Config::getInstance()->getConf('LOG.logConsole'); 182 | Logger::getInstance()->logConsole($logConsole); 183 | 184 | $ignoreCategory = Config::getInstance()->getConf('LOG.ignoreCategory'); 185 | Logger::getInstance()->ignoreCategory($ignoreCategory); 186 | 187 | $displayConsole = Config::getInstance()->getConf('LOG.displayConsole'); 188 | Logger::getInstance()->displayConsole($displayConsole); 189 | 190 | //初始化追追踪器 191 | $trigger = Di::getInstance()->get(SysConst::TRIGGER_HANDLER); 192 | Trigger::getInstance($trigger); 193 | 194 | //在没有配置自定义错误处理器的情况下,转化为trigger处理 195 | $errorHandler = Di::getInstance()->get(SysConst::ERROR_HANDLER); 196 | if (!is_callable($errorHandler)) { 197 | $errorHandler = function ($errorCode, $description, $file = null, $line = null) { 198 | $l = new Location(); 199 | $l->setFile($file); 200 | $l->setLine($line); 201 | Trigger::getInstance()->error($description, $errorCode, $l); 202 | }; 203 | } 204 | set_error_handler($errorHandler); 205 | 206 | $func = Di::getInstance()->get(SysConst::SHUTDOWN_FUNCTION); 207 | if (!is_callable($func)) { 208 | $func = function () { 209 | $error = error_get_last(); 210 | if (!empty($error)) { 211 | $l = new Location(); 212 | $l->setFile($error['file']); 213 | $l->setLine($error['line']); 214 | Trigger::getInstance()->error($error['message'], $error['type'], $l); 215 | } 216 | }; 217 | } 218 | register_shutdown_function($func); 219 | } 220 | 221 | private function registerDefaultCallBack(Server $server, int $serverType) 222 | { 223 | /* 224 | * 注册默认回调 225 | */ 226 | if (in_array($serverType, [EASYSWOOLE_WEB_SERVER, EASYSWOOLE_WEB_SOCKET_SERVER], true)) { 227 | $namespace = Di::getInstance()->get(SysConst::HTTP_CONTROLLER_NAMESPACE); 228 | if (empty($namespace)) { 229 | $namespace = 'App\\HttpController\\'; 230 | } 231 | $depth = intval(Di::getInstance()->get(SysConst::HTTP_CONTROLLER_MAX_DEPTH)); 232 | $depth = $depth > 5 ? $depth : 5; 233 | $max = intval(Di::getInstance()->get(SysConst::HTTP_CONTROLLER_POOL_MAX_NUM)); 234 | if ($max == 0) { 235 | $max = 500; 236 | } 237 | $waitTime = intval(Di::getInstance()->get(SysConst::HTTP_CONTROLLER_POOL_WAIT_TIME)); 238 | if ($waitTime == 0) { 239 | $waitTime = 5; 240 | } 241 | $dispatcher = Dispatcher::getInstance()->setNamespacePrefix($namespace)->setMaxDepth($depth); 242 | //补充HTTP_EXCEPTION_HANDLER默认回调 243 | $httpExceptionHandler = Di::getInstance()->get(SysConst::HTTP_EXCEPTION_HANDLER); 244 | if (!is_callable($httpExceptionHandler)) { 245 | $httpExceptionHandler = function ($throwable, $request, $response) { 246 | $response->withStatus(Status::CODE_INTERNAL_SERVER_ERROR); 247 | $response->write(nl2br($throwable->getMessage() . "\n" . $throwable->getTraceAsString())); 248 | Trigger::getInstance()->throwable($throwable); 249 | }; 250 | Di::getInstance()->set(SysConst::HTTP_EXCEPTION_HANDLER, $httpExceptionHandler); 251 | } 252 | $dispatcher->setHttpExceptionHandler($httpExceptionHandler); 253 | $requestHook = Di::getInstance()->get(SysConst::HTTP_GLOBAL_ON_REQUEST); 254 | $afterRequestHook = Di::getInstance()->get(SysConst::HTTP_GLOBAL_AFTER_REQUEST); 255 | EventHelper::on($server, EventRegister::onRequest, function (SwooleRequest $request, SwooleResponse $response) use ($dispatcher, $requestHook, $afterRequestHook) { 256 | $request_psr = new Request($request); 257 | $response_psr = new Response($response); 258 | try { 259 | $ret = null; 260 | if (is_callable($requestHook)) { 261 | $ret = call_user_func($requestHook, $request_psr, $response_psr); 262 | } 263 | if ($ret !== false) { 264 | $dispatcher->dispatch($request_psr, $response_psr); 265 | } 266 | } catch (\Throwable $throwable) { 267 | call_user_func(Di::getInstance()->get(SysConst::HTTP_EXCEPTION_HANDLER), $throwable, $request_psr, $response_psr); 268 | } finally { 269 | try { 270 | if (is_callable($afterRequestHook)) { 271 | call_user_func($afterRequestHook, $request_psr, $response_psr); 272 | } 273 | } catch (\Throwable $throwable) { 274 | call_user_func(Di::getInstance()->get(SysConst::HTTP_EXCEPTION_HANDLER), $throwable, $request_psr, $response_psr); 275 | } 276 | } 277 | $response_psr->__response(); 278 | }); 279 | } 280 | 281 | $register = ServerManager::getInstance()->getEventRegister(); 282 | //注册默认的worker start 283 | EventHelper::registerWithAdd($register, EventRegister::onWorkerStart, function (Server $server, $workerId) { 284 | $serverName = Config::getInstance()->getConf('SERVER_NAME'); 285 | $type = 'Unknown'; 286 | if (($workerId < Config::getInstance()->getConf('MAIN_SERVER.SETTING.worker_num')) && $workerId >= 0) { 287 | $type = 'Worker'; 288 | } 289 | $processName = "{$serverName}.{$this->runMode}.{$type}.{$workerId}"; 290 | $this->setProcessName($processName); 291 | $table = Manager::getInstance()->getProcessTable(); 292 | $pid = getmypid(); 293 | $table->set($pid, [ 294 | 'pid' => $pid, 295 | 'name' => $processName, 296 | 'group' => "{$serverName}.{$this->runMode}.{$type}", 297 | 'startUpTime' => time() 298 | ]); 299 | Timer::tick(1 * 1000, function () use ($table, $pid) { 300 | $table->set($pid, [ 301 | 'memoryUsage' => memory_get_usage(), 302 | 'memoryPeakUsage' => memory_get_peak_usage(true) 303 | ]); 304 | }); 305 | register_shutdown_function(function () use ($pid) { 306 | $table = Manager::getInstance()->getProcessTable(); 307 | $table->del($pid); 308 | }); 309 | }); 310 | //onWorkerStop,onWorkerExit,register_shutdown_function冗余清理 311 | EventHelper::registerWithAdd($register, $register::onWorkerStop, function () { 312 | $table = Manager::getInstance()->getProcessTable(); 313 | $pid = getmypid(); 314 | $table->del($pid); 315 | Timer::clearAll(); 316 | SwooleEvent::exit(); 317 | }); 318 | 319 | /* 320 | * 开启reload async的时候,清理事件 321 | */ 322 | EventHelper::registerWithAdd($register, $register::onWorkerExit, function () { 323 | $table = Manager::getInstance()->getProcessTable(); 324 | $pid = getmypid(); 325 | $table->del($pid); 326 | Timer::clearAll(); 327 | SwooleEvent::exit(); 328 | }); 329 | 330 | EventHelper::registerWithAdd($register, EventRegister::onManagerStart, function (Server $server) { 331 | $serverName = Config::getInstance()->getConf('SERVER_NAME'); 332 | $this->setProcessName($serverName . '.Manager'); 333 | }); 334 | } 335 | 336 | public function loadEnv() 337 | { 338 | $mode = CommandManager::getInstance()->getOpt('mode'); 339 | if (!empty($mode)) { 340 | $this->runMode($mode); 341 | } 342 | 343 | $file = EASYSWOOLE_ROOT . "/{$this->runMode}.php"; 344 | if (!file_exists($file)) { 345 | die(Color::error("can not load config file {$this->runMode}.php") . "\n"); 346 | } 347 | Config::getInstance()->loadFile($file); 348 | } 349 | 350 | private function extraHandler() 351 | { 352 | $mode = $this->runMode(); 353 | $serverName = Config::getInstance()->getConf('SERVER_NAME').".{$mode}"; 354 | //注册Task进程 355 | $config = Config::getInstance()->getConf('MAIN_SERVER.TASK'); 356 | $config = TaskManager::getInstance()->getConfig()->restore($config); 357 | $config->setTempDir(EASYSWOOLE_TEMP_DIR); 358 | $config->setServerName($serverName); 359 | $config->setOnException(function (\Throwable $throwable) { 360 | Trigger::getInstance()->throwable($throwable); 361 | }); 362 | $server = ServerManager::getInstance()->getSwooleServer(); 363 | TaskManager::getInstance()->attachToServer($server); 364 | //初始化进程管理器 365 | Manager::getInstance()->attachToServer($server); 366 | //初始化Bridge 367 | Bridge::getInstance()->attachServer($server, $serverName); 368 | //注册Crontab 369 | Crontab::getInstance()->getConfig()->setTempDir(EASYSWOOLE_TEMP_DIR); 370 | Crontab::getInstance()->getConfig()->setServerName($serverName); 371 | Crontab::getInstance()->getConfig()->setOnException(function (\Throwable $throwable){ 372 | Trigger::getInstance()->throwable($throwable); 373 | }); 374 | Crontab::getInstance()->attachToServer($server); 375 | } 376 | 377 | /** 378 | * 设置进程名 379 | * @param string $processName 380 | */ 381 | protected function setProcessName(string $processName = ''): void 382 | { 383 | if (empty($processName)) { 384 | return; 385 | } 386 | $banOS = ['Darwin','CYGWIN','WINNT']; 387 | foreach ($banOS as $os){ 388 | if (strpos(PHP_OS, $os) !== false) { 389 | return ; 390 | } 391 | } 392 | 393 | if (function_exists('cli_set_process_title')) { 394 | cli_set_process_title($processName); 395 | } else if (function_exists('swoole_set_process_name')) { 396 | swoole_set_process_name($processName); 397 | } 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /src/Crontab/Crontab.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 35 | $this->callback = new Event(); 36 | } 37 | 38 | public function onLog(): Event 39 | { 40 | return $this->callback; 41 | } 42 | 43 | public function logLevel(?int $level = null) 44 | { 45 | if ($level !== null) { 46 | $this->logLevel = $level; 47 | return $this; 48 | } 49 | return $this->logLevel; 50 | } 51 | 52 | public function displayConsole(?bool $is = null) 53 | { 54 | if($is === null){ 55 | return $this->displayConsole; 56 | }else{ 57 | $this->displayConsole = $is; 58 | return $this; 59 | } 60 | } 61 | 62 | public function logConsole(?bool $is = null) 63 | { 64 | if ($is === null) { 65 | return $this->logConsole; 66 | } else { 67 | $this->logConsole = $is; 68 | return $this; 69 | } 70 | } 71 | 72 | public function ignoreCategory(?array $arr = null) 73 | { 74 | if ($arr === null) { 75 | return $this->ignoreCategory; 76 | } else { 77 | $this->ignoreCategory = $arr; 78 | return $this; 79 | } 80 | } 81 | 82 | public function log(?string $msg, int $logLevel = LoggerInterface::LOG_LEVEL_DEBUG, string $category = 'debug') 83 | { 84 | if ($logLevel < $this->logLevel) { 85 | return; 86 | } 87 | 88 | if (in_array($category, $this->ignoreCategory)) { 89 | return; 90 | } 91 | 92 | if ($this->logConsole) { 93 | $this->console($msg, $logLevel, $category); 94 | } 95 | 96 | $this->logger->log($msg, $logLevel, $category); 97 | $calls = $this->callback->all(); 98 | foreach ($calls as $call) { 99 | call_user_func($call, $msg, $logLevel, $category); 100 | } 101 | } 102 | 103 | public function console(?string $msg, int $logLevel = LoggerInterface::LOG_LEVEL_DEBUG, string $category = 'debug') 104 | { 105 | if($this->displayConsole){ 106 | $this->logger->console($msg, $logLevel, $category); 107 | } 108 | } 109 | 110 | public function debug(?string $msg, string $category = 'debug') 111 | { 112 | $this->log($msg, LoggerInterface::LOG_LEVEL_DEBUG, $category); 113 | } 114 | 115 | public function info(?string $msg, string $category = 'info') 116 | { 117 | $this->log($msg, LoggerInterface::LOG_LEVEL_INFO, $category); 118 | } 119 | 120 | public function notice(?string $msg, string $category = 'notice') 121 | { 122 | $this->log($msg, LoggerInterface::LOG_LEVEL_NOTICE, $category); 123 | } 124 | 125 | /** 保留,为了兼容已经在低版本中使用了这个方法的程序 */ 126 | public function waring(?string $msg, string $category = 'waring') 127 | { 128 | $this->log($msg, LoggerInterface::LOG_LEVEL_WARNING, $category); 129 | } 130 | 131 | public function warning(?string $msg, string $category = 'warning') 132 | { 133 | $this->log($msg, LoggerInterface::LOG_LEVEL_WARNING, $category); 134 | } 135 | 136 | public function error(?string $msg, string $category = 'error') 137 | { 138 | $this->log($msg, LoggerInterface::LOG_LEVEL_ERROR, $category); 139 | } 140 | 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/Resource/Config._php: -------------------------------------------------------------------------------- 1 | "EasySwoole", 7 | 'MAIN_SERVER' => [ 8 | 'LISTEN_ADDRESS' => '0.0.0.0', 9 | 'PORT' => 9501, 10 | 'SERVER_TYPE' => EASYSWOOLE_WEB_SERVER, //可选为 EASYSWOOLE_SERVER EASYSWOOLE_WEB_SERVER EASYSWOOLE_WEB_SOCKET_SERVER 11 | 'SOCK_TYPE' => SWOOLE_TCP, 12 | 'RUN_MODEL' => SWOOLE_PROCESS, 13 | 'SETTING' => [ 14 | 'worker_num' => 8, 15 | 'reload_async' => true, 16 | 'max_wait_time' => 3 17 | ], 18 | 'TASK' => [ 19 | 'workerNum' => 4, 20 | 'maxRunningNum' => 128, 21 | 'timeout' => 15 22 | ] 23 | ], 24 | "LOG" => [ 25 | 'dir' => null, 26 | 'level' => LoggerInterface::LOG_LEVEL_DEBUG, 27 | 'handler' => null, 28 | 'logConsole' => true, 29 | 'displayConsole' => true, 30 | 'ignoreCategory' => [] 31 | ], 32 | 'TEMP_DIR' => null 33 | ]; 34 | -------------------------------------------------------------------------------- /src/Resource/EasySwooleEvent._php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | EasySwoole 8 | 40 | 41 | 42 | 43 |

哇靠!页面丢了?

44 |

看来又有程序猿要被开除了

45 |
          .----.
46 |        _.'__    `.
47 |    .--($)($$)---/#\
48 |  .' @          /###\
49 |  :         ,   #####
50 |   `-..__.-' _.-\###/
51 |         `;_:    `"'
52 |       .'"""""`.
53 |      /,  ya ,\\
54 |     //  404!  \\
55 |     `-._______.-'
56 |     ___`. | .'___
57 |    (______|______)
58 | 
59 | 60 | -------------------------------------------------------------------------------- /src/Resource/Http/Index._php: -------------------------------------------------------------------------------- 1 | response()->write(file_get_contents($file)); 19 | } 20 | 21 | function test() 22 | { 23 | $this->response()->write('this is test'); 24 | } 25 | 26 | protected function actionNotFound(?string $action) 27 | { 28 | $this->response()->withStatus(404); 29 | $file = EASYSWOOLE_ROOT.'/vendor/easyswoole/easyswoole/src/Resource/Http/404.html'; 30 | if(!is_file($file)){ 31 | $file = EASYSWOOLE_ROOT.'/src/Resource/Http/404.html'; 32 | } 33 | $this->response()->write(file_get_contents($file)); 34 | } 35 | } -------------------------------------------------------------------------------- /src/Resource/Http/Router._php: -------------------------------------------------------------------------------- 1 | get('/router','/test'); 20 | /* 21 | * eg path : /closure/index.html ; /closure/ ; /closure 22 | */ 23 | $routeCollector->get('/closure',function (Request $request,Response $response){ 24 | $response->write('this is closure router'); 25 | //不再进入控制器解析 26 | return false; 27 | }); 28 | } 29 | } -------------------------------------------------------------------------------- /src/Resource/Http/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | easySwoole|swoole框架|swoole拓展|swoole 8 | 9 | 10 | 13 | 14 | 15 |
16 |
17 |
18 | 欢迎使用 EasySwoole 19 |
20 | 24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /src/Resource/bootstrap._php: -------------------------------------------------------------------------------- 1 | setScript(current($argv)); 25 | $caller->setCommand(next($argv)); 26 | $caller->setParams($argv); 27 | reset($argv); 28 | 29 | $ret = CommandRunner::getInstance()->run($caller); 30 | if($ret && !empty($ret->getMsg())){ 31 | echo $ret->getMsg()."\n"; 32 | } 33 | -------------------------------------------------------------------------------- /src/ServerManager.php: -------------------------------------------------------------------------------- 1 | mainServerEventRegister = new EventRegister(); 33 | } 34 | /** 35 | * @param string $serverName 36 | * @return null|Server|Server\Port|WebSocketServer|HttpServer 37 | */ 38 | function getSwooleServer(string $serverName = null) 39 | { 40 | if($serverName === null){ 41 | return $this->swooleServer; 42 | }else{ 43 | if(isset($this->subServer[$serverName])){ 44 | return $this->subServer[$serverName]; 45 | } 46 | return null; 47 | } 48 | } 49 | 50 | function createSwooleServer($port,$type ,$address = '0.0.0.0',array $setting = [],...$args):bool 51 | { 52 | switch ($type){ 53 | case EASYSWOOLE_SERVER:{ 54 | $this->swooleServer = new Server($address,$port,...$args); 55 | break; 56 | } 57 | case EASYSWOOLE_WEB_SERVER:{ 58 | $this->swooleServer = new HttpServer($address,$port,...$args); 59 | break; 60 | } 61 | case EASYSWOOLE_WEB_SOCKET_SERVER:{ 62 | $this->swooleServer = new WebSocketServer($address,$port,...$args); 63 | break; 64 | } 65 | default:{ 66 | Trigger::getInstance()->error("unknown server type :{$type}"); 67 | return false; 68 | } 69 | } 70 | if($this->swooleServer){ 71 | $this->swooleServer->set($setting); 72 | } 73 | return true; 74 | } 75 | 76 | 77 | public function addServer(string $serverName,int $port,int $type = SWOOLE_TCP,string $listenAddress = '0.0.0.0',array $setting = []):EventRegister 78 | { 79 | $eventRegister = new EventRegister(); 80 | $subPort = $this->swooleServer->addlistener($listenAddress,$port,$type); 81 | if(!empty($setting)){ 82 | $subPort->set($setting); 83 | } 84 | $this->subServer[$serverName] = $subPort; 85 | $this->subServerRegister[$serverName] = [ 86 | 'port'=>$port, 87 | 'listenAddress'=>$listenAddress, 88 | 'type'=>$type, 89 | 'setting'=>$setting, 90 | 'eventRegister'=>$eventRegister 91 | ]; 92 | return $eventRegister; 93 | } 94 | 95 | function getEventRegister(string $serverName = null):?EventRegister 96 | { 97 | if($serverName === null){ 98 | return $this->mainServerEventRegister; 99 | }else if(isset($this->subServerRegister[$serverName])){ 100 | return $this->subServerRegister[$serverName]['eventRegister']; 101 | } 102 | return null; 103 | } 104 | 105 | function start() 106 | { 107 | //注册主服务事件回调 108 | $events = $this->getEventRegister()->all(); 109 | foreach ($events as $event => $callback){ 110 | $this->getSwooleServer()->on($event, function (...$args) use ($callback) { 111 | foreach ($callback as $item) { 112 | call_user_func($item,...$args); 113 | } 114 | }); 115 | } 116 | //注册子服务的事件回调 117 | foreach ($this->subServer as $serverName => $subPort ){ 118 | $events = $this->subServerRegister[$serverName]['eventRegister']->all(); 119 | foreach ($events as $event => $callback){ 120 | $subPort->on($event, function (...$args) use ($callback) { 121 | foreach ($callback as $item) { 122 | call_user_func($item,...$args); 123 | } 124 | }); 125 | } 126 | } 127 | $this->isStart = true; 128 | $this->getSwooleServer()->start(); 129 | } 130 | 131 | function isStart():bool 132 | { 133 | return $this->isStart; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Swoole/EventHelper.php: -------------------------------------------------------------------------------- 1 | set($event,$callback); 17 | } 18 | 19 | public static function registerWithAdd(EventRegister $register,string $event,callable $callback):void 20 | { 21 | $register->add($event,$callback); 22 | } 23 | 24 | public static function on(\Swoole\Server $server,string $event,callable $callback) 25 | { 26 | $server->on($event,$callback); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Swoole/EventRegister.php: -------------------------------------------------------------------------------- 1 | getConstants(); 43 | parent::__construct(array_values($constants)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/SysConst.php: -------------------------------------------------------------------------------- 1 | trigger = $trigger; 33 | $this->onError = new Event(); 34 | $this->onException = new Event(); 35 | } 36 | 37 | public function error($msg,int $errorCode = E_USER_ERROR,Location $location = null) 38 | { 39 | if($location == null){ 40 | $location = $this->getLocation(); 41 | } 42 | $this->trigger->error($msg,$errorCode,$location); 43 | $all = $this->onError->all(); 44 | foreach ($all as $call){ 45 | call_user_func($call,$msg,$errorCode,$location); 46 | } 47 | } 48 | 49 | public function throwable(\Throwable $throwable) 50 | { 51 | $this->trigger->throwable($throwable); 52 | $all = $this->onException->all(); 53 | foreach ($all as $call){ 54 | call_user_func($call,$throwable); 55 | } 56 | } 57 | 58 | public function onError():Event 59 | { 60 | return $this->onError; 61 | } 62 | 63 | public function onException():Event 64 | { 65 | return $this->onException; 66 | } 67 | 68 | private function getLocation():Location 69 | { 70 | $location = new Location(); 71 | $debugTrace = debug_backtrace(); 72 | array_shift($debugTrace); 73 | $caller = array_shift($debugTrace); 74 | $location->setLine($caller['line']); 75 | $location->setFile($caller['file']); 76 | return $location; 77 | } 78 | } -------------------------------------------------------------------------------- /src/Utility/DefaultTrigger.php: -------------------------------------------------------------------------------- 1 | setLine($caller['line']); 22 | $location->setFile($caller['file']); 23 | } 24 | 25 | Logger::getInstance()->log("{$msg} at file:{$location->getFile()} line:{$location->getLine()}", $this->errorMapLogLevel($errorCode), 'trigger'); 26 | } 27 | 28 | public function throwable(\Throwable $throwable) 29 | { 30 | $msg = "{$throwable->getMessage()} at file:{$throwable->getFile()} line:{$throwable->getLine()}"; 31 | Logger::getInstance()->log($msg, LoggerInterface::LOG_LEVEL_ERROR, 'trigger'); 32 | } 33 | 34 | private function errorMapLogLevel(int $errorCode) 35 | { 36 | switch ($errorCode) { 37 | case E_PARSE: 38 | case E_ERROR: 39 | case E_CORE_ERROR: 40 | case E_COMPILE_ERROR: 41 | case E_USER_ERROR: 42 | return LoggerInterface::LOG_LEVEL_ERROR; 43 | case E_WARNING: 44 | case E_USER_WARNING: 45 | case E_COMPILE_WARNING: 46 | case E_RECOVERABLE_ERROR: 47 | return LoggerInterface::LOG_LEVEL_WARNING; 48 | case E_NOTICE: 49 | case E_USER_NOTICE: 50 | case E_STRICT: 51 | case E_DEPRECATED: 52 | case E_USER_DEPRECATED: 53 | return LoggerInterface::LOG_LEVEL_NOTICE; 54 | default : 55 | return LoggerInterface::LOG_LEVEL_INFO; 56 | } 57 | } 58 | } 59 | --------------------------------------------------------------------------------