ECN>> shy>> 返回
项目作者: lynncho

项目描述 :
简洁却强大的高性能框架 The simple but powerful high performance framework.
高级语言: PHP
项目地址: git://github.com/lynncho/shy.git
创建时间: 2018-03-02T07:31:00Z
项目社区:https://github.com/lynncho/shy

开源协议:MIT License

关键词:
framework memory-resident mvc php

下载


害羞框架 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

  1. #默认master分支,包含最新特性,可能不稳定
  2. git clone https://github.com/lynnclub/shy.git MyProjectName
  3. #获取指定的发行版本
  4. git clone -b 1.4.0 https://github.com/lynnclub/shy.git MyProjectName

推荐使用稳定的 发行版本

第二步:安装依赖包 Step two: Install dependencies

  1. #进入项目目录
  2. cd MyProjectName
  3. #执行composer依赖包安装
  4. composer install

恭喜!!!接下来,只需要配置Web站点,或者启动常驻内存模式,就可以运行框架了。

第三步(可选):常驻内存模式运行 Step three(optional): Running in resident memory mode

  1. 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)系列规范,与遵守相同规范的框架互相兼容。

  1. 基础编码:遵守 PSR-1: Basic Coding Standard
  2. 日志:遵守 PSR-3: Logger Interface
  3. 自动加载:遵守 PSR-4: Autoloader
  4. 容器:遵守 PSR-11: Container interface ;
  5. 缓存:遵守 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 目录结构

  1. shy 框架根目录
  2. |
  3. | phpunit.php 单元测试入口
  4. | command 命令模式入口
  5. | server.php Http服务调试入口(仅供调试使用,不建议将整个项目目录暴露在web服务器下)
  6. | gulpfile.js Gulp前端构建服务
  7. |
  8. |___app 开发目录
  9. | |
  10. | |___Command 命令服务开发目录
  11. | |___Function 函数开发目录
  12. | |___Http Http服务开发目录
  13. | |___Socket Socket服务开发目录
  14. |
  15. |___bootstrap 服务启动目录
  16. | |
  17. | |___command.php 命令服务启动程序
  18. | |___http.php Http服务启动程序
  19. | |___http_workerman.php 基于workerman的常驻内存Http服务启动程序
  20. |
  21. |___cache 缓存目录
  22. | |
  23. | |___app 系统缓存目录
  24. | |___log 日志目录
  25. |
  26. |___config 配置目录
  27. | |
  28. | |___ develop 开发环境配置目录
  29. | |___ testing 测试环境配置目录
  30. | |___ production 生产环境配置目录
  31. |
  32. |___public Http服务开放目录
  33. | |
  34. | | index.php Http服务入口
  35. | |
  36. | |___upload 上传文件目录
  37. | |___vendor 前端第三方资源包目录
  38. |
  39. |___shy 框架目录
  40. | |
  41. | | Command.php 命令服务
  42. | | Config.php 配置组件
  43. | | Container.php 容器组件
  44. | | Facade.php 门面组件
  45. | | Hook.php 钩子组件
  46. | | Pipeline.php 流水线组件
  47. | | Process.php 进程组件
  48. | | HttpInWorkerMan.php 基于workerman的常驻内存Http服务
  49. | | SocketInWorkerMan.php 基于workermanSocket服务
  50. | |
  51. | |___Cache 缓存目录
  52. | |___Command 命令模式目录
  53. | |___Contract 契约目录
  54. | |___Exception 异常类目录
  55. | |___Http Web服务目录
  56. | |___Library 类库目录
  57. | |___Socket Socket服务目录
  58. |
  59. |___tests 单元测试目录
  60. |
  61. |___vendor composer依赖包安装目录

1.4 Http服务生命周期

  1. 启动器运行(Bootstrap)
  2. 启动自动加载(Composer)
  3. 读取并设置运行环境(SHY_ENV)
  4. 定义内置常量(Constant)
  5. 启动容器,注册组件(Container)
  6. 启动配置组件(Config)
  7. 设置时区(Timezone)
  8. 注册异常处理(ExceptionHandler)
  9. 会话初始化(Session)
  10. 引入模版函数文件(View)
  11. 引入自定义文件
  12. 装载请求(Request)
  13. 路由解析(Router)
  14. 执行中间件、控制器(Router、Middleware、Controller)
  15. 输出响应(Response)
  16. 请求组件恢复初始化状态(Request)

