PHP 的开拓者可能是所有措辞里被惯坏的最厉害的,由于险些每个框架都供应了脚手架,像这样:
composer create-project
这个在 npm、go mod 是没有这个功能的,须要自己创建程序骨架,当然 npm 和 go 生态产生了自己的办理方案,便是 vue-cli 和 mixcli 这样的脚手架工具来卖力创建。
和 npm init 、go mod init 一样,我们利用 composer init 创建一个项目

mkdir hellocd hellocomposer init
交互式填写一些内容后,天生了 composer.json 文件
{ "name": "liujian/hello", "type": "project", "autoload": { "psr-4": { "Liujian\\Hello\\": "src/" } }, "require": {}}
这个文件因此 composer 库的标准创建的,必须要两级名称,这让我很蛋疼,以是我修正一下
{ "name": "project/app", "type": "project", "autoload": { "psr-4": { "App\\": "src/" } }, "require": {}}
选择我须要利用的库
和 node.js、go 生态一样,第二步便是探求我们须要的库,常日我们的需求是写一个 API 做事,就须要一个 http server 库,一个 db 库就可以开始事情了。
由于是当代化的 PHP 开拓,因此我选择了 PHP CLI 模式的常驻高性能库,这里我选择的是:
mix/vega Vega 是一个用 PHP 编写的 CLI 模式 HTTP 网络框架,支持 Swoole、WorkerManmix/database 可在各种环境中利用的轻量数据库,支持 FPM、Swoole、WorkerMan,可选的连接池 (协程)这两个都是 MixPHP V3+ 的核心组件。
Mix Vega & Mix Database 安装Vega 同时支持 Swoole、WorkerMan,往后还会支持 Swow,最大略原则,由于 WorkerMan 可以不须要安装扩展即可实行,开拓先采取 WorkerMan 来驱动 Vega,上线可根据自己的须要切换。
安装 Workerman
composerrequireworkerman/workerman
安装 Mix Vega
composerrequiremix/vega
安装 Mix Database
composerrequiremix/database
创建一个入口文件
vue 的入口常日是 src/main.js 由于 js 常日是单入口项目,我们还是按二进制的老例,创建一个 bin/start.php 入口文件
<?phprequire __DIR__ . '/../vendor/autoload.php';$vega = new Mix\Vega\Engine();$vega->handleF('/hello', function (Mix\Vega\Context $ctx) { $ctx->string(200, 'hello, world!');})->methods('GET');$http_worker = new Workerman\Worker("http://0.0.0.0:2345");$http_worker->onMessage = $vega->handler();$http_worker->count = 4;Workerman\Worker::runAll();
然后我们模拟 npm 的搞法,在 composer.json 增加:
"scripts": { "server": "php bin/start.php start"},
这里我非常困惑 composer 的搞法,npm 的入口文件中可不须要 require __DIR__ . '/../vendor/autoload.php'; 直接 npm run server 实行的脚本是自己可以找到对应依赖的,但是 composer 即便利用 composer run server 实行对应的脚本,依然要在代码里处理 autoload,给差评。
现在我们 composer run server 启动做事试试:
% composer run server> php8 bin/start.php startWorkerman[bin/start.php] start in DEBUG mode----------------------------------------- WORKERMAN -----------------------------------------Workerman version:4.0.19 PHP version:8.0.7------------------------------------------ WORKERS ------------------------------------------proto user worker listen processes status tcp liujian none http://0.0.0.0:2345 4 [OK] ---------------------------------------------------------------------------------------------Press Ctrl+C to stop. Start success.
写一个 API 接口
我们将上面的入口文件改造一下,写一个用户查询接口,Vega 的利用非常大略。
<?phprequire __DIR__ . '/../vendor/autoload.php';const DSN = 'mysql:host=127.0.0.1;port=3306;charset=utf8;dbname=test';const USERNAME = 'root';const PASSWORD = '123456';$db = new \Mix\Database\Database(DSN, USERNAME, PASSWORD);$vega = new Mix\Vega\Engine();$vega->handleF('/users/{id}', function (Mix\Vega\Context $ctx) use ($db) { $row = $db->table('users')->where('id = ?', $ctx->param('id'))->first(); if (!$row) { throw new \Exception('User not found'); } $ctx->JSON(200, [ 'code' => 0, 'message' => 'ok', 'data' => $row ]);})->methods('GET');$http_worker = new Workerman\Worker("http://0.0.0.0:2345");$http_worker->onMessage = $vega->handler();$http_worker->count = 4;Workerman\Worker::runAll();
curl 测试一下:
% curl http://127.0.0.1:2345/users/1{"code":0,"message":"ok","data":{"id":"1","name":"foo2","balance":"102","add_time":"2021-07-06 08:40:20"}}
利用 PSR 调度一下目录构造
前面我们定义了 PSR
"autoload": { "psr-4": { "App\\": "src/" }},
接下来我们采取自动加载来合理拆分上面入口文件的代码,拆分后目录构造如下:
├── bin│ └── start.php├── composer.json├── composer.lock├── src│ ├── Controller│ │ └── Users.php│ ├── Database│ │ └── DB.php│ └── Router│ └── Vega.php└── vendor
bin/start.php
<?phprequire __DIR__ . '/../vendor/autoload.php';$vega = \App\Router\Vega::new();$http_worker = new Workerman\Worker("http://0.0.0.0:2345");$http_worker->onMessage = $vega->handler();$http_worker->count = 8;Workerman\Worker::runAll();
src/Router/Vega.php
<?phpnamespace App\Router;use App\Controller\Users;use Mix\Vega\Engine;class Vega{ / @return Engine / public static function new() { $vega = new Engine(); $vega->handleC('/users/{id}', [new Users(), 'index'])->methods('GET'); return $vega; }}
src/Controller/Users.php
<?phpnamespace App\Controller;use App\Database\DB;use Mix\Vega\Context;class Users{ public function index(Context $ctx) { $row = DB::instance()->table('users')->where('id = ?', $ctx->param('id'))->first(); if (!$row) { throw new \Exception('User not found'); } $ctx->JSON(200, [ 'code' => 0, 'message' => 'ok', 'data' => $row ]); }}
src/Database/DB.php
<?phpnamespace App\Database;use Mix\Database\Database;const DSN = 'mysql:host=127.0.0.1;port=3306;charset=utf8;dbname=test';const USERNAME = 'root';const PASSWORD = '123456';class DB extends Database{ static private $instance; public static function instance() { if (!isset(self::$instance)) { self::$instance = new self(DSN, USERNAME, PASSWORD); } return self::$instance; }}
调度完基本就完成了一个正式项目的雏形了,接下来大家可以自由发挥。
压测一下mysql: docker mysql8 本机cpu: macOS M1 8核mem: 16Gwokerman (未安装libevent): 8进程,相称于8个mysql连接
% wrk -c 1000 -d 1m http://127.0.0.1:2345/users/1Running 1m test @ http://127.0.0.1:2345/users/1 2 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 36.08ms 8.11ms 428.09ms 95.38% Req/Sec 3.49k 211.80 4.00k 71.00% 416817 requests in 1.00m, 109.31MB read Socket errors: connect 749, read 295, write 1, timeout 0Requests/sec: 6943.38Transfer/sec: 1.82MB