vkoori / one
一个可以在fpm或swoole协程模式下运行的极简PHP框架
v1.13.4
2022-11-09 08:32 UTC
Requires
- php: >=8.1
- ext-json: *
- ext-pdo: *
- lizhichao/one-ck: >=0.1.11
- rakit/validation: ^1.4
Suggests
- ext-msgpack: globalData和Rpc服务通讯的打包
- ext-pdo: pdo数据库驱动
- ext-redis: 如果缓存驱动为Redis
- ext-swoole: 常驻内存运行必须的扩展
README
One - 一个支持[swoole | php-fpm]环境的极简高性能PHP框架
- 快速 - 即使在php-fpm下也能在
1ms
内响应请求 - 简单 - 让你专注于使用
one
而不是如何使用one
- 灵活 - 每个组件都是松散耦合的,可以灵活匹配和使用,使用方式一致
- 原生sql可以与模型关系
with
一起使用,关系可以跨数据库类型 - session可以在http、websocket甚至tcp、udp和cli下使用
- ...
- 原生sql可以与模型关系
- 高效 - 运行性能、开发效率高,易于维护。
- 轻量级 - 没有其他依赖,所有组件的总代码量(从路由和orm到)不超过500k。如果没有复杂的调用关系,可以快速掌握设计原则
- 子数据库和子表 - 支持无线子数据库和表,上层使用方法保持不变
hello world
安装
composer create-project lizhichao/one-app app cd app php App/swoole.php # stop : `php App/swoole.php -o stop` # reload : `php App/swoole.php -o reload` # Daemon start : `php App/swoole.php -o start` curl http://127.0.0.1:8081/
性能
参考:
主要组件
- 路由器
- 支持贪婪匹配和优先级
- 支持ws/tcp/http……任何协议
- 性能良好,添加数十万个路由不会降低解析性能
- 路由分组、中间件...应有尽有
- ORM
- 支持数据库:mysql,clickHouse,
- 关系处理:一对一、一对多、多对一、多态...有各种关系,可以跨数据库类型关联
- 缓存:自动刷新数据,支持配置各种缓存粒度
- 事件:所有操作都可以捕获,包括你使用原生SQL操作数据库
- 数据库连接:同步、异步、阻塞、断开和重新连接都支持
- sql模板:自动生成模板id,你可以了解项目有哪些类型的sql,调用次数的比例,并为后续的数据优化提供数据支持。
- 语句重用:提供SQL执行性能
- 模型支持动态子数据库子表和大量数据
- rpc
- 可以自动生成远程方法映射,支持IDE提示
- 直接调用映射方法 == 调用远程方法,支持链式调用
- 支持
rpc中间件
,认证,加密解密,缓存...
- 日志
- 完整信息:记录完整的文件名+行号,以便快速定位代码位置
- requestId:你可以轻松查看整个请求日志信息和服务关系
路由器
Router::get('/', \App\Controllers\IndexController::class . '@index'); // router with params Router::get('/user/{id}', \App\Controllers\IndexController::class . '@user'); // router with group Router::group(['namespace'=>'App\\Test\\WebSocket'],function (){ // websocket router Router::set('ws','/a','TestController@abc'); Router::set('ws','/b','TestController@bbb'); }); // Middleware Router::group([ 'middle' => [ \App\Test\MixPro\TestMiddle::class . '@checkSession' ] ], function () { Router::get('/mix/ws', HttpController::class . '@ws'); Router::get('/user/{id}', \App\Controllers\IndexController::class . '@user'); Router::post('/mix/http/send', HttpController::class . '@httpSend'); });
orm
定义模型
namespace App\Model; use One\Database\Mysql\Model; // There is no need to specify the primary key in the model, the framework will cache the database structure // Automatically match the primary key, automatically filter the fields in the non-table structure class User extends Model { // Define the table name corresponding to the model CONST TABLE = 'users'; // define relationship public function articles() { return $this->hasMany('id',Article::class,'user_id'); } // define event // Whether to enable automatic caching // …… }
使用模型
- 数据库连接在
fpm
下是一个单列 - 在
swoole
模式下,所有数据库操作都自动切换到连接池
// Query a record $user = User::find(1); // Related query $user_list = User::whereIn('id',[1,2,3])->with('articles')->findAll()->toArray(); // update $r = $user->update(['name' => 'aaa']); // or $r = user::where('id',1)->update(['name' => 'aaa']); // $r To influence the number of records
缓存
// Set cache without expiration time Cache::set('ccc',1); // Set the cache to expire in 1 minute Cache::set('ccc',1,60); Cache::get('ccc'); // or cache ccc expires 10s under tag1 Cache::get('ccc',function (){ return 'info'; },10,['tag1']); // Refresh all caches under tag1 Cache::flush('tag1');
HTTP/TCP/WEBSOCKET/UDP
启动websocket服务器,添加http服务监控,添加tcp服务监控
[ // Main server 'server' => [ 'server_type' => \One\Swoole\OneServer::SWOOLE_WEBSOCKET_SERVER, 'port' => 8082, // Event callback 'action' => \One\Swoole\Server\WsServer::class, 'mode' => SWOOLE_PROCESS, 'sock_type' => SWOOLE_SOCK_TCP, 'ip' => '0.0.0.0', // swoole Server setting parameters 'set' => [ 'worker_num' => 5 ] ], // Add listener 'add_listener' => [ [ 'port' => 8081, // Event callback 'action' => \App\Server\AppHttpPort::class, 'type' => SWOOLE_SOCK_TCP, 'ip' => '0.0.0.0', // Set parameters for monitoring 'set' => [ 'open_http_protocol' => true, 'open_websocket_protocol' => false ] ], [ 'port' => 8083, // Unpacking protocol 'pack_protocol' => \One\Protocol\Text::class, // Event callback 'action' => \App\Test\MixPro\TcpPort::class, 'type' => SWOOLE_SOCK_TCP, 'ip' => '0.0.0.0', // Set parameters for monitoring 'set' => [ 'open_http_protocol' => false, 'open_websocket_protocol' => false ] ] ] ];
RPC
像调用本项目的方法一样调用远程服务器的方法。跨语言,跨机器。
服务
启动 rpc 服务。框架为每种协议内置了 rpc 服务,只需将其添加到上述配置文件中的 action
即可。例如:支持 http
调用,支持 tcp
调用。
// http Protocol rpc service [ 'port' => 8082, 'action' => \App\Server\RpcHttpPort::class, 'type' => SWOOLE_SOCK_TCP, 'ip' => '0.0.0.0', 'set' => [ 'open_http_protocol' => true, 'open_websocket_protocol' => false ] ], // tcp Protocol rpc service [ 'port' => 8083, 'action' => \App\Server\RpcTcpPort::class, 'type' => SWOOLE_SOCK_TCP, 'pack_protocol' => \One\Protocol\Frame::class, // tcp packing protocol 'ip' => '0.0.0.0', 'set' => [ 'open_http_protocol' => false, 'open_websocket_protocol' => false, 'open_length_check' => 1, 'package_length_func' => '\One\Protocol\Frame::length', 'package_body_offset' => \One\Protocol\Frame::HEAD_LEN, ] ]
将特定服务添加到 rpc,例如,有一个类 Abc
class Abc { private $a; public function __construct($a = 0) { $this->a = $a; } public function add($a, $b) { return $this->a + $a + $b; } public function time() { return date('Y-m-d H:i:s'); } public function setA($a) { $this->a = $a; return $this; } }
将 Abc
添加到 rpc 服务
// Add Abc to rpc service RpcServer::add(Abc::class); // If you don't want to add all the methods under Abc to the rpc service, you can also specify the addition. // Unspecified methods cannot be called by the client. // RpcServer::add(Abc::class,'add'); // Add in groups //RpcServer::group([ // // The middleware can do permission verification, data encryption and decryption, etc. // 'middle' => [ // TestMiddle::class . '@aa' // ], // // Cache If set, when called with the same parameters, the cache information will be returned and will not be called. Unit: seconds // 'cache' => 10 //], function () { // RpcServer::add(Abc::class); // RpcServer::add(User::class); //});
客户端调用
为了方便调用,我们创建了一个映射类(一个框架可以自动生成)
class ClientAbc extends RpcClientHttp { // rpc server address protected $_rpc_server = 'http://127.0.0.1:8082/'; // The remote class is not set and the default is the current class name protected $_remote_class_name = 'Abc'; }
调用 rpc 服务的远程方法与调用此项目的相同。你可以想象这个方法就在你的项目中。
$abc = new ClientAbc(5); // $res === 10 $res = $abc->add(2,3); // Chain call $res === 105 $res = $abc->setA(100)->add(2,3); // If the User of the above model is added to rpc // RpcServer::add(User::class); // The following operation results are the same as above // $user_list = User::whereIn('id',[1,2,3])->with('articles')->findAll()->toArray();
上述调用是通过 http 协议进行的。你也可以通过其他协议进行调用。例如,Tcp 协议
class ClientAbc extends RpcClientTcp { // rpc server address protected $_rpc_server = 'tcp://127.0.0.1:8083/'; // The remote class is not set and the default is the current class name protected $_remote_class_name = 'Abc'; }
框架中存在 RpcClientHttp
和 RpcClientTcp
类。你也可以将其复制到任何其他地方使用。
更多
文档
待办事项
- 支持 Workerman
- ORM 支持 postgresql