1.5 远期规划

  1. 容器实例调度
  2. 单元测试覆盖率100%
  3. Swoole socket
  4. Api便捷开发框架

2、契约 Contract

契约是比接口(interface)更广义的概念,意义相似,但是不局限于接口的形式。契约可以是接口、抽象类,甚至是非硬性约束的惯例。

各种组件都按照契约的规范实现,比如容器、缓存、配置、日志、门面等,部分组件的契约还兼容PSR规范。

契约在容器中的使用方式:在容器中注册契约类名,绑定实现了该契约的实体类。遵守同一契约的实体类,可以在容器中替换。

bootstrap程序中,契约类与实体类的绑定:

  1. $container->binds([
  2. ConfigContract::class => Config::class,
  3. LoggerContract::class => File::class,
  4. ExceptionHandlerContract::class => Handler::class,
  5. PipelineContract::class => Pipeline::class,
  6. CacheContract::class => Memory::class,
  7. DataBaseContract::class => Illuminate::class,
  8. ResponseContract::class => Response::class,
  9. SessionContract::class => Session::class,
  10. RouterContract::class => Router::class,
  11. ViewContract::class => View::class,
  12. ]);

3、容器与依赖注入 Container and Dependency injection

框架的容器类,遵守PSR(PHP Standards Recommendations)中的《PSR-11: Container interface》接口规范。并且,实现了PHP的ArrayAccess、Countable接口,可以当作数组使用。

容器是对实例集中管理的实例池,可以创建、绑定、使用、替换或者移除实例。

此外,框架拓展了容器的概念,支持把字符、数组等任意内容当作实例管理。即容器是实例与数据的集中管理池

注意事项:

  1. 需要复用的、贯穿框架生命周期的实例或者数据,应该加入到容器;
  2. 框架的核心服务及配置,可以被自由访问,建议对框架设计有足够的了解再操作。

3.1 绑定实例

3.1.1 用法模式

支持绑定实例或者数据,绑定内容为闭包,将在 加入与获取实例 时执行该闭包。

  1. bind(类名, 类名、实例或匿名函数):类名为键,对应的值可以是类名、实例或匿名函数
  2. bind(数据名, 数据):数据名为键,对应传入的数据
  3. bind(类名):等于bind(类名, 类名)
  4. bind(数据名):等于bind(数据名, 数据名)

3.1.2 代码示例

  1. /**
  2. * 绑定类名
  3. */
  4. bind(Shy\Http::class);
  5. /**
  6. * 直接绑定实例
  7. */
  8. bind(Shy\Http::class, new Shy\Http());
  9. bind('Shy\Http', new Shy\Http());
  10. /**
  11. * 绑定匿名函数(可用于延迟传参、即执行闭包时再传参,支持任意个参数)
  12. */
  13. bind(Shy\Http::class, function ($param1, $param2) {
  14. return new Shy\Http($param1, $param2);
  15. });
  16. /**
  17. * 链式调用:绑定实例->加入容器然后获取实例->执行实例的run方法
  18. *
  19. * getOrMake方法等同于shy函数
  20. */
  21. bind(Shy\Http::class, new Shy\Http())->getOrMake(Shy\Http::class)->run();
  22. /**
  23. * 链式调用:绑定匿名函数->带参执行,加入容器然后获取实例->执行实例的run方法
  24. */
  25. bind(Shy\Http::class, function ($param1, $param2) {
  26. return new Shy\Http($param1, $param2);
  27. })->getOrMake(Shy\Http::class, $param1, $param2)->run();

如上所述,bind函数是对容器类的封装,会返回容器实例以便链式调用。

bind函数的功能只是绑定实例,并没有将实例加入到容器的实例池。将实例加入容器、获取容器中实例,是shy函数的功能。

3.2 加入容器与获取实例

3.2.1 用法模式

  1. shy(类名, null, 任意个实例化参数):类名作为实体类
  2. shy(类名, 实体类名, 任意个实例化参数):类名绑定实体类
  3. shy(类名, 实例或匿名函数, 任意个实例化参数):类名绑定实例或者匿名函数

