一、PHP文件上传实战
(一)文件上传相关知识点:
1.预定义变量:$_FILES
-包含文件上传的所有信息(name,type,tmp_name,error,size)
- name:上传文件的名字
- type:上传文件的格式集
- tmp_name:上传后的文件临时储存位置
- error:上传文件的错误信息
0:文件上传成功
1:上传文件超过限制大小
2:上传文件超过表单限制大小
3:上传文件不完整
4:没有文件上传
5:未定义
6:找不到临时文件
7:写入失败,目录没有写入权限
- size:文件的尺寸,用字节表示
2.函数:
move_uploaded_file('临时文件',"新文件")
:将上传文件移动到新的位置;临时文件和新文件都有完整的路径和文件名字is_uploaded_file("文件名字")
:一般用来判断临时文件是否是由上传生成的;strstr($str,'分割符');
获取分隔符以后的所有字符(包含分隔符);如果由第三个参数为true时,获取分割符以前的所有字符(不包含分隔符);
(二)文件上传实战案例
封装的上传文件函数 common.php
<?php
/*
封装文件上传函数
*/
function uploadFile(array $fileInfo,$uploadPath="./uploads/",$flag = true,array $allowExts=['jpg','jpeg','png','wbmp','gif'],$maxSize="2MB"){
if(!empty($fileInfo)){
if ($fileInfo['error']>0) {
switch($fileInfo['error']) :
case 1:
$res['mess'] = '文件超过`php.ini`中`upload_max_filesize`值<br>';
break;
case 2:
$res['mess'] = '文件大小超过表单中`MAX_FILE_SIZE`指定的值<br>';
break;
case 3:
$res['mess'] = '文件只有部分被上传<br>';
break;
case 4:
$res['mess'] = '没有文件被上传<br>';
break;
case 6:
$res['mess'] = '找不到临时文件夹<br>';
break;
case 6:
$res['mess'] = '文件写入失败<br>';
break;
default:
$res['mess'] = 'Error: 系统错误<br>';
break;
endswitch;
return $res['mess'];
}else{
// 按照指定字符拆分为数组
$temp = explode(".",$fileInfo["name"]);
// 获取文件后缀名
$extension = array_pop($temp);
if(!in_array($extension, $allowExts)){
$res['mess'] = ("该文件格式占不支持!");
}
$maxSize = '2MB';
// 检查文件上传大小 需小于2MB
if($fileInfo["size"] > intval($maxSize)*1024*1024){
$res['mess'] = ("图片超大,请上传小于{$maxSize}的图片!");
}
if($flag){
// 检测图片是否合法
if(!getimagesize($fileInfo['tmp_name'])){
$res['mess'] = ("不是真实图片内容,get out~");
}
}
// 检测图片是否通过http post方式上传
if(!is_uploaded_file($fileInfo['tmp_name'])){
$res['mess'] = ("上传方式错误:请使用http post方式上传");
}
// 如果以上发生错误,就直接return结束返回错误内容
if(!empty($res['mess'])){
return $res['mess'];
}
// 检测uploads文件夹是否存在
// 如果没有 uploads 目录,你需要创建它,uploads 目录权限为 777
if(!file_exists($uploadPath))
{
// 不存在则创建uploads文件夹
mkdir($uploadPath,0777,true);
chmod($uploadPath,0777);
}
// 判断当前目录下的 uploads 目录是否存在该文件
if (file_exists($uploadPath . $fileInfo["name"]))
{
echo $fileInfo["name"] . " already exists. ";
}else{
$tmpname = $fileInfo['tmp_name']; // 临时文件名称
$name = $fileInfo['name']; // 文件的原名称
$file_name = md5(date('YmdHis')).rand(100,999).'.'.$extension; // 避免文件重名,更改文件名称
$fileRealPath = $uploadPath.$file_name;
// 检测是否上传成功
if(move_uploaded_file($tmpname, $fileRealPath)){
$res['mess'] = $name."图片上传成功!";
$res['imgurl'] = $fileRealPath;
}else{
$res['mess'] = $name."图片上传失败!请检查网络状况……";
}
return $res;
}
}
}
}
//多文件处理 重新刷洗 整合上传文件的信息
function upload()
{
// 将如下三维数组拆分为二维数组,即将三维数组中的每一项拆分成独立的数组
$files = [];
foreach($_FILES as $file){
for ($i=0; $i < count($file['name']); $i++) {
$files[$i]['name'] = $file['name'][$i];
$files[$i]['type'] = $file['type'][$i];
$files[$i]['tmp_name'] = $file['tmp_name'][$i];
$files[$i]['error'] = $file['error'][$i];
$files[$i]['size'] = $file['size'][$i];
echo "<br>";
}
}
return $files;
}
upload.php 多文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PHP多文件上传</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<fieldset>
<legend>多文件上传</legend>
<input type="file" name="file[]" multiple capture accept="image/*">
<input type="submit" value="上传">
</fieldset>
</form>
</body>
</html>
<?php
require 'common.php';
if(!empty($_FILES)){
// printf('<pre>%s</pre>',print_r($_FILES,true));//三维数组 文件信息散开
// echo '<hr>';
$files = upload();
// printf('<pre>%s</pre>',print_r($files,true));
foreach ($files as $fileInfo) {
$res = uploadFile($fileInfo);
echo '<p style="color:green">'.$res['mess'].'</p>';
$uploadFiles[] = $res['imgurl'];
}
printf('<pre>%s</pre>',print_r($uploadFiles,true));
var_dump(json_encode($uploadFiles));
}
?>
二、MVC设计模式的原理和实现
MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式。
MVC把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
MVC的目的是实现一种动态的程序设计,便于后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。
MVC各部分的职能:
- 模型Model – 管理大部分的业务逻辑和所有的数据库逻辑。模型提供了连接和操作数据库的抽象层。
- 控制器Controller - 负责响应用户请求、准备数据,以及决定如何展示数据。
- 视图View – 负责渲染数据,通过HTML方式呈现给用户。
<?php
//模型层:当前页面要显示的数据
$pdo = new PDO('mysql:host=localhost;dbname=mydb','root','root');
$users = $pdo->query('SELECT `sno`,`sname`,`ssex`,`sbirthday` FROM `student` order by sno asc LIMIT 10')->fetchAll(PDO::FETCH_ASSOC);
?>
<!-- 视图层 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学生信息表</title>
<link rel="stylesheet" href="style.css">
<style>
table {border-collapse: collapse; border: 1px solid;text-align: center; width: 500px;height: 150px;width: 600px;}
caption {font-size: 1.2rem; margin-bottom: 10px;}
thead tr:first-of-type { background-color:lightblue;}
td,th {border: 1px solid; padding:5px}
</style>
</head>
<body>
<table border="1" cellspacing="0" cellpadding="5">
<caption>用户信息表</caption>
<thead>
<tr>
<td>学号</td>
<td>姓名</td>
<td>性别</td>
<td>出生日期</td>
<td>操作</td>
</tr>
</thead>
<tbody>
<?php foreach($users as $user):?>
<tr>
<td><?= $user['sno']?></td>
<td><?= $user['sname']?></td>
<td><?= $user['ssex']?></td>
<td><?= $user['sbirthday']?></td>
<td><button>删除</button> <button>编辑</button></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</body>
</html>
MVC是最经典,也是最流行的Web项目设计思想
M:(Model)模型层,用来实现也数据库相关的操作,在PHP中负责数据管理,数据生成。
V:(View)视图层,用来实现用户界面与数据展示,在PHP中负责输出,处理如何调用模板、需要的资源文件。
C:(Controller) 控制器层,根据视图层的要求选择合适的模型,在PHP中根据请求决定调用的视图及使用的数据。
一个典型的Web MVC流程:
- Controller截获用户发出的请求;
- Controller调用Model完成状态的读写操作;
- Controller把数据传递给View;
- View渲染最终结果并呈献给用户。
MVC的典型目录结构如下:
/*
├─www # 网站根目录
│ ├─controller # 控制器目录
│ │ ├─DemoController.php # Demo控制器
│ ├─model # 模型目录
│ │ ├─model.php # model模型
│ ├─view # 视图目录
│ │ ├─index.php # index视图
│ ├─index.php # 入口文件
*/
规定代码的规范:
- MySQL的表名需小写或小写加下划线,如:
item
,car_orders
。 - 模块名(Models)需用大驼峰命名法,即首字母大写,并在名称后添加
Model
,如:ItemModel
,CarModel
。 - 控制器(Controllers)需用大驼峰命名法,即首字母大写,并在名称后添加
Controller
,如:ItemController
,CarController
。 - 方法名(Action)需用小驼峰命名法,即首字母小写,如:
index
,indexPost
。 - 视图(Views)部署结构为控制器名/行为名,如:
item/view.php
,car/buy.php
。
MVC文件夹基本结构如下:
模型Model
在面对复杂问题时,面向对象编程可以更好的描述现实中的业务逻辑,所以MVC的程序也是通过面向对象的方式实现的m模型是处理数据的,而数据是存储在数据库里的。在项目中,所有对数据库的直接操作,都应该封装到一个数据库操作类中。
运用学过的面向对象、单例模式、PDO等相关知识,就可以封装一个PDO的数据库操作类。
封装一个PDO数据库操作类(Db.php)
<?php
/**
* PDO-MySQL数据库操作类
*/
class MySQLPDO{
//数据库默认连接信息
private $dbConfig = array(
'db' => 'mysql', //数据库类型
'host' => 'localhost', //服务器地址
'port' => '3306', //端口
'user' => 'root', //用户名
'pass' => 'root', //密码
'charset' => 'utf8', //字符集
'dbname' => 'mydb', //默认数据库
);
//单例模式 本类对象引用
private static $instance;
//PDO实例
private $db;
/**
* 私有构造方法
* @param $params array 数据库连接信息
*/
private function __construct($params){
//初始化属性
$this->dbConfig = array_merge($this->dbConfig,$params);
//连接服务器
$this->connect();
}
/**
* 获得单例对象
* @param $params array 数据库连接信息
* @return object 单例的对象
*/
public static function getInstance($params = array()){
if(!self::$instance instanceof self){
self::$instance = new self($params);
}
return self::$instance; //返回对象
}
/**
* 私有克隆
*/
private function __clone() {}
/**
* 连接目标服务器
*/
private function connect(){
try{
//连接信息
$dsn = "{$this->dbConfig['db']}:host={$this->dbConfig['host']};
port={$this->dbConfig['host']};
dbname={$this->dbConfig['dbname']};
charset={$this->dbConfig['charset']}";
//实例化PDO
$this->db = new PDO($dsn,$this->dbConfig['user'],$this->dbConfig['pass']);
//设定字符集
$this->db->query("set names {$this->dbConfig['charset']}");
}catch (PDOException $e){
//错误提示
die("数据库操作失败:{$e->getMessage()}");
}
}
/**
* 执行SQL
* @param $sql string 执行的SQL语句
* @return object PDOStatement
*/
public function query($sql){
$rst = $this->db->query($sql);
if($rst===false){
$error = $this->db->errorInfo();
die("数据库操作失败:ERROR {$error[1]}({$error[0]}): {$error[2]}");
}
return $rst;
}
/**
* 取得一行结果
* @param $sql string 执行的SQL语句
* @return array 关联数组结果
*/
public function fetchRow($sql){
return $this->query($sql)->fetch(PDO::FETCH_ASSOC);
}
/**
* 取得所有结果
* @param $sql string 执行的SQL语句
* @return array 关联数组结果
*/
public function fetchAll($sql){
return $this->query($sql)->fetchAll(PDO::FETCH_ASSOC);
}
}
// 调用方式
// 连接数据库
// $pdo = MySQLPDO::getInstance();
每个模型都是一个模型类,对表的所有操作,都要放到模型类中完成。
- 创建基础模型类:Model.php
- 创建Student模型类 : studentModel.php
<?php
require __DIR__.'./Db.php';
/**
* 基础模型类
*/
class Model{
protected $db; //保存数据库对象
public function __construct(){
$this->initDB(); // 初始化数据库
}
private function initDB(){
//配置数据库连接信息
$dbConfig = array('dbname'=>'mydb');
//实例化数据库操作类
$this->db = MySQLPDO::getInstance($dbConfig);
}
}
?>
<?php
/**
* student表的操作类,继承基础模型类
*/
require __DIR__.'./Model.php';
class studentModel extends Model{
/* 查询所有学生 */
public function getAll(){
$data = $this->db->fetchAll('select * from `student` order by `sno` desc limit 10');
return $data;
}
/* 查询指定id的学生 */
public function getByID($sno){
$data = $this->db->fetchRow("select * from `student` where sno={$sno}");
return $data;
}
}
index.php 测试 实例
<?php
require 'studentModel.php';
echo "<pre>";
// var_dump((new studentModel)->getAll());
var_dump((new studentModel)->getByID('106'));
?>
视图 View
根据控制器类要实现的功能方法,创建视图文件
- 查看所有学生信息视图文件:
<?php
/*
视图层:展示数据
*/
class View{
public function fetch($data){
$table= "<style>
table {border-collapse: collapse; border: 1px solid;text-align: center; width: 500px;height: 150px;width: 600px;}
caption {font-size: 1.2rem; margin-bottom: 10px;}
thead tr:first-of-type { background-color:lightblue;}
td,th {border: 1px solid; padding:5px}
</style>";
$table .= '<table border="1" cellspacing="0" cellpadding="5">
<caption>学生信息表</caption>
<thead>
<tr>
<td>学号</td>
<td>姓名</td>
<td>性别</td>
<td>出生日期</td>
<td>操作</td>
</tr>
</thead>
<tbody>';
foreach ($data as $item) {
$table .= '<tr>';
$table .= "<td>{$item['sno']}</td>";
$table .= "<td>{$item['sname']}</td>";
$table .= "<td>{$item['ssex']}</td>";
$table .= "<td>{$item['sbirthday']}</td>";
$table .= "<td><button>删除</button> <button>编辑</button></td>";
$table .= '</tr>';
}
$table .= '</tbody></table>';
return $table;
}
}
?>
index.php 测试 实例
<?php
require '../model/studentModel.php';
require './view.php';
$data = (new studentModel)->getAll();
echo (new View)->fetch($data);
?>
控制 Controllers
控制器是MVC应用程序中的指挥官,它接收用户的请求,并决定需要调用哪些模型进行处理,再用相应的视图显示从模型返回的数据,最后通过浏览器呈现给用户。
控制器是根据模块创建的,即每个模块对应一个控制器类 创建StudentController.class.php控制器类,实现的功能:
- 查看所有学生信息
- 查看指定学生信息
<?php
/**
* 学生模块控制器类
*/
require '../model/studentModel.php';
require '../view/View.php';
class studentController{
public function index()
{
//1. 获取数据 实例化模型,取出数据
$model = new studentModel();
$data = $model->getAll();
//2.模板渲染
$view = new View();
return $view->fetch($data);
}
}
//客户端 代码
echo = (new studentModel)->index();
?>
MVC设计模式
依赖注入
服务容器Container
Facade门面技术
单例模式
(把构造方法私有化) 防止重复实例化,避免大量的new操作,减少消耗系统和内存的资源,使得有且仅有一个实例对象
依赖注入
依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作方法中一旦对参数进行对象类型约束则会自动触发依赖注入,由于访问控制器的参数都来自于URL请求,普通变量就是通过参数绑定自动获取,对象变量则是通过依赖注入生成。
通俗来说,就是你当前操作一个类,但是这个类的某些方法或者功能不是单单只靠这个类就能完成的,而是要借助另一个类的才能完成的。
最直接的标志就是传参数据为对象的时候。
严格来说,你目前操作的是两个类,这两个类之间相互依赖,传参的方式叫注入。
<?php
/**
* 学生模块控制器类
*/
require '../model/studentModel.php';
require '../view/View.php';
class studentController{
// 将依赖的外部对象,在操作方法中注入
// public function index(Model $model,View $view){
// // 1.获取数据
// $data = $model->getAll();
// // 2.模板渲染
// return $view->fetch($data);
// }
protected $model;
protected $view;
// 将注入的转移到构造方法中
//通过构造方法将外部对象初始化,实现了外部依赖注入的对象在类内部的共享/复用
public function __construct(Model $model,View $view){
$this->model = $model;
$this->view = $view;
}
public function index(){
// 1.获取数据
$data = $this->model->getAll();
// 2.模板渲染
return $this->view->fetch($data);
}
}
//客户端 代码
$model = new studentModel;
$view = new view;
$ctrl = new studentController($model,$view);
// echo $ctrl->index($model,$view);
echo call_user_func_array([$ctrl,'index'],[$model,$view]);
?>
服务容器
容器来更方便的管理类依赖及运行依赖注入,服务容器就是一个自动产生类的工厂。
container
是一个简单的服务容器里面有bind
,make
两个方法bind
是向容器中绑定服务对象。make
则是从容器中取出对象。
在bind
方法中需要传入一个 concrete
我们可以传入一个实例对象或者是一个闭包函数。
可以看到这全使用的是闭包函数,其实也可以这样写
$sms = new SeedSMS();$message->bind('SMS',$sms);
这种写法与闭包相比的区别就是我们需要先实例化对象才能往容易中绑定服务。而闭包则是我们使用这个服务的时候才去实例化对象。可以看出闭包是有很多的优势的。
*make*
方法就从容器中出去方法。通过 *call_user_func_array*
返回一个对象。
https://www.kancloud.cn/manual/thinkphp6_0/1037489
<?php
/**
* 容器:container :依赖注入的类统一由容器进行管理,容器 数组
*
* 如果当前类依赖的对象很多, 可以将这些依赖的对象 , 类,闭包,放到一个"服务容器"中进行统一管理 bind make
*/
require '../model/studentModel.php';
require '../view/View.php';
// 服务容器:一个自动生成类/对象的工厂
class Container{
// 1.对象容器 数组
protected $container=[];
/**
* 2.往对象容器数组中去绑定 对象
* 绑定一个类、闭包、实例、接口实现到容器
* @access public
* @param string|array $abstract 类标识、接口的别名 alias
* @param mixed $concrete 要绑定的类、闭包或者实例
* @return $this
*/
public function bind($abstract, Closure $concrete){
$this->container[$abstract]=$concrete;
}
//3. 从对象容器中取出 对象并调用
public function make($abstract,$params=[])
{
return call_user_func_array($this->container[$abstract],$params);
}
}
$container=new Container();
// 绑定一个闭包到容器,我们使用这个model对象的时候 再去实例化
$container->bind("Model",function(){
return new studentModel();
});
$container->bind("View",function(){
return new View();
});
class Controls{
public function show(Container $container){
// var_dump($container);
// var_dump($container->make("Model"));
$data=$container->make("Model")->getAll();
// var_dump($data);
return $container->make("View")->fetch($data);
}
}
//客户端代码
echo (new Controls())->show($container);
//对多个对象的依赖转为对一个容器的依赖, 对多个对象使用服务容器进行统一接管 takeover
Facade门面技术
门面为容器中的(动态)类提供了一个静态调用接口,相比于传统的静态方法调用, 带来了更好的可测试性和扩展性,你可以为任何的非静态类库定义一个facade
类。
https://www.cnblogs.com/wenly/articles/1211043.html
https://www.kancloud.cn/manual/thinkphp6_0/1037491
<?php
require '../model/studentModel.php';
require '../view/View.php';
//服务容器
class Container{
protected $container=[];
public function bind($abstract, Closure $concrete){
$this->container[$abstract]=$concrete;
}
public function make($abstract,$params=[])
{
return call_user_func_array($this->container[$abstract],$params);
}
}
$container=new Container();
// 绑定一个闭包到容器,我们使用这个model对象的时候 再去实例化
$container->bind("Model",function(){
return new studentModel();
});
$container->bind("View",function(){
return new View();
});
//门面技术
class Facade {
//为容器中的类提供一种静态调用方式
protected static $container;
public static function initialize(Container $container)
{
static::$container = $container;
}
}
//模型类成员的访问静态化(给container中的成员model类套一个静态访问马甲)
class UserModel extends Facade{
public static function getData()
{
//后期静态绑定
return static::$container->make('Model')->getAll();
}
}
class UserView extends Facade{
public static function fetch($data)
{
return static::$container->make('View')->fetch($data);
}
}
class Controller {
public function __construct(Container $container){
Facade::initialize($container);
}
public function index()
{
$data = UserModel::getData();
return UserView::fetch($data);
}
}
//客户端代码
echo (new Controller($container))->index();
控制器总结:
1.服务容器与Facade都是简化客户端(也就最终使用者)的操作
2.服务容器:是在调用前,提前把类实列保存到一个关联数组中,使用直接调用即可;通常通过一个类来实现,把需要使用的通过类函数绑定到类属性数组中,调用时,通过类函数直接调用即可
3.Facade技术主要用来简化类的调用;通过把一般类的方法和函数静态化,简化调用步骤(省去类实列化步骤);
4.Facade(门面技术)用到的初始化方法:initialize(){}
- initialize:类的任何方法和属性被调用是都会触发该函数;
- construct:类在实列化时才会被触发;
5.Facade(门面技术):主要是通过门面类把所有的类中的方法套壳静态化方便后面使用者调用;
6.控制类需要调用的类->把类实例化后绑定到服务容器中->门面技术(通过子类继承父类的属性,把服务容器类绑定门面类属性中中,然后子类把所需要的方法静态化)->控制类(就可以通过静态方法调用相关类方法)
php常见五种设计模式:https://www.cnblogs.com/cxx8181602/p/9392523.html