玩一下
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基础控制器HomeControllershome控制器
# 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模型目录 -
Articlearticle模型 -
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.phpRedisCache 驱动类
【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)