3.2.2 代码示例

  1. /**
  2. * 实例化Http类,并加入容器
  3. */
  4. shy(Shy\Http::class);
  5. /**
  6. * 带参数实例化(未绑定)
  7. */
  8. shy(Shy\Http::class, null, $param1, $param2);
  9. /**
  10. * 先绑定,再带参数实例化
  11. */
  12. bind(Shy\Http::class);
  13. shy(Shy\Http::class, $param1, $param2);
  14. /**
  15. * 带参数实例化File类,契约为Logger
  16. */
  17. shy(Shy\Contract\Logger::class, Shy\Logger\File::class, $param1, $param2);
  18. // 获取契约为Logger的File实例(需要已绑定或已加入容器)
  19. shy(Shy\Contract\Logger::class);
  20. /**
  21. * 直接将实例加入容器
  22. */
  23. shy(Shy\Http::class, new Shy\Http());
  24. /**
  25. * 设置类名的别名
  26. */
  27. shy()->alias('config', Shy\Contract\Config::class);
  28. // 使用别名获取实例
  29. shy('config');

shy函数是框架的核心函数,代表对容器的操作

使用shy函数时,如果容器内已经存在指定类名的实例,则直接返回该实例。如果不存在,会将实例加入到容器中,然后返回被加入的实例。

该函数会自动尝试上述所有用法模式来获取实例。比如,从绑定的实例中获取、执行绑定的匿名函数获取实例、或者通过反射实例化类。

实例加入容器之后,会清除绑定以免占用内存。

3.3 更多操作

更多操作请直接使用容器类。通过使用shy函数、不传参数,可以获取到容器实例。

  1. /**
  2. * 无参数、返回容器本身
  3. */
  4. shy();
  5. /**
  6. * 是否存在实例
  7. */
  8. shy()->has(Shy\Http::class);
  9. /**
  10. * 创建实例、已存在实例会替换实例
  11. */
  12. shy()->make(Shy\Http::class);
  13. /**
  14. * 移除实例
  15. */
  16. shy()->remove(Shy\Http::class);

3.4 依赖注入

容器执行闭包、或者通过反射实例化类的时候,会自动注入构造方法的依赖,无需手动实例化。

  1. use Shy\Http\Contract\Request;
  2. use Shy\Contract\Config;
  3. /**
  4. * Logger constructor.
  5. *
  6. * @param Request $request
  7. * @param Config $config
  8. */
  9. public function __construct(Config $config, Request $request = null)
  10. {
  11. $this->config = $config;
  12. $this->request = $request;
  13. }

如上所述,Logger类的构造方法依赖Config和Request契约类参数。容器通过反射感知变量类型,可以自动注入契约绑定的实体类。

4、配置与环境 Config and Environment

配置类继承了内存缓存类Shy\Cache\Memory::class,无依赖。在app.cache开启时,配置会被持久化缓存到本地文件。

  1. /**
  2. * 读取配置文件app.php的配置
  3. */
  4. $appConfig = config('app');
  5. /**
  6. * 读取配置文件app.php中的,cache配置
  7. */
  8. $isCache = config('app.cache');
  9. /**
  10. * 读取配置文件workerman.php中的,socket配置
  11. */
  12. $socketConfig = config('workerman.socket');

环境配置

框架预设了三种环境,develop开发环境、testing测试环境、production生产环境。如果未设置环境值,默认为develop开发环境。

将优先使用环境目录下的配置文件,当环境目录下不存在该文件时,使用通用的配置文件

Nginx fastcgi配置环境值:

  1. fastcgi_param SHY_ENV 'testing';

Linux系统配置环境值:

  1. // .bash_profile文件
  2. export SHY_ENV=production

环境配置可以自由拓展,框架按照配置的环境值,读取config目录下的同名环境目录。

5、异常捕获 Exception Handler

框架在各个服务的入口处,注册了异常(Exception)与错误(Error)捕获,能够捕获并处理所有未被捕获的异常、错误,甚至是Shut Down

错误与Shut Down会被转化成异常,统一按异常处理。

框架为每个服务提供了异常处理类(Handler)。你也可以实现Handler接口来自定义异常处理,在启动器的服务入口中替换绑定关系,使其生效。

替换异常处理契约绑定的实体类:

  1. // MyProjectName/bootstrap/http.php文件
  2. $container->bind(Shy\Contract\ExceptionHandler::class, Shy\Http\Exception\Handler::class);

对于需要返回Http Code的错误,可以抛出HttpException。该错误的响应会输出errors/common.php模版:

  1. use Shy\Http\Exception\HttpException;
  2. throw new HttpException(403, lang(5000));

