玩一下
composer
搭建框架的流程
- 利用 Composer 一步一步构建自己的 PHP 框架(一)——基础准备
- 利用 Composer 一步一步构建自己的 PHP 框架(二)——构建路由
- 利用 Composer 一步一步构建自己的 PHP 框架(三)——设计 MVC
- 利用 Composer 一步一步构建自己的 PHP 框架(四)——使用 ORM
- 利用 Composer 完善自己的 PHP 框架(一)——视图装载
- 利用 Composer 完善自己的 PHP 框架(二)——发送邮件
- 利用 Composer 完善自己的 PHP 框架(三)——Redis 缓存
默认在项目的
vendor
目录进行安装,相关的依赖库。
# php73是我的php命令环境,这个根据具体的情况使用
$ php73 -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');"
$ php73 composer-setup.php
$ php73 -r "unlink('composer-setup.php');"
# 然后会生成一个命令文件 `composer.phar`
# 例如安装库
$ php73 composer.phar require gregwar/captcha
$ php73 composer.phar config -g repo.packagist composer https://packagist.phpcomposer.com
$ php73 composer.phar config -g repo.packagist composer https://mirrors.aliyun.com/composer/ --no-plugins
$ php73 composer.phar config -g repo.packagist composer https://mirrors.cloud.tencent.com/composer/
composer.json
{
"require": {
}
}
$ php73 composer.phar update
# 安装路由库
{
"require": {
"noahbuscher/macaw": "dev-master"
}
}
# 更新
$ php73 composer.phar update
- 引入
// composer 自动加载
use \NoahBuscher\Macaw\Macaw;
// 路由配置
Macaw::get('/', function() {
echo 'Hello world!';
});
Macaw::dispatch();
主要根据一个静态类(
Macaw
)来进行实现,是一个简单的PHP路由器,只需要通过composer,引入文件,就能快速使用。
主要技术点
static
静态类、静态成员变量、静态方法(因此不需要new对象,而是可以直接通过类进行调用)__callstatic
魔术方法,用来调用不存在的类的静态方法(这样好处是同上,想要静态化去做)$_SERVER
服务器环境信息的数组 (需要去拿到相关当前页面路径的信息)$_SERVER['REQUEST_METHOD']
访问页面请求的方法(如,GET、POST)$_SERVER['REQUEST_URI']
访问页面所需的URI (如,/index.html)
- 一些基础的PHP函数
- 页面url
parse_url()
用来获取页面url的path部分内容 (如,用来寻找当前页面的path)
- 正册相关
preg_match()
执行匹配正则表达式preg_replace()
正则替换 (如,用于批量将//转化为/)
- 数组相关
array_keys()
用来获取数组的key的部分,返回数组array_values()
用来获取数组的value的部分,返回数组
- 字符串相关
strpos()
用来寻找字符串首次出现的位置 (如,用来寻找路由 / 的位置)str_replace()
- 字符串数组转化相关
explode()
将字符串进行分割成数组,拿到不同的part (如,Controllers\demo@page)
- 类型判断相关
is_array()
is_object()
method_exists()
- 函数回调
call_user_func()
- 页面url
主要流程
- 路由装载。通过静态方法的形式(
Macaw::get()
),将项目所有的路由配置都加载进去
- 主要通过
__callstatic($method, $params)
方式来将不同的请求方式的路由配置读进去 - 将相关路由信息push到静态数组中(①
uri
、②method
、③callback
)
- 路由调度。将当前页面的path与系统配置的路由对比,从而找到真正的执行方法
- 获取当前页面请求的 uri
- 获取当前页面请求的 method
- found_route 用来标注是否匹配到路由 当前false
- 看一下uri是否有完全匹配的路由
- 有=> 进入完全匹配的形式
- 无=> 进入正则匹配的形式
- 假设进入完全匹配的形式
- 获取全部匹配uri的路由,然后依次匹配对应method是否相等
- method有完全匹配的,也有Any任意方式的都可以请求到
- 假设进入完全匹配的形式,找到对应的方法的路由
- 获取回调方式,可能是回调函数方法,可能是控制器调用的类的方法
- 然后实现
- 如果最后还是找不到
- 404 显示
app/controllers
控制器目录BaseControllers
基础控制器HomeControllers
home控制器
# composer配置自动加载
"autoload": {
"classmap": [
"app/controllers",
"app/models"
]
}
# 更新
$ php73 composer.phar dump-autoload
# 文件变化
autoload_classmap.php文件 映射 classMap
'BaseController' => $baseDir . '/app/controllers/BaseController.php',
'HomeController' => $baseDir . '/app/controllers/HomeController.php',
autoload_static.php文件 映射 classMap
'BaseController' => __DIR__ . '/../..' . '/app/controllers/BaseController.php',
'HomeController' => __DIR__ . '/../..' . '/app/controllers/HomeController.php',
-
app/models
模型目录 -
Article
article模型 -
MySQL数据库链接方法
- mysqli
- pdo
-
sql文件
- json文件
# composer配置自动加载
"autoload": {
"classmap": [
"app/models",
]
}
# 更新
$ php73 composer.phar dump-autoload
# 文件变化
autoload_classmap.php文件 映射 classMap
'Article' => $baseDir . '/app/models/Article.php',
autoload_static.php文件 映射 classMap
'Article' => __DIR__ . '/../..' . '/app/models/Article.php',
ORM
全称是Object Relational Mapping
(对象关系映射) , O(Object)对象,数据Model持久化类。R(Relation)关系数据,M (Mapping)映射,将对象映射到关系数据,将关系数据映射到对象的过程。ORM 就是以OOP思想,产生增删改查SQL语句。 即用PHP来实现MySQL数据操作。比如有MySQL原生API、MySQLi面向过程、MySQLi面向对象、PDO等。
【envms/fluentpdo
】
- 安装
# 安装路由库
{
"require": {
"envms/fluentpdo": "^2.2.0"
}
}
# 更新
$ php73 composer.phar update
# 文件变化
### composer
autoload_psr4.php
自动加载的映射 'Envms\\FluentPDO\\' => array($vendorDir . '/envms/fluentpdo/src'),
autoload_static.php
prefixLengthsPsr4(长度映射关系,以第一个字母开头的数组,key为库,value为长度)
prefixDirsPsr4 (前缀路径对应,以库为key,路径映射为value)
installed.json
envms/fluentpdo 库的信息 (一个大json)
installed.php
每次root的reference都会变动一次
增加库json基础简单信息
### fluentpdo
src主代码中
Queries/ 基本操作(Base、Common、Delete、Insert、Json、Select、Update)
Exception.php (异常处理)
Literal.php (直译)
Query.php (主文件)
Regex.php (规则类)
Structure.php (结构主键、外键、索引)
Utilities.php (工具类)
- 使用
$pdo = new PDO("mysql:host=$host;port=$port;dbname=$dbname;charset=utf8", $username, $password, $options);
$fluent = new \Envms\FluentPDO\Query($pdo);
// 查询最新的5条数据
$query = $fluent->from('article')
->orderBy('id DESC')
->limit(5);
// 查询获取所有数据
$list = $query->fetchAll();
- 将数据库配置移到配置文件config中(database.php)
app/views
视图目录- 控制器获取模型数据,然后
require
控制器引入视图模板,渲染html
功能
- 可以根据视图名找到视图文件;
- 可以优雅的将变量的值传递给视图;
service/View.php
视图装载器
# composer配置自动加载
"autoload": {
"classmap": [
"services",
]
}
# 更新
$ php73 composer.phar dump-autoload
# composer 文件变化
autoload_classmap.php文件 映射 classMap
'View' => $baseDir . '/services/View.php',
autoload_static.php文件 映射 classMap
'View' => __DIR__ . '/../..' . '/services/View.php',
View
视图装载类View::make($viewName)
静态方法。接受视图名称作为参数,实例化View
类的对象self::getFilePath($viewName)
判断视图文件是否存在;Exception
视图不存在则抛出异常;new View($viewFilePath)
如果试图存在,则返回视图对象;
$this->with($key, $value = null)
变量装载- 可以优雅的给这个
View
对象插入要在视图里调用的变量;
- 可以优雅的给这个
$this->withKey($value)
变量装载- 实现原理
__call($method, $parameters)
魔术方法; - 例如 withPageTitle($value)将采用蛇形的命名,转化为
$page_title
变量使用; - 例如 withTitle($value)将采用蛇形的命名,转化为
$title
变量使用;
- 实现原理
- 控制器
Controller
中的视图变量$this->view
则是基于BaseController
中- 父类
BaseController.php
会在析构函数__destruct()
中处理视图的加载; extract($data)
变量加载。 视图要用到的变量;require($viewFilePath)
视图文件。将最终运算结果发送给浏览器;
- 父类
- 请求入口
index.php
- 启动器
bootstrap.php
- 自动加载 (引入autoload文件)
- 初始化工作(例如数据库的一些配置等)
- 加载路由 (最后加载路由去分发请求)
- 启动器
引用同laravel一样的错误信息提示,
filp/whoops
- filp/whoops
- 依赖于 psr/log
- 文档
【filp/whoops】
- 安装
# 安装路由库
{
"require": {
"filp/whoops": "*"
}
}
# 更新
$ php73 composer.phar update
# 注意一下
vendor/composer下文件的变化
- 使用
// 【启动器 bootstrap.php】
// 错误提示 whoops库引入
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
// 【路由 routes.php】
// 采用错误页面显示404
Macaw::$error_callback = function() {
throw new Exception("路由无匹配项 404 Not Found");
};
-
测试
-
访问到一个不存在的路由,观察页面报错内容,已经是同Larvel一样的报错信息
services
目录
View.php
视图装载器Mail.php
邮件发送器RedisCache.php
RedisCache 驱动类
【nette/mail】
- 安装
# 安装路由库
{
"require": {
"nette/mail": "*"
}
}
# 更新
$ php73 composer.phar update
# 注意一下文件变化
vendor/composer
vendor/nette/mail
vendor/nette/utils
功能
- 核心是给 一批邮件地址 发送邮件内容。一个是目标地址,一个是发送内容。
# composer配置自动加载
"autoload": {
"classmap": [
"services",
]
}
# 更新
$ php73 composer.phar dump-autoload
# composer 文件变化
- 组件:类。
Mail
组件邮件发送类。继承Nette\Mail\Message
类;
- 配置:
config/mail.php
。- 关于host, 不同的邮件服务商不同,例如qq是
smtp.qq.com
、163是smtp.163.com
; - 关于密码,QQ服务,需要在邮件设置->服务中,设置IMAP/SMTP服务,开通第三方授权码登陆;
- 关于host, 不同的邮件服务商不同,例如qq是
- 组件:构造函数。
Mail::to($to)
静态方法。接受要接收的邮件作为参数,并且Mail
类的对象,返回$this
;__construct()
构造函数中,默认设置config/mail.php
配置文件中的username
为发件人(调用Message addTo()
);to($to)
支持字符串,也支持数组,可以发送一封或者多封(调用Message setFrom()
);
- 组件:相关方法。
Mail
的公共方法from()
、title()
、content()
封装了原来的Message
的相关方法,加入相关异常判断;- 设置发件人:
Mail from()
=>Message setFrom()
- 设置邮件标题:
Mail title()
=>Message setSubject()
- 设置邮件内容:
Mail content()
=>Message setHTMLBody()
(可以富文本)
- 控制器:信息装载。
$this->mail
成员变量,用来保存邮件组装的信息- 在方法中任意为止,可以将
Mail::to()
设置的邮件信息对象赋值给,控制器成员变量$this->mail
- 控制器:发送邮件。
- 所有控制器,都继承基类
BaseController.php
; - 父类
BaseController.php
会在析构函数__destruct()
中处理邮件的发送; - 单例模式。实例化邮件发送类。
new Nette\Mail\SmtpMailer($mail->config)
; - 然后将邮件要发送的信息,
send()
出去; - 稍等一会,邮件发送成功;
- 所有控制器,都继承基类
Redis
是一个开源的内存数据库服务器。可以用Redis
作为高速缓存,存放系统经常需要访问的数据,但它作用远不止于此,还要看你怎么使用它!
- 字符串(strings)
- 散列(hashes)
- 列表(lists)
- 集合(sets)
- 有序集合(sorted sets)
redis
官方推荐的php客户端是predis
和phpredis
。
phpredis
是使用 c 写的 php 扩展;predis
是使用纯 php 写的。
这里选择predis
,使用composer
安装方便一些;
当然事先你需要安装 redis服务,以及php的redis扩展,这些你应该都知道~
【predis/predis】
- 安装
# 安装路由库
{
"require": {
"predis/predis": "*"
}
}
# 更新
$ php73 composer.phar update
# 注意一下文件变化
vendor/composer
功能
创建Redis缓存类
services/RedisCache.php
【报错】
如果发现报错 Non-static method Redis::set() cannot be called statically
则可能是你PHP安装的有Redis
扩展,造成了冲突,所以可以换一个类库的名字,如 RedisCache
# composer配置自动加载
"autoload": {
"classmap": [
"services",
]
}
# 更新
$ php73 composer.phar dump-autoload
# composer 文件变化
- 组件:类
RedisCache
组件缓存设置类
- 配置:
config/redis.php
- 组件:相关方法
set($key, $value, $time=null, $unit=null)
可以设置过期时间,并且设置不同的单位- 每次执行方法都需要实例化一个对象,
new \Predis\Client();
这里也需要进行优化
【 物理CPU个数查看 】
[root@Centos7 apps]# cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
1
【 每个物理封装CPU的物理核数 】
[root@Centos7 apps]# cat /proc/cpuinfo| grep "cpu cores"| uniq
cpu cores : 4
【 查看cpu运行模式 】
[root@Centos7 apps]# getconf LONG_BIT
64
【 查看服务器CPU型号 】
[root@Centos7 apps]# cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq
Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
【 查看服务器型号指令 】
[root@Centos7 apps]# dmidecode -s system-product-name
VirtualBox
【 内核信息查询 】
[root@Centos7 apps]# uname -a
Linux Centos7 3.10.0-1160.11.1.el7.x86_64 #1 SMP Fri Dec 18 16:34:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
【 内存及使用情况查询 】
[root@Centos7 apps]# free -h
total used free shared buff/cache available
Mem: 3.7G 708M 2.7G 8.7M 280M 2.8G
Swap: 2.0G 0B 2.0G
PFC
、Yaf
分别在本地进行压测情况
[root@Centos7 ~]# ab -n1000 -c1 http://pfc.tacks.com/home/echotest
Requests per second: 39.11 [#/sec] (mean)
[root@Centos7 ~]# ab -n1000 -c100 http://pfc.tacks.com/home/echotest
Requests per second: 107.18 [#/sec] (mean)
[root@Centos7 ~]# ab -n1000 -c200 http://pfc.tacks.com/home/echotest
Requests per second: 64.42 [#/sec] (mean)
=====================================================================================
[root@Centos7 ~]# ab -n1000 -c1 http://yaf.tacks.com/home/echotest
Requests per second: 32.10 [#/sec] (mean)
[root@Centos7 ~]# ab -n1000 -c100 http://yaf.tacks.com/home/echotest
Requests per second: 76.45 [#/sec] (mean)
[root@Centos7 ~]# ab -n1000 -c200 http://yaf.tacks.com/home/echotest
Requests per second: 64.29 [#/sec] (mean)