害羞框架 Shy Framework
简洁却强大的高性能框架 Simple but powerful high performance framework
框架实现简洁、功能强大、覆盖全面,正如她的名字:Shy——纤细身形、不漏内秀。
The framework is simple, powerful, and comprehensive, just like her name: Shy - slender figure, not leaking show.
运行 Run
第一步:克隆或下载项目 Step one: Clone or Download
#默认master分支,包含最新特性,可能不稳定 git clone https://github.com/lynnclub/shy.git MyProjectName #获取指定的发行版本 git clone -b 1.4.0 https://github.com/lynnclub/shy.git MyProjectName
推荐使用稳定的 发行版本。
第二步:安装依赖包 Step two: Install dependencies
#进入项目目录 cd MyProjectName #执行composer依赖包安装 composer install
恭喜!!!接下来,只需要配置Web站点,或者启动常驻内存模式,就可以运行框架了。
第三步(可选):常驻内存模式运行 Step three(optional): Running in resident memory mode
php command http_workerman start
版本 Version
分为大中小三级版本号,1.x版本兼容php>=7.0,2.x版本兼容php>=7.4。
2.1.0
完善session,注释翻译为中文,其它优化。
1.4.0
完善session,注释翻译为中文,其它优化。
1、概述 Overview
框架内置了丰富的组件(Component),例如配置、日志、门面、缓存、流水线、进程管理等等。这些组件按照拼积木的方式灵活组合,形成了面向不同使用场景的 Web框架、CLI框架、Socket框架、API框架,并且可以多入口独立运行。
所有内置组件都抽象出了契约(Contract),拥有规范约束、可以自由替换。
容器(Container)作为其中最核心的组件,提供了操作便捷的实例(Instance)复用池。需要复用的实例,包括内置组件,都可以在容器中创建、获取、销毁或替换。
框架还提供了一种革新 PHP传统Web框架 的运行模式——常驻内存模式(PHP-CLI)。
在该模式下,由于避免了代码重复编译导致的性能开销,框架的Web服务性能得以大幅提升!!!
1.1 框架兼容
框架全力遵守 PHP-FIG组织 制定的PSR(PHP Standards Recommendations)系列规范,与遵守相同规范的框架互相兼容。
基础编码:遵守 PSR-1: Basic Coding Standard ; 日志:遵守 PSR-3: Logger Interface; 自动加载:遵守 PSR-4: Autoloader; 容器:遵守 PSR-11: Container interface ; 缓存:遵守 PSR-16: Common Interface for Caching Libraries。1.2 特性列表
启动器(Bootstrap) 内置常量(Constant) 契约(Contract) 容器与依赖注入(Container and Dependency Injection) 配置与环境(Config、SHY_ENV) 异常处理(Exception Handler) 日志(Logger) 流水线(Pipeline) 进程管理(Process) 钩子(Hook) 门面(Facade) 缓存(Cache) 数据库(DataBase) 请求(Request) 中间件(Middleware) 控制器(Controller) 响应(Response) 会话(Session) 路由(Router) 模版(View) 命令模式(Command Mode) 常驻内存模式(PHP-CLI Http Mode) Socket模式(Socket Mode) 单元测试(Unit Test)1.3 目录结构
shy 框架根目录
|
| phpunit.php 单元测试入口
| command 命令模式入口
| server.php Http服务调试入口(仅供调试使用,不建议将整个项目目录暴露在web服务器下)
| gulpfile.js Gulp前端构建服务
|
|___app 开发目录
| |
| |___Command 命令服务开发目录
| |___Function 函数开发目录
| |___Http Http服务开发目录
| |___Socket Socket服务开发目录
|
|___bootstrap 服务启动目录
| |
| |___command.php 命令服务启动程序
| |___http.php Http服务启动程序
| |___http_workerman.php 基于workerman的常驻内存Http服务启动程序
|
|___cache 缓存目录
| |
| |___app 系统缓存目录
| |___log 日志目录
|
|___config 配置目录
| |
| |___ develop 开发环境配置目录
| |___ testing 测试环境配置目录
| |___ production 生产环境配置目录
|
|___public Http服务开放目录
| |
| | index.php Http服务入口
| |
| |___upload 上传文件目录
| |___vendor 前端第三方资源包目录
|
|___shy 框架目录
| |
| | Command.php 命令服务
| | Config.php 配置组件
| | Container.php 容器组件
| | Facade.php 门面组件
| | Hook.php 钩子组件
| | Pipeline.php 流水线组件
| | Process.php 进程组件
| | HttpInWorkerMan.php 基于workerman的常驻内存Http服务
| | SocketInWorkerMan.php 基于workerman的Socket服务
| |
| |___Cache 缓存目录
| |___Command 命令模式目录
| |___Contract 契约目录
| |___Exception 异常类目录
| |___Http Web服务目录
| |___Library 类库目录
| |___Socket Socket服务目录
|
|___tests 单元测试目录
|
|___vendor composer依赖包安装目录
1.4 Http服务生命周期
启动器运行(Bootstrap) 启动自动加载(Composer) 读取并设置运行环境(SHY_ENV) 定义内置常量(Constant) 启动容器,注册组件(Container) 启动配置组件(Config) 设置时区(Timezone) 注册异常处理(ExceptionHandler) 会话初始化(Session) 引入模版函数文件(View) 引入自定义文件 装载请求(Request) 路由解析(Router) 执行中间件、控制器(Router、Middleware、Controller) 输出响应(Response) 请求组件恢复初始化状态(Request)1.5 远期规划
容器实例调度 单元测试覆盖率100% Swoole socket Api便捷开发框架2、契约 Contract
契约是比接口(interface)更广义的概念,意义相似,但是不局限于接口的形式。契约可以是接口、抽象类,甚至是非硬性约束的惯例。
各种组件都按照契约的规范实现,比如容器、缓存、配置、日志、门面等,部分组件的契约还兼容PSR规范。
契约在容器中的使用方式:在容器中注册契约类名,绑定实现了该契约的实体类。遵守同一契约的实体类,可以在容器中替换。
bootstrap程序中,契约类与实体类的绑定:
$container->binds([ ConfigContract::class => Config::class, LoggerContract::class => File::class, ExceptionHandlerContract::class => Handler::class, PipelineContract::class => Pipeline::class, CacheContract::class => Memory::class, DataBaseContract::class => Illuminate::class, ResponseContract::class => Response::class, SessionContract::class => Session::class, RouterContract::class => Router::class, ViewContract::class => View::class, ]);
3、容器与依赖注入 Container and Dependency injection
框架的容器类,遵守PSR(PHP Standards Recommendations)中的《PSR-11: Container interface》接口规范。并且,实现了PHP的ArrayAccess、Countable接口,可以当作数组使用。
容器是对实例集中管理的实例池,可以创建、绑定、使用、替换或者移除实例。
此外,框架拓展了容器的概念,支持把字符、数组等任意内容当作实例管理。即容器是实例与数据的集中管理池。
注意事项:
需要复用的、贯穿框架生命周期的实例或者数据,应该加入到容器; 框架的核心服务及配置,可以被自由访问,建议对框架设计有足够的了解再操作。3.1 绑定实例
3.1.1 用法模式
支持绑定实例或者数据,绑定内容为闭包,将在 加入与获取实例 时执行该闭包。
bind(类名, 类名、实例或匿名函数):类名为键,对应的值可以是类名、实例或匿名函数 bind(数据名, 数据):数据名为键,对应传入的数据 bind(类名):等于bind(类名, 类名) bind(数据名):等于bind(数据名, 数据名)3.1.2 代码示例
/** * 绑定类名 */ bind(ShyHttp::class); /** * 直接绑定实例 */ bind(ShyHttp::class, new ShyHttp()); bind('ShyHttp', new ShyHttp()); /** * 绑定匿名函数(可用于延迟传参、即执行闭包时再传参,支持任意个参数) */ bind(ShyHttp::class, function ($param1, $param2) { return new ShyHttp($param1, $param2); }); /** * 链式调用:绑定实例->加入容器然后获取实例->执行实例的run方法 * * getOrMake方法等同于shy函数 */ bind(ShyHttp::class, new ShyHttp())->getOrMake(ShyHttp::class)->run(); /** * 链式调用:绑定匿名函数->带参执行,加入容器然后获取实例->执行实例的run方法 */ bind(ShyHttp::class, function ($param1, $param2) { return new ShyHttp($param1, $param2); })->getOrMake(ShyHttp::class, $param1, $param2)->run();
如上所述,bind函数是对容器类的封装,会返回容器实例以便链式调用。
bind函数的功能只是绑定实例,并没有将实例加入到容器的实例池。将实例加入容器、获取容器中实例,是shy函数的功能。
3.2 加入容器与获取实例
3.2.1 用法模式
shy(类名, null, 任意个实例化参数):类名作为实体类 shy(类名, 实体类名, 任意个实例化参数):类名绑定实体类 shy(类名, 实例或匿名函数, 任意个实例化参数):类名绑定实例或者匿名函数3.2.2 代码示例
/** * 实例化Http类,并加入容器 */ shy(ShyHttp::class); /** * 带参数实例化(未绑定) */ shy(ShyHttp::class, null, $param1, $param2); /** * 先绑定,再带参数实例化 */ bind(ShyHttp::class); shy(ShyHttp::class, $param1, $param2); /** * 带参数实例化File类,契约为Logger */ shy(ShyContractLogger::class, ShyLoggerFile::class, $param1, $param2); // 获取契约为Logger的File实例(需要已绑定或已加入容器) shy(ShyContractLogger::class); /** * 直接将实例加入容器 */ shy(ShyHttp::class, new ShyHttp()); /** * 设置类名的别名 */ shy()->alias('config', ShyContractConfig::class); // 使用别名获取实例 shy('config');
shy函数是框架的核心函数,代表对容器的操作。
使用shy函数时,如果容器内已经存在指定类名的实例,则直接返回该实例。如果不存在,会将实例加入到容器中,然后返回被加入的实例。
该函数会自动尝试上述所有用法模式来获取实例。比如,从绑定的实例中获取、执行绑定的匿名函数获取实例、或者通过反射实例化类。
实例加入容器之后,会清除绑定以免占用内存。
3.3 更多操作
更多操作请直接使用容器类。通过使用shy函数、不传参数,可以获取到容器实例。
/** * 无参数、返回容器本身 */ shy(); /** * 是否存在实例 */ shy()->has(ShyHttp::class); /** * 创建实例、已存在实例会替换实例 */ shy()->make(ShyHttp::class); /** * 移除实例 */ shy()->remove(ShyHttp::class);
3.4 依赖注入
容器执行闭包、或者通过反射实例化类的时候,会自动注入构造方法的依赖,无需手动实例化。
use ShyHttpContractRequest; use ShyContractConfig; /** * Logger constructor. * * @param Request $request * @param Config $config */ public function __construct(Config $config, Request $request = null) { $this->config = $config; $this->request = $request; }
如上所述,Logger类的构造方法依赖Config和Request契约类参数。容器通过反射感知变量类型,可以自动注入契约绑定的实体类。
4、配置与环境 Config and Environment
配置类继承了内存缓存类ShyCacheMemory::class
,无依赖。在app.cache开启时,配置会被持久化缓存到本地文件。
/** * 读取配置文件app.php的配置 */ $appConfig = config('app'); /** * 读取配置文件app.php中的,cache配置 */ $isCache = config('app.cache'); /** * 读取配置文件workerman.php中的,socket配置 */ $socketConfig = config('workerman.socket');
环境配置
框架预设了三种环境,develop开发环境、testing测试环境、production生产环境。如果未设置环境值,默认为develop开发环境。
将优先使用环境目录下的配置文件,当环境目录下不存在该文件时,使用通用的配置文件。
Nginx fastcgi配置环境值:
fastcgi_param SHY_ENV 'testing';
Linux系统配置环境值:
// .bash_profile文件 export SHY_ENV=production
环境配置可以自由拓展,框架按照配置的环境值,读取config目录下的同名环境目录。
5、异常捕获 Exception Handler
框架在各个服务的入口处,注册了异常(Exception)与错误(Error)捕获,能够捕获并处理所有未被捕获的异常、错误,甚至是Shut Down。
错误与Shut Down会被转化成异常,统一按异常处理。
框架为每个服务提供了异常处理类(Handler)。你也可以实现Handler接口来自定义异常处理,在启动器的服务入口中替换绑定关系,使其生效。
替换异常处理契约绑定的实体类:
// MyProjectName/bootstrap/http.php文件 $container->bind(ShyContractExceptionHandler::class, ShyHttpExceptionHandler::class);
对于需要返回Http Code的错误,可以抛出HttpException。该错误的响应会输出errors/common.php
模版:
use ShyHttpExceptionHttpException; throw new HttpException(403, lang(5000));
6、门面 Facade
门面提供了便捷的静态调用方式。实现门面类需要继承框架的门面抽象类,并且重写父类的getInstance()
方法,以便向父类传递实体类。实现代码示例如下:
namespace ShyFacade; use ShyFacade; use ShyContractCache as CacheContract; class Cache extends Facade { /** * 获取实例 * Get the instance. * * @return object */ protected static function getInstance() { return shy(CacheContract::class); } }
门面类的父类——门面抽象类,通过魔术方法__callStatic()
调用实体类中的方法。可参考 容器与依赖注入 章节,以便理解框架如何获取实体类的实例。
7、缓存 Cache
框架的缓存类,遵守PSR(PHP Standards Recommendations)中的《PSR-16: Common Interface for Caching Libraries》接口规范。
并且,实现了PHP的ArrayAccess接口,可以当作数组使用。由于phpredis拓展不完全兼容PSR-16,所以框架对缓存的PSR规范无硬性约束、仅建议遵守。
框架默认使用无依赖的 Memory 内存缓存,通过文件做持久化储存,每次GC最多回收10条。在常驻内存模式下,由于该缓存只在关闭、启动服务的时候执行文件持久化,所以性能开销较小。
框架还提供了基于phpredis拓展实现的Redis缓存,推荐有条件时优先使用。可以在bootstrap目录下的服务启动文件中,替换缓存契约绑定的实体类:
$container->bind(ShyContractCache::class, ShyCacheRedis::class);
调用缓存门面的方法:
use ShyFacadeCache; Cache::set('test', 123); Cache::get('test');
8、日志 Logger
框架的日志类,遵守PSR(PHP Standards Recommendations)中的《PSR-3: Logger Interface》接口规范。
8.1 简介
框架实现了本地文件日志ShyLoggerFile
,以及阿里云日志ShyLoggerAliyun
(阿里云日志类继承了本地文件日志类,使用的时候也会保存本地文件日志)。
如果不需要记录日志,日志契约可以更换绑定PsrLogNullLogger
类。
可以在bootstrap目录下的服务启动文件中,替换日志契约绑定的实体类:
$container->bind(ShyContractLogger::class, ShyLoggerAliyun::class);
8.2 错误级别
emergency 紧急 alert 警报 critical 严重 error 错误 warning 警告 notice 注意 info 信息 debug 调试8.3 自定义日志
自定义日志需要实现ShyContractLogger
接口,并且继承PSR的PsrLogAbstractLogger
。
9、流水线 Pipeline
流水线是一种连贯的流程调度工具,使用流水线执行的对象或函数,可以享受容器的依赖注入服务。流水线连通了包括中间件、控制器在内的运行流程。
Pipeline类的方法:
send:设置传入参数,参数数量没有限制; through:设置流水线的处理对象,可以是一个或者多个; via:设置处理对象需要执行的方法,默认执行handle方法; then:流水线的执行方法,需要设置回调函数; run:流水线的执行方法,无需设置回调函数。开发者可使用流水线来执行自己的调度,代码实例如下:
use ShyContractPipeline; /** * 执行一组中间件,带回调执行 */ $response = shy(Pipeline::class) ->send(...$this->pathParam) ->through($this->middleware) ->then(function () { return $this->runController(); }); /** * 执行单个控制器,不带回调执行 */ $response = shy(Pipeline::class) ->through($this->controller) ->via($this->method) ->run();
10、请求 Request
通过门面类使用:
// 引入门面类 use ShyHttpFacadeRequest; // 是否初始化(常驻内存模式使用) Request::initialized(); // 获取全部请求 Request::all(); // 获取数据流 php://input Request::getContent(); // 获取GET请求参数 Request::get('key'); // 获取POST请求参数 Request::post('key');
通过契约的依赖注入使用:
// 引入契约类 use ShyHttpContractRequest; // 控制器通过依赖注入使用实体类 public function test(Request $request) { return 'controller echo test ' . json_encode($request->all()); }
11、中间件 Middleware
中间件是控制器请求与响应的中间步骤,是流水线(Pipeline)的一种特例。流水线传入的第一个参数$next
,是用于运行控制器的闭包。
11.1 前置中间件
中间件在控制器之前执行,称为“前置中间件”。如下,是有IP白名单功能的前置中间件:
namespace ShyHttpMiddleware; use Closure; use ShyContractMiddleware; use ShyHttpFacadeRequest; use ShyFacadeLogger; use ShyHttpExceptionHttpException; class IpWhitelist implements Middleware { /** * Handle * * @param Closure $next * @param array ...$passable * @return mixed|string */ public function handle(Closure $next, ...$passable) { $hit = FALSE; $whitelist = config('ip_whitelist'); if (is_array($whitelist)) { $userIps = Request::getClientIps(); foreach ($userIps as $userIp) { if (in_array($userIp, $whitelist)) { $hit = TRUE; } } } if (!$hit) { Logger::info('Ip whitelist block request', Request::all()); if (Request::expectsJson()) { return get_response_json(5000); } else { throw new HttpException(403, lang(5000)); } } return $next(); } }
11.2 后置中间件
use ShyContractMiddleware; use Closure; class Test implements Middleware { /** * Handle * * @param Closure $next * @param array ...$passable * @return mixed|string */ public function handle(Closure $next, ...$passable) { // run controller $response = $next(); // do something $response = json_encode($response); return $response; } }
11.3 使用中间件
中间件需要在配置文件middleware.php
中定义别名或者别名组,然后在路由中填写别名使用。
return [ 'IpWhitelist' => ShyHttpMiddlewareIpWhitelist::class, 'Throttle' => ShyHttpMiddlewareThrottle::class, 'Example' => AppHttpMiddlewareExample::class, 'GroupExample' => [ ShyHttpMiddlewareCSRF::class, ] ];
11.4 内置中间件
CSRF:防止跨站请求伪造,配合内置函数csrf_token()
使用;
GetOnly:仅限GET请求,其它方式响应404;
PostOnly:仅限POST请求,其它方式响应404;
IpWhitelist:IP白名单,通过配置文件ip_whitelist.php
管理白名单;
Throttle:限流阀,默认1分钟内限制单IP请求60次,可在路由中自定义设置。例如1分钟内限制10次、5分钟解禁:Throttle:10,5
。
12、响应 Response
对于控制器,只需要return数据或者模版,Response组件会自动输出响应。建议不要手动输出,而是交给框架去处理响应。
/** * 返回字符串 */ return 'controller echo'; /** * 返回内置模版 */ return view('home', compact('title', 'info'))->layout('main'); /** * 返回Smarty模版 */ return smarty('smarty.tpl', $params);
13、路由 Router
路由通过请求(Request)组件获取到请求路径,然后解析出对应控制器及其方法,最终调度执行中间件与控制器。
路由支持"路径模式"和"配置模式",可以在配置文件app.php
中关闭或启用。路径模式是直接把请求路径当成控制器及其方法,比较简单;配置模式根据请求路径查找路由配置,得到控制器及其方法,支持中间件、路径前缀、命令空间、指定域名的功能。
两种模式同时启用时,配置模式优先。推荐使用配置模式,以便使用中间件等功能。
配置模式的路由配置文件router.php
示例:
return [ 'group' => [ ['middleware' => ['Example', 'Throttle:10'], 'path' => [ '/echo/string/with/middleware' => 'test2@test2', '/return/string/with/middleware' => 'test2@test3', ]], ['prefix' => 'test/prefix', 'middleware' => ['Throttle:10,5'], 'path' => [ '/home' => 'home@index', '/return/string/with/get/param' => 'home@test', ]], ['prefix' => 'controller_2', 'namespace' => 'AppHttpController_2', 'path' => [ '/home' => 'home@index', '/return/string/without/get/param' => 'home@test', '/smarty' => 'home@smarty', ]], ['middleware' => ['GroupExample'], 'path' => [ '/test4' => 'test2@test2',//echo string with middleware ]], ['middleware' => ['Stop'], 'path' => [ '/middleware_stop/?' => 'test2@test2',//echo string with middleware ]], ['host' => 'www.test.com', 'middleware' => ['Throttle:10,5'], 'path' => [ '/home' => 'home@index', '/return/string/with/get/param' => 'home@test', ]], ], 'path' => [ '/' => 'home@index',//view home '/test/url/func' => 'home@test2',//return string 'test/path/param/?' => 'home@test3', '/smarty' => 'home@smarty', '/not/found' => 'home@home3',//404 '/test/error/500' => 'home@test4',//500 '/home/path/test' => 'home@index',//view home '/testLang' => 'test2@testLang',//zh-CN '/testLangUS' => 'test2@testLang2'//en-US ] ];
配置的功能点:
path:访问路径,绑定对应的控制器及其方法,单独使用时不支持配置中间件等功能;支持路径参数,通过英文问号定义; middleware:路径使用的中间件,支持配置多个中间件,可以直接使用类名,也可以在middleware.php文件中定义简写别名; prefix:路径前缀,相同前缀的路径可以归组; namespace:控制器的命名空间,默认为目录app/Http/Controller对应的命名空间; host:指定域名,路径只在指定域名下存在; group:配置组,中间件等功能的必须包含在配置组内。路由配置文件在debug关闭的时候(一般是生产环境),会自动缓存路由的索引,可能导致对路由的修改不生效,建议开启debug,或者手动删除路由缓存文件cache/app/router.cache
。
14、控制器 Controller
控制器方法中应该返回(return)数据、以便交由框架响应组件输出,不应该直接在控制器内输出。
在控制器内使用实例,建议通过门面,或者契约的依赖注入。
15、数据库 DataBase
可以在bootstrap目录下的服务启动文件中,替换数据库契约绑定的实体类:
/** * 默认使用Pdo */ $container->bind(ShyContractDataBase::class, ShyDataBaseIlluminate::class);
15.1 laravel的DB包
框架兼容laravel的DB包,你可以通过下面的命令安装此包:
composer require illuminate/database 5.8
在配置文件database.php
中配置数据库,然后在启动文件中替换DataBase实体类,便可使用了。
使用方式如下:
use Shy/Core/Facades/DB; DB::table('users')->where('id', 2)->get();
Illuminate Database的更多用法,可以查看该项目的文档
16、模版 View
框架自带模版没有采用字符解析这种复杂的设计,因为这种方式不仅实现复杂、还制定了一套模版规则需要用户学习。
框架的模版需要使用原生PHP语法开发,并且只提供了必须少量函数,学习成本较低。
但是,要求开发者做好isset()
、empty()
、is_array()
等预防报错处理。
此外,为了满足开发者的需求,框架支持了Smarty模版系统。
16.1 自带模版的辅助函数
view:模版类的封装。用于便捷地在控制器中使用模版,可传参、也可链式调用。每次使用本函数都会在容器中新建或替换模版实例。 include_view:在布局页中输出模版;在布局页、模版中引入组件模版。 param:在模版中输出变量或常量,不使用该函数输出报错无法被框架正常处理。在控制器方法中使用view函数:
public function index() { $info = 'Hello World'; $title = 'Shy Framework'; return view('home', compact('title', 'info'))->layout('main'); return view('home', compact('title', 'info'), 'main');//等价方法 }
include_view函数用于在布局页中输出子模版,或者引入模版组件;param函数输出变量和常量:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/> <meta name="renderer" content="webkit"> <title><?php param('title') ?></title> <link type="text/css" rel="stylesheet" href="<?php url() ?>css/app.css"> <?php push_resource('header-css') ?> </head> <body> <?php include_view() ?> <?php include_view('component/footer') ?> <?php push_resource('footer-js') ?> </body> </html>
布局页的子页:
<?php push_resource('footer-js', [url() . 'vendor/jquery/dist/jquery.js', ''], 'js'); ?> <div id="hello-world"> <?php param('info') ?> <?php param('not_exist_param', true) ?> </div> <div id="system"> <p>Container Start Id: <?php echo shy()->startId(); ?></p> <p>Memory Peak: <?php echo memory_get_peak_usage() / 1024; ?> kb</p> <p>Running Time: <?php echo microtime(true) - shy()->startTime(); ?> second</p> <?php if (shy()->has('HTTP_LOOP_START_TIME')) { ?> <p>Recycling Time: <?php echo microtime(true) - shy()->get('HTTP_LOOP_START_TIME'); ?> second</p> <?php } ?> <br> <p>Loaded instances memory used: </p> <ul> <?php $instanceCount = 0; foreach (shy()->memoryUsed() as $abstract => $instances) { foreach ($instances as $key => $memoryUsed) { $instanceCount++; ?> <li><?php echo '[' . $instanceCount . '] ' . $abstract . '(' . ($key + 1) . ') ' . $memoryUsed / 1024 . ' kb'; ?></li> <?php } } ?> </ul> </div>
16.2 Smarty模版
框架提供了对Smarty模版的支持,需要先安装Smarty包。
composer require smarty/smarty
16.2.1 在控制器中调用Smarty模版实例
return smarty('smarty.tpl', $params);
16.2.2 Smarty模版实例
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/> <meta name="renderer" content="webkit"> <title>{$title}</title> <link type="text/css" rel="stylesheet" href="{BASE_URL}css/app.css"> </head> <body> <div id="hello-world"> {$info} </div> <div id="system"> <p>Container Start Id: {$shy->startId()}</p> <p>Memory Peak:{memory_get_peak_usage()/1024} kb</p> <p>Running Time: {microtime(true) - $shy->startTime()} second</p> {if $shy->has('HTTP_LOOP_START_TIME')} <p>Recycling Time: {microtime(true) - $shy->get('HTTP_LOOP_START_TIME')} second</p> {/if} <br> <p>Loaded instance's abstract: </p> <ol> {foreach $shy->memoryUsed() as $abstract => $instances} {foreach $instances as $key => $memoryUsed} <li>{$abstract}({$key + 1}) {$memoryUsed/1024} kb</li> {/foreach} {/foreach} </ol> </div> {include file='component/footer.php'} </body> </html>
17、命令模式 Command Mode
框架支持命令模式。在项目根目录执行下述命令可以查看所有命令:
php command list
如果你需要拓展命令,可以在appCommand目录下编写命令,并在配置文件command.php
中配置命令名称、对象和方法。
拓展命令的代码示例:
namespace AppCommand; class Example { public function test() { return 'Just for fun'; } }
18、常驻内存模式 PHP-CLI Mode
18.1 简介
常驻内存模式是以PHP-CLI方式执行Web框架。
该模式相对与PHP传统的Web服务运行方式,做到了进程复用不销毁,避免了代码重复加载、重复编译导致的消耗,所以框架性能得到大幅度提升。
框架基于WorkerMan提供的socket服务,实现了通过命令模式运行的、常驻内存的Web服务框架。传统Web方式与常驻内存模式可以同时运行。
18.2 使用
安装WorkerMan包:
composer require workerman/workerman
你可以在配置文件workerman.php
中,配置服务端口、工作进程数:
/* | http in socket */ 'http' => [ 'port' => 2348, 'worker' => 2 ],
由于常驻内存模式不依赖nginx、apache等服务器程序,框架本身可作为服务器,port端口可以直接占用80端口。
如果你希望框架配合nginx、apache等使用,也可以使用其它端口运行框架、然后配置服务器程序的端口代理。
nginx转发配置:
location / { root /usr/share/nginx/shy/pulic; if (!-e $request_filename) { proxy_pass http://127.0.0.1:2348; } }
在项目根目录执行下面的命令即可管理服务:
/** * 启动 */ php command http_workerman start /** * 后台运行模式启动 */ php command http_workerman start -d /** * 关闭|重启|平滑重启|查看状态|查看链接 */ php command http_workerman stop|restart|reload|status|connections
18.3 开发者注意事项
PHP-CLI + Socket运行环境,相对于传统Web运行环境有根本差异,所以有很多需要注意的地方。
最需要注意的是,实例循环复用积累的运行状态。比如:某实例带着 上一个请求的状态 执行新的请求。在实例复用之前必须将实例恢复初始状态,否则难以保证不会出现复用混乱的情况。
框架各部分已经做好了初始化处理,但是框架无法顾及到开发者实现的业务逻辑部分。
开发者注意事项:
业务逻辑实例如果想要复用,必须做好初始化,否则会出现混乱。如果不需要复用,在常驻内存模式下运行请销毁实例。 需要改变的值不能用常量,否则复用的时候无法重新赋值。 header函数在Socket环境下不能正常使用,可以使用WorkerMan的http对象的方法。echo、var_dump或者页面等可以正常输出,因为框架做了ob缓冲区、会自动把缓冲区内容放进WorkerMan的通道里面输出。18.4 容器实例智能调度(未完成)
目前只是简单的复用框架实例,计划实现"基于使用历史统计的容器实例智能调度系统"。
系统会自动判断实例是否可复用、并且基于统计数据动态判断是否自动销毁实例或者预先载入实例。本功能可以简化开发者的操作,同时可以平衡内存与时间占用、提升运行效率。
19、通信模式 Socket Mode
框架封装了基于WorkerMan的Socket服务。你可以在配置文件workerman.php
中配置服务端口、工作进程数。支持配置多组服务,并且支持同时运行多组服务。
启动对应服务:
/** * 启动 */ php command workerman chat start
上述命令中,chat是服务名。更多操作请查看常驻内存模式章节。
20、单元测试 Unit Test
框架可使用phpunit做单元测试。在tests文件夹中编写测试代码,在框架根目录执行下面的命令即可执行测试。
由于框架支持php7.0及以上版本,适配phpunit版本为phpunit 6.x。
安装phpunit 6.5:
wget https://phar.phpunit.de/phpunit-6.5.phar php phpunit-6.5.phar --version chmod +x phpunit-6.5.phar mv phpunit-6.5.phar /usr/local/bin/phpunit
执行单元测试:
phpunit tests/containerTest
21、常量 Constant
框架提供了一些常量可供使用:
SHY_ENV:运行环境,develop、testing、production。 BASE_PATH:项目根目录 APP_PATH:app开发目录 CACHE_PATH:缓存文件目录 EXTEND_PATH:拓展目录 PUBLIC_PATH:Http服务开放目录 VIEW_PATH:模版目录22、杂项函数 Miscellaneous function
get_throwable_array:以数组形式获取可抛出对象 is_cli:是否处于CLI模式下 stream_for:基于输入类型创建stream流 dd:调试输出 get_array_key:从数组中获取健值 empty_or_splice:留空或者拼接 random_code:随机码 mime:获取mime类型 url:获取url redirect:跳转 xss_clean:清除XSS csrf_field:生成CSRF口令表单域 csrf_meta:生成CSRF口令meta标签 csrf_token:生成CSRF口令 csrf_verify:验证CSRF口令 is_valid_ip:验证IP get_valid_ips:过滤非法IP get_response_json:获取响应json url_chinese_encode:中文url编码 is_mobile:是否移动设备
版权声明:
1、该文章(资料)来源于互联网公开信息,我方只是对该内容做点评,所分享的下载地址为原作者公开地址。2、网站不提供资料下载,如需下载请到原作者页面进行下载。
3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考学习用!
4、如文档内容存在违规,或者侵犯商业秘密、侵犯著作权等,请点击“违规举报”。