6、门面 Facade

门面提供了便捷的静态调用方式。实现门面类需要继承框架的门面抽象类,并且重写父类的getInstance()方法,以便向父类传递实体类。实现代码示例如下:

  1. namespace Shy\Facade;
  2. use Shy\Facade;
  3. use Shy\Contract\Cache as CacheContract;
  4. class Cache extends Facade
  5. {
  6. /**
  7. * 获取实例
  8. * Get the instance.
  9. *
  10. * @return object
  11. */
  12. protected static function getInstance()
  13. {
  14. return shy(CacheContract::class);
  15. }
  16. }

门面类的父类——门面抽象类,通过魔术方法__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目录下的服务启动文件中,替换缓存契约绑定的实体类:

  1. $container->bind(Shy\Contract\Cache::class, Shy\Cache\Redis::class);

调用缓存门面的方法:

  1. use Shy\Facade\Cache;
  2. Cache::set('test', 123);
  3. Cache::get('test');

8、日志 Logger

框架的日志类,遵守PSR(PHP Standards Recommendations)中的《PSR-3: Logger Interface》接口规范。

8.1 简介

框架实现了本地文件日志Shy\Logger\File,以及阿里云日志Shy\Logger\Aliyun(阿里云日志类继承了本地文件日志类,使用的时候也会保存本地文件日志)。

如果不需要记录日志,日志契约可以更换绑定Psr\Log\NullLogger类。

可以在bootstrap目录下的服务启动文件中,替换日志契约绑定的实体类:

  1. $container->bind(Shy\Contract\Logger::class, Shy\Logger\Aliyun::class);

8.2 错误级别

  1. emergency 紧急
  2. alert 警报
  3. critical 严重
  4. error 错误
  5. warning 警告
  6. notice 注意
  7. info 信息
  8. debug 调试

8.3 自定义日志

自定义日志需要实现Shy\Contract\Logger接口,并且继承PSR的Psr\Log\AbstractLogger

9、流水线 Pipeline

流水线是一种连贯的流程调度工具,使用流水线执行的对象或函数,可以享受容器的依赖注入服务。流水线连通了包括中间件、控制器在内的运行流程。

Pipeline类的方法:

  1. send:设置传入参数,参数数量没有限制;
  2. through:设置流水线的处理对象,可以是一个或者多个;
  3. via:设置处理对象需要执行的方法,默认执行handle方法;
  4. then:流水线的执行方法,需要设置回调函数;
  5. run:流水线的执行方法,无需设置回调函数。

开发者可使用流水线来执行自己的调度,代码实例如下:

  1. use Shy\Contract\Pipeline;
  2. /**
  3. * 执行一组中间件,带回调执行
  4. */
  5. $response = shy(Pipeline::class)
  6. ->send(...$this->pathParam)
  7. ->through($this->middleware)
  8. ->then(function () {
  9. return $this->runController();
  10. });
  11. /**
  12. * 执行单个控制器,不带回调执行
  13. */
  14. $response = shy(Pipeline::class)
  15. ->through($this->controller)
  16. ->via($this->method)
  17. ->run();

10、请求 Request

通过门面类使用:

  1. // 引入门面类
  2. use Shy\Http\Facade\Request;
  3. // 是否初始化(常驻内存模式使用)
  4. Request::initialized();
  5. // 获取全部请求
  6. Request::all();
  7. // 获取数据流 php://input
  8. Request::getContent();
  9. // 获取GET请求参数
  10. Request::get('key');
  11. // 获取POST请求参数
  12. Request::post('key');

通过契约的依赖注入使用:

  1. // 引入契约类
  2. use Shy\Http\Contract\Request;
  3. // 控制器通过依赖注入使用实体类
  4. public function test(Request $request)
  5. {
  6. return 'controller echo test ' . json_encode($request->all());
  7. }

11、中间件 Middleware

中间件是控制器请求与响应的中间步骤,是流水线(Pipeline)的一种特例。流水线传入的第一个参数$next,是用于运行控制器的闭包。

11.1 前置中间件

