Как создать свой собственный PHP-фреймворк
Почему мы собираемся создать собственный PHP-фреймворк? Наверное, подавляющее большинство людей скажет «на рынке уже столько фреймворков, какие там колеса строить?». Моя точка зрения заключается в том, что «не цель сделать колесо, а цель — получить знания в процессе изготовления колеса».
Итак, как вы можете создать свой собственный PHP-фреймворк? Общий процесс выглядит следующим образом:
入口文件 ----> 注册自加载函数
----> 注册错误(和异常)处理函数
----> 加载配置文件
----> 请求
----> 路由
---->(控制器 <----> 数据模型)
----> 响应
----> json
----> 视图渲染数据
Помимо этого нам также понадобится юнит-тестирование, поддержка nosql, поддержка документации по интерфейсу, некоторые вспомогательные скрипты и т.д. В итоге каталог моего фреймворка выглядит так:
Список каталогов фреймворков
app [PHP应用目录]
├── demo [模块目录]
│ ├── controllers [控制器目录]
│ │ └── Index.php [默认控制器文件,输出json数据]
│ ├── logics [逻辑层,主要写业务逻辑的地方]
│ │ ├── exceptions [异常目录]
│ │ ├── gateway [一个逻辑层实现的gateway演示]
│ │ ├── tools [工具类目录]
│ │ └── UserDefinedCase.php [注册框架加载到路由前的处理用例]
│ └── models [数据模型目录]
│ └── TestTable.php [演示模型文件,定义一一对应的数据模型]
├── config [配置目录]
│ ├── demo [模块配置目录]
│ │ ├── config.php [模块自定义配置]
│ │ └── route.php [模块自定义路由]
│ ├── common.php [公共配置]
│ ├── database.php [数据库配置]
│ ├── swoole.php [swoole配置]
│ └── nosql.php [nosql配置]
docs [接口文档目录]
├── apib [Api Blueprint]
│ └── demo.apib [接口文档示例文件]
├── swagger [swagger]
framework [Easy PHP核心框架目录]
├── exceptions [异常目录]
│ ├── CoreHttpException.php[核心http异常]
├── handles [框架运行时挂载处理机制类目录]
│ ├── Handle.php [处理机制接口]
│ ├── EnvHandle.php [环境变量处理机制类]
│ ├── ErrorHandle.php [错误处理机制类]
│ ├── ExceptionHandle.php [未捕获异常处理机制类]
│ ├── ConfigHandle.php [配置文件处理机制类]
│ ├── NosqlHandle.php [nosql处理机制类]
│ ├── LogHandle.php [log机制类]
│ ├── UserDefinedHandle.php[用户自定义处理机制类]
│ ├── RouterSwooleHan... [swoole模式路由处理机制类]
│ └── RouterHandle.php [路由处理机制类]
├── orm [对象关系模型]
│ ├── Interpreter.php [sql解析器]
│ ├── DB.php [数据库操作类]
│ ├── Model.php [数据模型基类]
│ └── db [数据库类目录]
│ └── Mysql.php [mysql实体类]
├── router [路由策略]
│ ├── RouterInterface.php [路由策略接口]
│ ├── General.php [普通路由]
│ ├── Pathinfo.php [pathinfo路由]
│ ├── Userdefined.php [自定义路由]
│ ├── Micromonomer.php [微单体路由]
│ ├── Job.php [脚本任务路由]
│ ├── EasySwooleRouter.php [swoole模式路由策略入口类]
│ └── EasyRouter.php [路由策略入口类]
├── nosql [nosql类目录]
│ ├── Memcahed.php [Memcahed类文件]
│ ├── MongoDB.php [MongoDB类文件]
│ └── Redis.php [Redis类文件]
├── App.php [框架类]
├── Container.php [服务容器]
├── Helper.php [框架助手类]
├── Load.php [自加载类]
├── Request.php [请求类]
├── Response.php [响应类]
├── run.php [框架应用启用脚本]
├── swoole.php [swoole模式框架应用启用脚本]
frontend [前端源码和资源目录]
├── src [资源目录]
│ ├── components [vue组件目录]
│ ├── views [vue视图目录]
│ ├── images [图片]
│ ├── ...
├── app.js [根js]
├── app.vue [根组件]
├── index.template.html [前端入口文件模板]
├── store.js [vuex store文件]
jobs [脚本目录,写业务脚本的地方]
├── demo [模块目录]
│ ├── Demo.php [脚本演示文件]
│ ├── ...
public [公共资源目录,暴露到万维网]
├── dist [前端build之后的资源目录,build生成的目录,不是发布分支忽略该目录]
│ └── ...
├── index.html [前端入口文件,build生成的文件,不是发布分支忽略该文件]
├── index.php [后端入口文件]
├── server.php [swoole模式后端入口文件]
runtime [临时目录]
├── logs [日志目录]
├── build [php打包生成phar文件目录]
tests [单元测试目录]
├── demo [模块名称]
│ └── DemoTest.php [测试演示]
├── TestCase.php [测试用例]
vendor [composer目录]
.git-hooks [git钩子目录]
├── pre-commit [git pre-commit预commit钩子示例文件]
├── commit-msg [git commit-msg示例文件]
.babelrc [babel配置文件]
.env.example [环境变量示例文件]
.gitignore [git忽略文件配置]
.travis.yml [持续集成工具travis-ci配置文件]
build [php打包脚本]
cli [框架cli模式运行脚本]
LICENSE [lincese文件]
logo.png [框架logo图片]
composer.json [composer配置文件]
composer.lock [composer lock文件]
package.json [前端依赖配置文件]
phpunit.xml [phpunit配置文件]
README-CN.md [中文版readme文件]
README.md [readme文件]
run [快速开始脚本]
webpack.config.js [webpack配置文件]
yarn.lock [yarn lock文件]
жизненный цикл
Описание модуля фреймворка:
входной файл
Определите единый файл ввода, чтобы обеспечить единый файл доступа к внешнему миру. Внутренняя сложность скрыта снаружи, аналогично идее автобусной шины предприятия.
// 载入框架运行文件
require('../framework/run.php');
Модули самозагружения
Используйте функцию spl_autoload_register для регистрации функции самозагрузки в очереди __autoload и используйте пространство имен для автоматической загрузки (требования) файлов классов при использовании класса. После того, как регистрация завершит самозагружающуюся логику, мы можем использовать пространство имен и сотрудничать с ним, чтобы объявить зависимость от файла класса.
Модуль ошибок и исключений
Во время выполнения скрипта:
- Ошибка:
SET_ERROR_HANDLER не может обрабатывать следующие ошибки уровня, e_ERROR, E_PARSE, E_CORE_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_Compile_E_Compile_Warning, и большую часть E_STRICT, сгенерированных в файле, где находится функция set_error_handler(). Таким образом, нам нужно использовать функцию register_shutdown_function для соответствия error_get_last, чтобы получить последнюю ошибку при завершении скрипта, цель состоит в том, чтобы настроить обработку для различных уровней ошибок и фатальных ошибок, таких как возврат к сообщению об ошибке дружественной подсказки.
[file: framework/hanles/ErrorHandle.php]
- аномальный:
Зарегистрируйте метод обработки неперехваченных исключений с помощью функции set_exception_handler, чтобы перехватывать неперехваченные исключения, такие как возврат дружественных подсказок и информации об исключении.
[file: framework/hanles/ExceptionHandle.php]
Модуль профиля
Загружает пользовательские и пользовательские файлы конфигурации.
Например, пример параметра файла .env конфигурации master-slave базы данных:
[database]
dbtype = mysqldb
dbprefix = easy
dbname = easyphp
dbhost = localhost
username = easyphp
password = easyphp
slave = 0,1
[database-slave-0]
dbname = easyphp
dbhost = localhost
username = easyphp
password = easyphp
[database-slave-1]
dbname = easyphp
dbhost = localhost
username = easyphp
password = easyphp
[file: framework/hanles/ConfigHandle.php]
ввод и вывод
- Определить объект запроса: содержит всю информацию о запросе
- Определенные объекты ответа: объявленная информация о реагировании
Фреймворк выводит все исключения и вывод контроллера в формате json, потому что я думаю, что сегодня он полностью отделен от передней и задней части, это очень удобно, нам в настоящее время не нужно переходить к рассмотрению чего-то еще.
Проверка параметра запроса, в настоящее время обеспечивает обязательную проверку длины, типа номера, используйте следующие
$request = App::$container->get('request');
$request->check('username', 'require');
$request->check('password', 'length', 12);
$request->check('code', 'number');
[file: framework/Response.php]
модуль маршрутизации
├── router [路由策略]
│ ├── RouterInterface.php [路由策略接口]
│ ├── General.php [普通路由]
│ ├── Pathinfo.php [pathinfo路由]
│ ├── Userdefined.php [自定义路由]
│ ├── Micromonomer.php [微单体路由]
│ ├── Job.php [脚本任务路由]
│ └── EasyRouter.php [路由策略入口类]
Через информацию об URL-адресе, к которой обращается пользователь, метод-член целевого класса контроллера выполняется с помощью правил маршрутизации. Здесь я грубо делю маршрутизацию на четыре категории:
традиционная маршрутизация
domain/index.php?module=Demo&contoller=Index&action=test&username=test
путьинформация о маршруте
domain/demo/index/modelExample
Пользовательская маршрутизация
// 定义在config/moduleName/route.php文件中,这个的this指向RouterHandle实例
$this->get('v1/user/info', function (Framework\App $app) {
return 'Hello Get Router';
});
Микромонолитная маршрутизация
Я подробно опишу так называемую микромонолитную маршрутизацию здесь.Сегодня, когда популярны SOA и микросервисная архитектура, многие команды переходят на сервис-ориентированный процесс, но сложность многих проблем в сервис-ориентированном процессе экспоненциальна. , Рост, например распределенные транзакции, развертывание сервисов, межсервисное отслеживание проблем и т. д. Это затрудняет для небольшой команды переход от монолитной архитектуры к сервисной архитектуре, поэтому кто-то предложил микромонолитную архитектуру.По моему мнению, в процессе SOA монолитной архитектуры мы помещаем каждый сервис в микросервис. Или поместите его в тот же блок модульным способом, например:
app
├── UserService [用户服务模块]
├── ContentService [内容服务模块]
├── OrderService [订单服务模块]
├── CartService [购物车服务模块]
├── PayService [支付服务模块]
├── GoodsService [商品服务模块]
└── CustomService [客服服务模块]
Как и выше, мы просто построили каждый сервисный модуль в единое целое, но как эти модули обмениваются данными? следующее:
App::$app->get('demo/index/hello', [
'user' => 'TIGERB'
]);
С помощью описанного выше метода мы можем обмениваться данными и зависеть от каждого модуля в рамках одного блока слабосвязанным образом. При этом развитие бизнеса сложно прогнозировать.Когда мы в будущем мигрируем на архитектуру SOA, это очень просто.Нам нужно только разделить предыдущие модули на различные проекты, а затем конвертировать реализацию Достаточно метода получения экземпляра приложения в RPC или стратегии REST, мы можем настроить соответствующую стратегию через конфигурационный файл или зарегистрировать собственные и сторонние реализации.
[file: framework/hanles/RouterHandle.php]
Традиционный шаблон MVC защищает шаблон MCL.
Традиционный шаблон MVC включает уровень модель-представление-контроллер.Большую часть времени мы будем писать бизнес-логику на уровне контроллера или уровне модели, но постепенно мы обнаружим, что код трудно читать, поддерживать и расширять, поэтому Я форсирую здесь Слой логики. Что касается того, как писать код в слое логики, это полностью зависит от вас, вы можете реализовать в нем класс инструмента, вы также можете создать в нем новую подпапку и построить в ней свой код бизнес-логики, вы даже можете реализовать Шлюз на основе паттерна Link of Responsibilities (приведу конкретные примеры). Таким образом, наша окончательная структура выглядит так:
- M: модели, обязанности включают только операции, связанные с моделью данных.
- C: контроллеры, отвечающие за предоставление ресурсов внешнему миру.В архитектуре с разделением внешнего и внутреннего интерфейса контроллеры фактически эквивалентны представлениям в формате json.
- L: логика, где ответственность гибкая для реализации всей бизнес-логики
логический слой логики
Пример логического уровня, реализующего шлюз:
Мы добавили каталог шлюза в каталог уровня логики, после чего мы можем гибко записывать логику в этот каталог. Структура шлюза следующая:
gateway [Logics层目录下gateway逻辑目录]
├── Check.php [接口]
├── CheckAppkey.php [检验app key]
├── CheckArguments.php [校验必传参数]
├── CheckAuthority.php [校验访问权限]
├── CheckFrequent.php [校验访问频率]
├── CheckRouter.php [网关路由]
├── CheckSign.php [校验签名]
└── Entrance.php [网关入口文件]
Класс входа шлюза в основном отвечает за инициализацию шлюза.Код выглядит следующим образом:
// 初始化一个:必传参数校验的check
$checkArguments = new CheckArguments();
// 初始化一个:app key check
$checkAppkey = new CheckAppkey();
// 初始化一个:访问频次校验的check
$checkFrequent = new CheckFrequent();
// 初始化一个:签名校验的check
$checkSign = new CheckSign();
// 初始化一个:访问权限校验的check
$checkAuthority = new CheckAuthority();
// 初始化一个:网关路由规则
$checkRouter = new CheckRouter();
// 构成对象链
$checkArguments->setNext($checkAppkey)
->setNext($checkFrequent)
->setNext($checkSign)
->setNext($checkAuthority)
->setNext($checkRouter);
// 启动网关
$checkArguments->start(
APP::$container->get('request')
);
После реализации этого шлюза, как мы будем использовать его во фреймворке? В каталоге логического уровня я предоставляю определяемый пользователем класс сущности. Мы регистрируем класс входа шлюза в класс UserDefinedCase. Пример выглядит следующим образом:
/**
* 注册用户自定义执行的类
*
* @var array
*/
private $map = [
// 演示 加载自定义网关
'App\Demo\Logics\Gateway\Entrance'
];
Так что этот шлюз может работать. Давайте поговорим об этом классе UserDefinedCase.UserDefinedCase будет выполняться до того, как фреймворк будет загружен в механизм маршрутизации, чтобы мы могли гибко реализовать некоторую пользовательскую обработку. Этот шлюз - просто демонстрация, вы можете организовать свою логику так, как хотите~
Куда пропал Вид? Из-за выбора полного разделения интерфейса и сервера и SPA (одностраничное приложение) традиционный уровень просмотра также был удален. См. Подробное введение ниже.
Используйте Vue в качестве представления
Исходный каталог
Общая тенденция полного разделения front-end и back-end, двусторонней привязки данных, модуляризации и т. д. Здесь я помещаю свою собственную структуру внешнего интерфейса vue с открытым исходным кодом.easy-vueПортирован к этому проекту как просмотр слоя. Мы помещаем исходные файлы переднего конца в каталоге Frontend. Детали следующие. Вы также можете определить свои:
frontend [前端源码和资源目录,这里存放我们整个前端的源码文件]
├── src [资源目录]
│ ├── components [编写我们的前端组件]
│ ├── views [组装我们的视图]
│ ├── images [图片]
│ ├── ...
├── app.js [根js]
├── app.vue [根组件]
├── index.template.html [前端入口文件模板]
└── store.js [状态管理,这里只是个演示,你可以很灵活的编写文件和目录]
шаг сборки
yarn install
DOMAIN=http://你的域名 npm run dev
после компиляции
После успешной сборки каталог dist и файл index.html будут сгенерированы в общем каталоге. Файл .gitignore ветки без релиза будет игнорировать эти файлы, а ветку релиза можно игнорировать.
public [公共资源目录,暴露到万维网]
├── dist [前端build之后的资源目录,build生成的目录,不是发布分支忽略该目录]
│ └── ...
├── index.html [前端入口文件,build生成的文件,不是发布分支忽略该文件]
Реляционное сопоставление объектов базы данных
Что такое реляционное сопоставление объектов базы данных ORM (карта отношений объектов)? Согласно моему нынешнему пониманию: как следует из названия, это установление отношений между объектами и абстрактными вещами.В моделировании базы данных класс сущности модели на самом деле является конкретной таблицей, а работа с таблицей на самом деле является операцией модели. пример. Наверное, подавляющее большинство людей спросит: "Зачем вам это делать, разве не плохо оперировать прямыми sql-операторами? Это так хлопотно!" Мой ответ: прямые sql-операторы конечно возможны, все гибко, но из проектаМногоразовый, ремонтопригодный, расширяемыйВылет, используя идею ORM для обработки операций с данными.
Конкретные реализации ORM на рынке включают Active Record из серии фреймворков thinkphp, Active Record из серии фреймворков yii и Eloquent из серии фреймворков laravel (он считается самым элегантным). Затем смоделируйте ORM, прежде всего, БД клиентского объекта ORM: инициализируйте различные стратегии БД через файл конфигурации и инкапсулируйте все поведения при работе с базой данных, и, наконец, мы можем напрямую управлять базой данных через объект БД, стратегию БД. вот в настоящее время я только mysql (отвечает за установление соединений и базовых операций db) реализован. Затем мы разделяем функцию синтаксического анализа SQL объекта БД на черту многократно используемого синтаксического анализатора SQL, Конкретная функция состоит в том, чтобы анализировать цепочку операций объекта в определенном операторе SQL. Наконец, установите модель базового класса нашей модели, модель может напрямую наследовать БД. Окончательная структура выглядит следующим образом:
├── orm [对象关系模型]
│ ├── Interpreter.php [sql解析器]
│ ├── DB.php [数据库操作类]
│ ├── Model.php [数据模型基类]
│ └── db [数据库类目录]
│ └── Mysql.php [mysql实体类]
Класс БД использует примеры
/**
* DB操作示例
*
* findAll
*
* @return void
*/
public function dbFindAllDemo()
{
$where = [
'id' => ['>=', 2],
];
$instance = DB::table('user');
$res = $instance->where($where)
->orderBy('id asc')
->limit(5)
->findAll(['id','create_at']);
$sql = $instance->sql;
return $res;
}
Класс модели использует примеры
// controller 代码
/**
* model example
*
* @return mixed
*/
public function modelExample()
{
try {
DB::beginTransaction();
$testTableModel = new TestTable();
// find one data
$testTableModel->modelFindOneDemo();
// find all data
$testTableModel->modelFindAllDemo();
// save data
$testTableModel->modelSaveDemo();
// delete data
$testTableModel->modelDeleteDemo();
// update data
$testTableModel->modelUpdateDemo([
'nickname' => 'easy-php'
]);
// count data
$testTableModel->modelCountDemo();
DB::commit();
return 'success';
} catch (Exception $e) {
DB::rollBack();
return 'fail';
}
}
//TestTable model
/**
* Model操作示例
*
* findAll
*
* @return void
*/
public function modelFindAllDemo()
{
$where = [
'id' => ['>=', 2],
];
$res = $this->where($where)
->orderBy('id asc')
->limit(5)
->findAll(['id','create_at']);
$sql = $this->sql;
return $res;
}
Модуль сервисного контейнера
Что такое сервисный контейнер?
Контейнер службы звучит очень плавно. Насколько я понимаю, это просто предоставление стороннего объекта. Мы внедряем класс или экземпляр, который необходимо использовать бизнес-логике, в этот сторонний класс объекта. Когда нам нужно получить Экземпляр класса, который мы получили напрямую через этот сторонний класс сущностей.
Услуги значение контейнера?
С точки зрения шаблонов проектирования: на самом деле, независимо от шаблонов проектирования или фактического опыта программирования, мы все подчеркиваем «высокую связность и слабую связанность». Результатом нашей высокой согласованности является то, что роль каждой сущности чрезвычайно специфична. классы сущностей с различными функциями. При организации логической функции эти уточненные сущности будут иметь разную степень зависимостей, для этих зависимостей наш обычный подход таков:
class Demo
{
public function __construct()
{
// 类demo直接依赖RelyClassName
$instance = new RelyClassName();
}
}
Логической проблемы с таким способом написания нет, но он не соответствует «принципу наименьшего знания» шаблона проектирования, потому что между ними существует прямая зависимость, а вся структура кода недостаточно гибкая и жестко связанная. . Поэтому мы предоставляем сторонний объект для изменения прямой зависимости на зависимость от третьего лица.Мы получаем зависимый экземпляр напрямую через третье лицо для достижения цели слабой связи.Роль третьего лица здесь аналогична системе «Промежуточное программное обеспечение» в архитектуре — это все роли координации зависимостей и разъединения. Наконец, третья сторона здесь — так называемый сервис-контейнер.
После реализации контейнера службы я внедрил Request, Config и другие экземпляры в контейнер службы как синглтон, и очень удобно получать его из контейнера, когда нам нужно его использовать. Используйте следующим образом:
// 注入单例
App::$container->setSingle('别名,方便获取', '对象/闭包/类名');
// 例,注入Request实例
App::$container->setSingle('request', function () {
// 匿名函数懒加载
return new Request();
});
// 获取Request对象
App::$container->get('request');
Nosql-модуль
Обеспечьте поддержку nosql, предоставьте глобальный одноэлементный объект, и с помощью нашего сервисного контейнера, когда запускается фреймворк, мы внедряем требуемый экземпляр nosql в сервисный контейнер через конфигурацию файла конфигурации. В настоящее время мы поддерживаем redis/memcahed/mongodb.
как пользоваться? следующее,
// 获取redis对象
App::$container->getSingle('redis');
// 获取memcahed对象
App::$container->getSingle('memcahed');
// 获取mongodb对象
App::$container->getSingle('mongodb');
Swoole режим
Поддержка работы под расширением swoole
cd public && php server.php
Режим работы
Мы можем написать наш сценарий задачи прямо в каталоге заданий следующим образом.
jobs [脚本目录,写业务脚本的地方]
├── demo [模块目录]
│ ├── Demo.php [脚本演示文件]
│ ├── ...
Пример сценария задачи:
<?php
namespace Jobs\Demo;
/**
* Demo Jobs
*
* @author TIERGB <https://github.com/TIGERB>
*/
class Demo
{
/**
* job
*
* @example php cli --jobs=demo.demo.test
*/
public function test()
{
echo 'Hello Easy PHP Jobs';
}
}
Наконец, просто запустите следующую команду напрямую:
php cli --job=demo.demo.test
Генерация документации интерфейса и модуль имитации интерфейса
Обычно после того, как мы пишем интерфейс, интерфейсный документ является проблемой. Мы используем протокол BluePrint API для завершения записи и моделью интерфейсного документа, и мы используем чванство для реализации доступа в реальном времени к интерфейсу с помощью интерфейсного документа ( в настоящее время не реализован.
Инструмент, выбранный для протокола описания интерфейса Api Blueprint, — сноуборд, Конкретные инструкции заключаются в следующем:
Инструкции по генерации документа интерфейса
cd docs/apib
./snowboard html -i demo.apib -o demo.html -s
open the website, http://localhost:8088/
Инструкции по использованию имитации интерфейса
cd docs/apib
./snowboard mock -i demo.apib
open the website, http://localhost:8087/demo/index/hello
Модуль модульного тестирования
Это хорошая привычка для записи модульных тестов на основе phpunit.
как пользоваться?
Подготовка следующего тестового файла каталога тестов с конкретной ссылкой на файлы DemoTest в каталоге тестов/демо, затем запустите:
vendor/bin/phpunit
Пример тестового утверждения:
/**
* 演示测试
*/
public function testDemo()
{
$this->assertEquals(
'Hello Easy PHP',
// 执行demo模块index控制器hello操作,断言结果是不是等于'Hello Easy PHP' 
App::$app->get('demo/index/hello')
);
}
Справочник по синтаксису документации по утверждению phpunit
Конфигурация git-хука
Целью нормализовать наш код проекта и зафиксировать записи.
- Спецификация кода: используйте php_codesniffer вместе, чтобы применить формат кода перед его отправкой.
- спецификация commit-msg: Спецификация ruanyifeng сообщения фиксации, для проверки формата сообщения фиксации, улучшенной читабельности и простоты устранения неполадок журнала post-git и статистического журнала и т. д., используемая здесьTreriСкрипт commit-msg, спасибо~.
вспомогательный скрипт
Кли-скрипт
Запустите фреймворк в командной строке, смотрите инструкцию по использованию.
скрипт сборки
Чтобы упаковать скрипт проекта PHP, упакуйте весь проект в каталог runtime/build, например:
runtime/build/App.20170505085503.phar
<?php
// 入口文件引入包文件即可
require('runtime/build/App.20170505085503.phar');
Command:
php cli --build
как пользоваться?
воплощать в жизнь:
composer create-project tigerb/easy-php easy --prefer-dist && cd easy
Режим обслуживания сайта:
Быстро начать демонстрацию:
php cli --run
Демонстрация выглядит следующим образом:
Режим клиентского сценария:
php cli --method=<module.controller.action> --<arguments>=<value> ...
例如, php cli --method=demo.index.get --username=easy-php
Режим качания:
cd public && php server.php
Получить помощь:
Используйте команду php cli или php cli --help
производительность-fpm
ab -c 100 -n 10000 "http://easy-php.local/Demo/Index/hello"
Document Path: /
Document Length: 53 bytes
Concurrency Level: 100
Time taken for tests: 3.259 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 1970000 bytes
HTML transferred: 530000 bytes
Requests per second: 3068.87 [#/sec] (mean)
Time per request: 32.585 [ms] (mean)
Time per request: 0.326 [ms] (mean, across all concurrent requests)
Transfer rate: 590.40 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 4
Processing: 6 32 4.0 31 68
Waiting: 6 32 4.0 31 68
Total: 8 32 4.0 31 68
Percentage of the requests served within a certain time (ms)
50% 31
66% 32
75% 33
80% 34
90% 39
95% 41
98% 43
99% 46
100% 68 (longest request)
Производительность - Swoole
ab -c 100 -n 10000 "http://easy-php.local/Demo/Index/hello"
Concurrency Level: 100
Time taken for tests: 1.319 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 1870000 bytes
HTML transferred: 160000 bytes
Requests per second: 7580.84 [#/sec] (mean)
Time per request: 13.191 [ms] (mean)
Time per request: 0.132 [ms] (mean, across all concurrent requests)
Transfer rate: 1384.39 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 5 10.6 3 172
Processing: 1 9 13.4 7 177
Waiting: 0 7 11.7 6 173
Total: 3 13 16.9 11 179
Percentage of the requests served within a certain time (ms)
50% 11
66% 12
75% 13
80% 14
90% 15
95% 17
98% 28
99% 39
100% 179 (longest request)
Вопросы и предложения
Есть еще много недостатков, если вы обнаружите какие-либо проблемы, вы можете сказать мнеissueили пиар.
Или, если вы думаете, что хотите узнать подробности реализации в этой структуре, вы также можете дать мнеissue, я подытожу его в соответствующей статье, чтобы поделиться с вами позже.
Как внести свой вклад?
cp ./.git-hooks/* ./git/hooks
Затем вы можете инициировать PR в обычном режиме, а я выполню проверку формата кода (psr) и проверку commit-msg для всех коммитов.Если возникает ошибка, пожалуйста, исправьте ее в соответствии с подсказками.
адрес проекта:GitHub.com/тигр B/легко…