方法拦截器+静态方法拦截器
方法拦截器类似属性拦截器,属性拦截器当属性不存在时或者不公开是,调用set跟get方法,set写入 get查询,方法拦截器公开的方法拦截器使用call,在本类中不存在某个方法时,触动方法拦截器,call,或者当本类中静态方法不存在时,触动静态方法拦截器__callstatic来执行操作,方法拦截器传入的值也是方法名称,第二个传入的值是方法的值,值可能为多个,所以是以[数组]的方式存在的。
代码部分:
<?php
// 方法拦截器 方法拦截器类似于属性拦截器,属性拦拦截器是set跟get ,方法拦截器是会调用的是__call __callStatic
/**
* 根据方法拦截器 搞一份事件委托和构造器查询
*
*
*/
class User
{
public function normal()
{
return __METHOD__;
}
//当访问不存在的方法时,或不可见的方法时,调用__Call方法
public function __Call($method, $arges) //第一是传进来的是第一个不可见的方法名称, 第二个是数组
{
var_dump($method);
echo '<br>';
var_dump($arges);
echo '<br>';
// echo 111;
printf('调用当前类中不存在的普通方法%s,参数列表为[%s]<br>', $method, implode(',', $arges)); // $arges传进来的是数组,可以通过implode()来分割成字符串,有俩值implode('分割符号',$args分割数组的变量)
}
public static function __callStatic($name, $arguments)
{
printf('目前是静态方法,当前调用的类不存在的静态普通方法%s,参数列表[%s]<br>', $name, implode(',', $arguments));
}
}
$u = new User;
echo $u->normal();
$u->login('老王登录了网站,想偷取数据', '是真的', '他还拿了你的保险箱');
User::ad('点击一下广告', '你就有钱赚', '到账50元');
// $u::ad('点击一下广告', '你就有钱赚', '到账50元');
方法拦截器之事件委托:
方法拦截器事件委托,也是类似属性拦截器的拦截,当工作类,也就是委托方,没有这个方法的时候,将启动事件委托,示例中的代码,是查询数据库的示例,并不是只能在查询数据库中用到,在流程类中,初始化类成员,在被委托方中通过回调call_user_func()的方法,进行对函数的回调,回调到流程类中,通过用户传入的初始值传入到拦截器中,拦截器通过回调函数(因为拦截器收到的是俩参数,一个是拦截的方法名,一个是拦截的方法参数,)传入到拦截器中,通过回调方法,回调到了流程类中,重新赋值执行。
代码部分:
<?php
/**
* 事件委托 请求委托 访问类中不存在的成员方法,会被魔术方法拦截,将请求重定向给别的对象成员方法类处理
*
* 委托是指一个对象转发或者委托一个请求给另外一个对象,被委托的一方替原来的对象处理请求。
* 委托比继承更加灵活 ,父类与子类的关系是固定的,只能单继承,但是请求可以委托给多个对象
*/
//被委托的类 数据库查询构造器
class Query
{
// 创建pdo对象的唯一实例
private static $db;
protected $table;
protected $field;
protected $limit;
//private 私有的 阻止此类在外部进行实例化
private function __construct()
{
}
//完成pdo的封装操作 后面进行下一步操作
static function connect($dsn, $user, $pwd)
{
if (is_null(static::$db)) //检测变量是否为 NULL 延迟绑定
{
static::$db = new pdo($dsn, $user, $pwd);
// return static::$db;
}
return new static;
}
public function table($table)
{
$this->table = $table;
return $this;
}
public function field($field)
{
$this->field = $field;
return $this;
}
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
public function getSql()
{
return sprintf('SELECT %s FROM %s LIMIT %d', $this->field, $this->table, $this->limit);
}
public function select()
{
return static::$db->query($this->getSql())->fetchAll(pdo::FETCH_ASSOC);
}
}
//委托方
class Db
{
static function __callStatic($name, $arguments)
{
$dsn = 'mysql:host=localhost;dbname=phpcn;charset=utf8;port=3306';
$user = 'root';
$pwd = '123456';
//获取到被委托的类 Query实例
$q = Query::connect($dsn, $user, $pwd);
return call_user_func([$q, $name], ...$arguments); //call_user_func第一个参数 或者说是方法 是被调用的回调函数,其余参数是回调函数的参数。
// 上面的$name在传入的时候不就是table 参数就是mj_course_cat
}
}
//客户端代码
$res = Db::table('mj_course_cat')->field('cat_id,name')->limit(10)->select();
var_dump($res);
看下面截图吧!!
数据(数据库)分页的提取
分页
1.首先赋值一个变量,值为每页取的数据量
2.通过超全局变量get,获取用户通过get传过来的变量,如果通过get传过来为0,那就默认为1
3.计算数据的偏移量 计算方式 页数-1*条数,比如第一次进来,就是偏移0,第二次偏移8,第三次偏移16,也就是查询的。
4.获取所有数据 将偏移量给数据库,也就是从第几个拿,拿几条
5.计算获取的总条数
6.计算总页数,总共有多少页
分页提取的模型部分,就是php处理文件部分ceil()
向上取整
<?php
$pdo = new pdo('mysql:host=localhost;charset=utf8;port=3306;dbname=phpcn', 'root', '123456', [PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]);
//每页取8条数据
$pageSize = 8;
// get是前端用户传过来的操作 默认是1,也就是当前访问的第几页
$page = $_GET['page'] ?? 1;
//计算一个偏移量 offset
$offset = ($page - 1) * $pageSize;
// 获取所有的数据
var_dump($offset);
$sql = "SELECT `cou_id`,`title`,`pic`,`info`,`add_time` FROM `mj_course_lists` ORDER BY `cou_id` DESC LIMIT {$offset},{$pageSize}";
$lists = $pdo->query($sql)->fetchAll();
//获取总条数
$sql1 = "SELECT COUNT(`cou_id`)AS `sum` FROM `mj_course_lists`";
$total = $pdo->query($sql1)->fetch()['sum']; //那一维数组的sum下面的值
var_dump($total);
// ceil()向上取整 计算总页数 也就是总条数除以每页数据
$pages = ceil($total / $pageSize);
var_dump($pages);
视图层部分,也就是前端那部分:
1.前端,通过总条数,fetch数组,然后forech遍历拿到数据,拿到数据,遍历到前台,
2.分页数据,自增一个数据,数据小于总页数,也就是小于$pages的值即可
<? require 'pageData.php' ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/css/style.css">
<title>Document</title>
</head>
<body>
<table>
<caption>课程信息表</caption>
<thead>
<tr>
<td>编号</td>
<td>名称</td>
<td>封面</td>
<td>课程简介</td>
<td>创建时间</td>
<td>操作</td>
</tr>
</thead>
<tbody>
<!-- $lists 是在数据库拿到的遍历结果 在pageData.php中 通过fetchall拿到后用foreach遍历出来 -->
<?php foreach ($lists as $list) : ?>
<tr>
<!-- 一维数组 拿结果 -->
<td><?= $list['cou_id'] ?></td>
<td><?= $list['title'] ?></td>
<td><img style="width:100px" src=" <?= $list['pic'] ?>" alt="课程封面"></td>
<td><?= $list['info'] ?></td>
<!-- date(format,timestamp); -->
<!-- 返回将整数 timestamp 按照给定的格式字串而产生的字符串。如果没有给出时间戳则使用本地当前时间。换句话说,timestamp 是可选的,默认值为 time()。 -->
<td><?= date("Y-m-d H:m:s", $list['add_time']) ?></td>
<td><button>删除</button><button>编辑</button></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- 生成分页条 -->
<p>
<? for ($i = 1; $i <= $pages; $i++) : ?>
<!-- 通过for里面的i生成页数 赋值给jump也就是当前的页数和后面 2 3 4 5等页数 -->
<? $jump = sprintf('?page=%d', $i) ?>
<!-- 当i=page也就是用户传过来的参数时,将active赋值给$active,如果不是就为空 拿到了当前页面class -->
<? $active = ($i == $page) ? 'active' : '' ?>
<a class="<?= $active ?>" href="<?= $jump ?>"><?= $i ?></a>
<!-- 生成高亮分页条 -->
<? endfor ?>
</p>
</body>
</html>
文件上传
文件上传
1. php 关于文件上传的配置
文件上传项目项在
php.ini
中设置,常用的配置项有:
序号 | 配置项 | 默认值 | 描述 |
---|---|---|---|
1 | file_uploads |
On |
使 PHP 支持文件上传 |
2 | upload_tmp_dir |
/tmp |
设置上传文件的暂存区位置 |
3 | max_file_uploads |
20 |
单次请求时允许上传的最大文件数量 |
4 | max_execution_time |
30 |
设置脚本被解析器终止之前PHP最长执行时间(秒) ,防止服务器资源被耗尽 |
5 | max_input_time |
60 |
设置 PHP 通过 POST/GET/PUT 解析接收数据的时长(秒) |
6 | memory_limit |
128M |
设置脚本能够分配的最大内存容量 单位字节 1M等于1024K,一兆等于1048576字节 |
7 | post_max_size |
8M |
设置通过 POST方法提交的数据量上限 |
8 | upload_max_filesize |
2M |
设置上传的单个文件的数据量上限 |
memory_limit >= post_max_size 防止失控的脚本独占服务器内存,严重时使服务器崩溃
post_max_size > upload_max_filesize 因为后者只是限制通过file输入类型传递过来的数据量,不像post_max_size可以限制所有通过post方法上传的数据量上限
2. $_FILE
- 上传文件的描述信息,全部保存在系统全局变量
$_FILES
中 $_FILES
以二维数组形式保存:$_FILES['form_file_name']['key']
'form_file_name'
: 对应着表单中<input type="file" name="my_pic">
中name
属性值'key'
: 共有 5 个键名, 描述如下:
序号 | 键名 | 描述 |
---|---|---|
1 | name |
文件在客户端的原始文件名(即存在用户电脑上的文件名) |
2 | type |
文件的 MIME 类型, 由浏览器提供, PHP 并不检查它 |
3 | tmp_name |
文件被上传到服务器上之后,在临时目录中显示的临时文件名 |
4 | error |
和该文件上传相关的错误代码 |
5 | size |
已上传文件在客户端机器上的实际大小(单位为字节) |
- 文件上传错误信息描述
序号 | 常量 | 值 | 描述 |
---|---|---|---|
1 | UPLOAD_ERR_OK |
0 |
没有错误发生,文件上传成功 |
2 | UPLOAD_ERR_INI_SIZE |
1 |
文件超过php.ini 中upload_max_filesize 值 |
3 | UPLOAD_ERR_FORM_SIZE |
2 |
文件大小超过表单中MAX_FILE_SIZE 指定的值 |
4 | UPLOAD_ERR_PARTIAL |
3 |
文件只有部分被上传 |
5 | UPLOAD_ERR_NO_FILE |
4 |
没有文件被上传 |
6 | UPLOAD_ERR_NO_TMP_DIR |
6 |
找不到临时文件夹 |
7 | UPLOAD_ERR_CANT_WRITE |
7 |
文件写入失败 |
3. PHP文件上传函数
系统中提供了多个文件处理函数,php还提供了两个专门来处理文件上传功能的函数
序号 | 键名 | 描述 |
---|---|---|
1 | is_uploaded_file() |
用来检测文件是否是通过http post方法上传的,而不是系统上的一个文件。作用是防止潜在攻击者通过问题脚本访问并非用于交互的文件 |
2 | move_uploaded_file() |
将上传的文件从临时目录移动到最终位置 |
php单文件上传前端文件
前端文件需要注意的是enctype()函数的值,需要改成multipart/form-data,二进制上传
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
</head>
<body>
<!-- 当上传文件时 enctype值需要设置为multipart/form-data,文件时二进制 -->
<form action="doUpload.php" method="post" enctype="multipart/form-data">
<label for="avatar">头像</label>
<input type="file" name="avatar" id="avatar">
<button>文件上传提交</button>
</form>
</body>
</html>
php后端接收文件
php后端接收文件用超全局函数$_FILES
来接收前端发过来的参数值
下面使用中的函数及说明
函数名 | 函数用法 | 函数解释 |
---|---|---|
explode() | explode(‘分割符号’, 接收的文件名) | 第一个是分割的符号,第二个是接收的文件名,接收的值是一个数组 |
end() | end(接收的数组) | end($arr);如果数组内有多个值,将拿到数组的尾部最后一个值 |
array_shift() | array_shift(接收的数组) | array_shift($arr)跟上面的类似,只不过如果数组内有多个值,将拿到数组的头部第一个值 |
in_array() | in_array($ext, $exts) | in_array($ext, $exts),第一个参数是检查的类型,第二个是存在数组中存在的类型,检查接收的类型,是不是数组中存在的类型,如果不是,就返回一个错误 |
move_uploaded_file() | move_uploaded_file($tmp_name, $des); | move_uploaded_file($tmp_name, $des);第一个参数是需要处理的文件,也就是需要移动的文件,第二个参数是移动到的路径,移动到某个路径 |
date() | date(‘YmdHms’, time()) | date(‘YmdHms’, time())第一个是需要转换成的显示的格式,第二个是拿到的时间格式 |
<?php
//获取文件信息$__file
var_dump($_FILES);
$originalFilename = $_FILES['avatar']['name'];
$type = $_FILES['avatar']['type'];
$tmp_name = $_FILES['avatar']['tmp_name'];
$error = $_FILES['avatar']['error'];
$size = $_FILES['avatar']['size'];
var_dump($error);
switch ($error):
case 0:
echo '<p style="color:green">文件上传成功</p> ';
break;
case 1:
echo '<p style="color:red">文件超过`php.ini`中`upload_max_filesize`值</p>';
break;
case 2:
echo '<p style="color:red">文件大小超过表单中`MAX_FILE_SIZE`指定的值</p>';
break;
case 3:
echo '<p style="color:red">文件只有部分被上传</p>';
break;
case 4:
echo '<p style="color:red">没有文件被上传</p>';
break;
case 6:
echo '<p style="color:red">找不到临时文件夹</p>';
break;
case 6:
echo '<p style="color:red">文件写入失败</p>';
break;
default:
echo '<p style="color:red">系统错误</p>';
break;
endswitch;
// 检查文件格式
// explode()分割成数组,前面是分割的符号,后面是分割的文件名
$arr = explode('.', $originalFilename);
// end()拿到数组的尾部,也就是数组的最后一个 .就没了
$ext = end($arr);
//array_shift拿到数组的第一个 .就没了
$prefix = array_shift($arr);
$exts = ['png', 'jpg', 'jpeg', 'wbmp', 'gif'];
// in_array($ext, $exts) $ext 传过来的文件分割的尾部类型, $exts 数组内存在的类型
if (!in_array($ext, $exts)) {
return '非法文件类型';
}
// 检查大小
// `is_uploaded_file()` | 用来检测文件是否是通过http post方法上传的,而不是系统上的一个文件。作用是防止潜在攻击者通过问题脚本访问并非用于交互的文件
if ($error = 0) {
//移动暂存区的图片到服务器指定的文件目录
// 定义一个要移动的目录
$des = 'storage/';
// file_exists检查是否有这个路径文件 没有就创建一个
if (!file_exists($des)) {
mkdir($des, 0777, true);
}
$newName = date('YmdHms', time()) . md5($prefix) . '.' . $ext;
// move_uploaded_file(暂存区,目的地)
move_uploaded_file($tmp_name, $des);
}