中间件在控制器之前执行,称为“前置中间件”。如下,是有IP白名单功能的前置中间件:

  1. namespace Shy\Http\Middleware;
  2. use Closure;
  3. use Shy\Contract\Middleware;
  4. use Shy\Http\Facade\Request;
  5. use Shy\Facade\Logger;
  6. use Shy\Http\Exception\HttpException;
  7. class IpWhitelist implements Middleware
  8. {
  9. /**
  10. * Handle
  11. *
  12. * @param Closure $next
  13. * @param array ...$passable
  14. * @return mixed|string
  15. */
  16. public function handle(Closure $next, ...$passable)
  17. {
  18. $hit = FALSE;
  19. $whitelist = config('ip_whitelist');
  20. if (is_array($whitelist)) {
  21. $userIps = Request::getClientIps();
  22. foreach ($userIps as $userIp) {
  23. if (in_array($userIp, $whitelist)) {
  24. $hit = TRUE;
  25. }
  26. }
  27. }
  28. if (!$hit) {
  29. Logger::info('Ip whitelist block request', Request::all());
  30. if (Request::expectsJson()) {
  31. return get_response_json(5000);
  32. } else {
  33. throw new HttpException(403, lang(5000));
  34. }
  35. }
  36. return $next();
  37. }
  38. }

11.2 后置中间件

  1. use Shy\Contract\Middleware;
  2. use Closure;
  3. class Test implements Middleware
  4. {
  5. /**
  6. * Handle
  7. *
  8. * @param Closure $next
  9. * @param array ...$passable
  10. * @return mixed|string
  11. */
  12. public function handle(Closure $next, ...$passable)
  13. {
  14. // run controller
  15. $response = $next();
  16. // do something
  17. $response = json_encode($response);
  18. return $response;
  19. }
  20. }

11.3 使用中间件

中间件需要在配置文件middleware.php中定义别名或者别名组,然后在路由中填写别名使用。

  1. return [
  2. 'IpWhitelist' => Shy\Http\Middleware\IpWhitelist::class,
  3. 'Throttle' => Shy\Http\Middleware\Throttle::class,
  4. 'Example' => App\Http\Middleware\Example::class,
  5. 'GroupExample' => [
  6. Shy\Http\Middleware\CSRF::class,
  7. ]
  8. ];

11.4 内置中间件

  1. CSRF:防止跨站请求伪造,配合内置函数csrf_token()使用;
  2. GetOnly:仅限GET请求,其它方式响应404;
  3. PostOnly:仅限POST请求,其它方式响应404;
  4. IpWhitelist:IP白名单,通过配置文件ip_whitelist.php管理白名单;
  5. Throttle:限流阀,默认1分钟内限制单IP请求60次,可在路由中自定义设置。例如1分钟内限制10次、5分钟解禁:Throttle:10,5

12、响应 Response

对于控制器,只需要return数据或者模版,Response组件会自动输出响应。建议不要手动输出,而是交给框架去处理响应。

  1. /**
  2. * 返回字符串
  3. */
  4. return 'controller echo';
  5. /**
  6. * 返回内置模版
  7. */
  8. return view('home', compact('title', 'info'))->layout('main');
  9. /**
  10. * 返回Smarty模版
  11. */
  12. return smarty('smarty.tpl', $params);

13、路由 Router

路由通过请求(Request)组件获取到请求路径,然后解析出对应控制器及其方法,最终调度执行中间件与控制器。

路由支持”路径模式”和”配置模式”,可以在配置文件app.php中关闭或启用。路径模式是直接把请求路径当成控制器及其方法,比较简单;配置模式根据请求路径查找路由配置,得到控制器及其方法,支持中间件、路径前缀、命令空间、指定域名的功能。

两种模式同时启用时,配置模式优先。推荐使用配置模式,以便使用中间件等功能

配置模式的路由配置文件router.php示例:

  1. return [
  2. 'group' => [
  3. ['middleware' => ['Example', 'Throttle:10'], 'path' => [
  4. '/echo/string/with/middleware' => 'test2@test2',
  5. '/return/string/with/middleware' => 'test2@test3',
  6. ]],
  7. ['prefix' => 'test/prefix', 'middleware' => ['Throttle:10,5'], 'path' => [
  8. '/home' => 'home@index',
  9. '/return/string/with/get/param' => 'home@test',
  10. ]],
  11. ['prefix' => 'controller_2', 'namespace' => 'App\\Http\\Controller_2', 'path' => [
  12. '/home' => 'home@index',
  13. '/return/string/without/get/param' => 'home@test',
  14. '/smarty' => 'home@smarty',
  15. ]],
  16. ['middleware' => ['GroupExample'], 'path' => [
  17. '/test4' => 'test2@test2',//echo string with middleware
  18. ]],
  19. ['middleware' => ['Stop'], 'path' => [
  20. '/middleware_stop/?' => 'test2@test2',//echo string with middleware
  21. ]],
  22. ['host' => 'www.test.com', 'middleware' => ['Throttle:10,5'], 'path' => [
  23. '/home' => 'home@index',
  24. '/return/string/with/get/param' => 'home@test',
  25. ]],
  26. ],
  27. 'path' => [
  28. '/' => 'home@index',//view home
  29. '/test/url/func' => 'home@test2',//return string
  30. 'test/path/param/?' => 'home@test3',
  31. '/smarty' => 'home@smarty',
  32. '/not/found' => 'home@home3',//404
  33. '/test/error/500' => 'home@test4',//500
  34. '/home/path/test' => 'home@index',//view home
  35. '/testLang' => 'test2@testLang',//zh-CN
  36. '/testLangUS' => 'test2@testLang2'//en-US
  37. ]
  38. ];

配置的功能点:

  1. path:访问路径,绑定对应的控制器及其方法,单独使用时不支持配置中间件等功能;支持路径参数,通过英文问号定义;
  2. middleware:路径使用的中间件,支持配置多个中间件,可以直接使用类名,也可以在middleware.php文件中定义简写别名;
  3. prefix:路径前缀,相同前缀的路径可以归组;
  4. namespace:控制器的命名空间,默认为目录app/Http/Controller对应的命名空间;
  5. host:指定域名,路径只在指定域名下存在;
  6. group:配置组,中间件等功能的必须包含在配置组内。

路由配置文件在debug关闭的时候(一般是生产环境),会自动缓存路由的索引,可能导致对路由的修改不生效,建议开启debug,或者手动删除路由缓存文件cache/app/router.cache

14、控制器 Controller

控制器方法中应该返回(return)数据、以便交由框架响应组件输出,不应该直接在控制器内输出。

在控制器内使用实例,建议通过门面,或者契约的依赖注入。

15、数据库 DataBase

可以在bootstrap目录下的服务启动文件中,替换数据库契约绑定的实体类:

  1. /**
  2. * 默认使用Pdo
  3. */
  4. $container->bind(Shy\Contract\DataBase::class, Shy\DataBase\Illuminate::class);

15.1 laravel的DB包

框架兼容laravel的DB包,你可以通过下面的命令安装此包:

  1. composer require illuminate/database 5.8

在配置文件database.php中配置数据库,然后在启动文件中替换DataBase实体类,便可使用了。

使用方式如下:

  1. use Shy/Core/Facades/DB;
  2. DB::table('users')->where('id', 2)->get();

Illuminate Database的更多用法,可以查看该项目的文档

16、模版 View

框架自带模版没有采用字符解析这种复杂的设计,因为这种方式不仅实现复杂、还制定了一套模版规则需要用户学习。

框架的模版需要使用原生PHP语法开发,并且只提供了必须少量函数,学习成本较低。

但是,要求开发者做好isset()empty()is_array()等预防报错处理。

此外,为了满足开发者的需求,框架支持了Smarty模版系统。

16.1 自带模版的辅助函数

  1. view:模版类的封装。用于便捷地在控制器中使用模版,可传参、也可链式调用。每次使用本函数都会在容器中新建或替换模版实例。
  2. include_view:在布局页中输出模版;在布局页、模版中引入组件模版。
  3. param:在模版中输出变量或常量,不使用该函数输出报错无法被框架正常处理。

在控制器方法中使用view函数:

  1. public function index()
  2. {
  3. $info = 'Hello World';
  4. $title = 'Shy Framework';
  5. return view('home', compact('title', 'info'))->layout('main');
  6. return view('home', compact('title', 'info'), 'main');//等价方法
  7. }

include_view函数用于在布局页中输出子模版,或者引入模版组件;param函数输出变量和常量:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport"
  7. content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
  8. <meta name="renderer" content="webkit">
  9. <title><?php param('title') ?></title>
  10. <link type="text/css" rel="stylesheet" href="<?php url() ?>css/app.css">
  11. <?php push_resource('header-css') ?>
  12. </head>
  13. <body>
  14. <?php include_view() ?>
  15. <?php include_view('component/footer') ?>
  16. <?php push_resource('footer-js') ?>
  17. </body>
  18. </html>

布局页的子页:

  1. <?php
  2. push_resource('footer-js', [url() . 'vendor/jquery/dist/jquery.js', ''], 'js');
  3. ?>
  4. <div id="hello-world">
  5. <?php param('info') ?>
  6. <?php param('not_exist_param', true) ?>
  7. </div>
  8. <div id="system">
  9. <p>Container Start Id: <?php echo shy()->startId(); ?></p>
  10. <p>Memory Peak: <?php echo memory_get_peak_usage() / 1024; ?> kb</p>
  11. <p>Running Time: <?php echo microtime(true) - shy()->startTime(); ?> second</p>
  12. <?php
  13. if (shy()->has('HTTP_LOOP_START_TIME')) { ?>
  14. <p>Recycling Time: <?php echo microtime(true) - shy()->get('HTTP_LOOP_START_TIME'); ?> second</p>
  15. <?php
  16. }
  17. ?>
  18. <br>
  19. <p>Loaded instances memory used: </p>
  20. <ul>
  21. <?php
  22. $instanceCount = 0;
  23. foreach (shy()->memoryUsed() as $abstract => $instances) {
  24. foreach ($instances as $key => $memoryUsed) {
  25. $instanceCount++;
  26. ?>
  27. <li><?php echo '[' . $instanceCount . '] ' . $abstract . '(' . ($key + 1) . ') ' . $memoryUsed / 1024 . ' kb'; ?></li>
  28. <?php }
  29. } ?>
  30. </ul>
  31. </div>

16.2 Smarty模版

框架提供了对Smarty模版的支持,需要先安装Smarty包。

  1. composer require smarty/smarty

16.2.1 在控制器中调用Smarty模版实例

  1. return smarty('smarty.tpl', $params);

16.2.2 Smarty模版实例

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport"
  7. content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
  8. <meta name="renderer" content="webkit">
  9. <title>{$title}</title>
  10. <link type="text/css" rel="stylesheet" href="{BASE_URL}css/app.css">
  11. </head>
  12. <body>
  13. <div id="hello-world">
  14. {$info}
  15. </div>
  16. <div id="system">
  17. <p>Container Start Id: {$shy->startId()}</p>
  18. <p>Memory Peak:{memory_get_peak_usage()/1024} kb</p>
  19. <p>Running Time: {microtime(true) - $shy->startTime()} second</p>
  20. {if $shy->has('HTTP_LOOP_START_TIME')}
  21. <p>Recycling Time: {microtime(true) - $shy->get('HTTP_LOOP_START_TIME')} second</p>
  22. {/if}
  23. <br>
  24. <p>Loaded instance's abstract: </p>
  25. <ol>
  26. {foreach $shy->memoryUsed() as $abstract => $instances}
  27. {foreach $instances as $key => $memoryUsed}
  28. <li>{$abstract}({$key + 1}) {$memoryUsed/1024} kb</li>
  29. {/foreach}
  30. {/foreach}
  31. </ol>
  32. </div>
  33. {include file='component/footer.php'}
  34. </body>
  35. </html>

17、命令模式 Command Mode

框架支持命令模式。在项目根目录执行下述命令可以查看所有命令:

  1. php command list

如果你需要拓展命令,可以在app\Command目录下编写命令,并在配置文件command.php中配置命令名称、对象和方法。

拓展命令的代码示例:

  1. namespace App\Command;
  2. class Example
  3. {
  4. public function test()
  5. {
  6. return 'Just for fun';
  7. }
  8. }

18、常驻内存模式 PHP-CLI Mode

18.1 简介

常驻内存模式是以PHP-CLI方式执行Web框架。

该模式相对与PHP传统的Web服务运行方式,做到了进程复用不销毁,避免了代码重复加载、重复编译导致的消耗,所以框架性能得到大幅度提升。

框架基于WorkerMan提供的socket服务,实现了通过命令模式运行的、常驻内存的Web服务框架。传统Web方式与常驻内存模式可以同时运行。

18.2 使用

安装WorkerMan包:

  1. composer require workerman/workerman

你可以在配置文件workerman.php中,配置服务端口、工作进程数:

  1. /*
  2. | http in socket
  3. */
  4. 'http' => [
  5. 'port' => 2348,
  6. 'worker' => 2
  7. ],

由于常驻内存模式不依赖nginx、apache等服务器程序,框架本身可作为服务器,port端口可以直接占用80端口。

如果你希望框架配合nginx、apache等使用,也可以使用其它端口运行框架、然后配置服务器程序的端口代理。

nginx转发配置:

  1. location / {
  2. root /usr/share/nginx/shy/pulic;
  3. if (!-e $request_filename) {
  4. proxy_pass http://127.0.0.1:2348;
  5. }
  6. }

在项目根目录执行下面的命令即可管理服务:

  1. /**
  2. * 启动
  3. */
  4. php command http_workerman start
  5. /**
  6. * 后台运行模式启动
  7. */
  8. php command http_workerman start -d
  9. /**
  10. * 关闭|重启|平滑重启|查看状态|查看链接
  11. */
  12. php command http_workerman stop|restart|reload|status|connections

18.3 开发者注意事项

PHP-CLI + Socket运行环境,相对于传统Web运行环境有根本差异,所以有很多需要注意的地方。

最需要注意的是,实例循环复用积累的运行状态。比如:某实例带着 上一个请求的状态 执行新的请求。在实例复用之前必须将实例恢复初始状态,否则难以保证不会出现复用混乱的情况

框架各部分已经做好了初始化处理,但是框架无法顾及到开发者实现的业务逻辑部分

开发者注意事项:

  1. 业务逻辑实例如果想要复用,必须做好初始化,否则会出现混乱。如果不需要复用,在常驻内存模式下运行请销毁实例。
  2. 需要改变的值不能用常量,否则复用的时候无法重新赋值。
  3. header函数在Socket环境下不能正常使用,可以使用WorkerMan的http对象的方法。echo、var_dump或者页面等可以正常输出,因为框架做了ob缓冲区、会自动把缓冲区内容放进WorkerMan的通道里面输出。

18.4 容器实例智能调度(未完成)

目前只是简单的复用框架实例,计划实现”基于使用历史统计的容器实例智能调度系统”。

系统会自动判断实例是否可复用、并且基于统计数据动态判断是否自动销毁实例或者预先载入实例。本功能可以简化开发者的操作,同时可以平衡内存与时间占用、提升运行效率。

19、通信模式 Socket Mode

框架封装了基于WorkerMan的Socket服务。你可以在配置文件workerman.php中配置服务端口、工作进程数。支持配置多组服务,并且支持同时运行多组服务。

启动对应服务:

  1. /**
  2. * 启动
  3. */
  4. php command workerman chat start

上述命令中,chat是服务名。更多操作请查看常驻内存模式章节。

20、单元测试 Unit Test

框架可使用phpunit做单元测试。在tests文件夹中编写测试代码,在框架根目录执行下面的命令即可执行测试。

由于框架支持php7.0及以上版本,适配phpunit版本为phpunit 6.x。

安装phpunit 6.5:

  1. wget https://phar.phpunit.de/phpunit-6.5.phar
  2. php phpunit-6.5.phar --version
  3. chmod +x phpunit-6.5.phar
  4. mv phpunit-6.5.phar /usr/local/bin/phpunit

执行单元测试:

  1. phpunit tests/containerTest

21、常量 Constant

框架提供了一些常量可供使用:

  1. SHY_ENV:运行环境,develop、testing、production。
  2. BASE_PATH:项目根目录
  3. APP_PATH:app开发目录
  4. CACHE_PATH:缓存文件目录
  5. EXTEND_PATH:拓展目录
  6. PUBLIC_PATH:Http服务开放目录
  7. VIEW_PATH:模版目录

22、杂项函数 Miscellaneous function

  1. get_throwable_array:以数组形式获取可抛出对象
  2. is_cli:是否处于CLI模式下
  3. stream_for:基于输入类型创建stream流
  4. dd:调试输出
  5. get_array_key:从数组中获取健值
  6. empty_or_splice:留空或者拼接
  7. random_code:随机码
  8. mime:获取mime类型
  9. url:获取url
  10. redirect:跳转
  11. xss_clean:清除XSS
  12. csrf_field:生成CSRF口令表单域
  13. csrf_meta:生成CSRF口令meta标签
  14. csrf_token:生成CSRF口令
  15. csrf_verify:验证CSRF口令
  16. is_valid_ip:验证IP
  17. get_valid_ips:过滤非法IP
  18. get_response_json:获取响应json
  19. url_chinese_encode:中文url编码
  20. is_mobile:是否移动设备