PHP8.1.21版本已发布
vue8.1.21版本已发布
jquery8.1.21版本已发布

博客列表 > PHP-自定义异常类处理上传错误--2019年10月12日

PHP-自定义异常类处理上传错误--2019年10月12日

Victor的博客
Victor的博客 原创
2019年10月26日 18:39:36 1112浏览

10月12日:

1. 写一个自定义异常类来处理上传过程以及各种错误

2. (选做) 写一个与指定数据表绑定的类, 实现基本的模型功能,例如查询, 新增, 更新,删除等操作


【1】自定义异常类来处理上传过程以及各种错误

有时候我们需要使用自定义的异常类,对特定类型的异常进行处理。

自定义异常类需要继承自Exception类,并添加自定义的成员属性和方法即可。接下来通过一个文件上传实例进行学习。

一般按如下格式使用自定义异常类:

<?php
	/* 自定义的一个异常处理类,但必须是扩展内异常处理类的子类 */
	class MyException extends Exception{
		//重定义构造器使第一个参数 message 变为必须被指定的属性
		public function __construct($message, $code=0){
		    //可以在这里定义一些自己的代码
		    parent::__construct($message, $code);
		}	
		public function __toString() {        
            //重写父类方法,自定义字符串输出的样式
		    return __CLASS__.":[".$this->code."]:".$this->message."<br>";
		}
		public function errorInfo() {    
             //为这个异常自定义一个处理方法
		}
	}
    try { //使用自定义的异常类捕获一个异常,并处理异常 
         	//主程序,如果发生异常,通过throw语句抛出
	} catch (MyException $e) {        //捕获自定义的异常对象
		echo $e->errorInfo();  //通过自定义的异常对象中的方法处理异常
	}
	
?>

文件上传过程说明:

通常一个文件以HTTP协议进行上传时,将以POST请求发送至Web服务器,Web服务器接收到请求并同意后,用户与Web服务器将建立连接,并传输数据。一般文件上传过程中将会经过如下几个检测步骤:

  • javascript校验;

  • 服务端MIME类型检测

  • 服务端目录路径检测

  • 服务端文件扩展名检测

  • 服务端文件内容检测

1、PHP使用超全局变量$_FILES来处理文件上传

    $_FILES数组内容如下:
    $_FILES['myFile']['name'] 客户端文件的原名称。
    $_FILES['myFile']['type'] 文件的 MIME 类型,需要浏览器提供该信息的支持,例如"image/gif"。
    $_FILES['myFile']['size'] 已上传文件的大小,单位为字节。
    $_FILES['myFile']['tmp_name'] 文件被上传后在服务端储存的临时文件名,一般是系统默认。
    $_FILES['myFile']['error'] 和该文件上传相关的错误代码。    
            UPLOAD_ERR_OK
            值:0; 没有错误发生,文件上传成功。
            UPLOAD_ERR_INI_SIZE
            值:1; 上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。
            UPLOAD_ERR_FORM_SIZE
            值:2; 上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。
            UPLOAD_ERR_PARTIAL
            值:3; 文件只有部分被上传。
            UPLOAD_ERR_NO_FILE
            值:4; 没有文件被上传。
            值:5; 上传文件大小为0.

2、文件被上传结束后,默认地被存储在了临时目录中,这时必须将它从临时目录中删除或移动到其它地方,如果没有,则会被删除。也就是不管是否上传成功,脚本执行完后临时目录里的文件肯定会被删除。

所以在删除之前要用PHP的 copy() 函数将它复制到其它位置,此时,才算完成了上传文件过程。

3、本例中使用自定义异常类给出上传过程中的结果 和 错误信息,在上传文件的客户端页面中设置一个隐藏的ifram,用来接收结果 或 错误信息。通过一个定时轮询ifram中body内容的变化,来决定是否显示iframe,从而实现页面无跳转的上传与信息提示。

最终的运行效果如下图:

upload.jpg

catch(Exception $e)可以接受系统类exception和自定义的异常类。

catch(customException $e)只能接受throw抛出的自定义的异常类,不能接受系统类exception

代码实现如下:

实例 --- 用自定义异常类 处理上传文件的错误信息
<?php
class MyException extends Exception {
	public function __construct($message, $code = 0) {
		parent::__construct($message, $code);		
	}
	public function errorInfo() {
		include 'info.php';  //显示错误信息
	}
}
try {
	//使用自定义的异常类捕获一个异常,并处理异常
	// 配置上传参数
	$fileType = ['jpg', 'jpeg', 'png', 'gif'];
	$fileSize = 3145728;
	$filePath = '\uploads\\';
	$fileName = $_FILES['myfile']['name'];
	$tempFile = $_FILES['myfile']['tmp_name'];
	$uploadError = $_FILES['myfile']['error'];
	if ($uploadError != 0) {
		switch ($uploadError) {
		case 1:throw new MyException("上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值", 1001);
		case 2:throw new MyException('上传文档不允许超过3M', 1002);
		case 3:throw new MyException('上传文件不完整', 1003);
		case 4:throw new MyException('没有文件被上传', 1004);
		case 5:throw new MyException("文件大小为0", 1005);
		default:throw new MyException('未知错误', 1000);
		}
	}
	//通过扩展名判断文件类型
	@$extension = end(explode('.', $fileName));
	if (!in_array($extension, $fileType)) {
		throw new MyException('不允许上传' . $extension . ' 文件类型', 1006);
	}
	//为了防止同名覆盖, 将上传的文件重命名: md5+时间戳
	$fileName = date('YmdHis', time()) . md5(mt_rand(1, 99)) . '.' . $extension;
	//传文件
	$uploadPath = __DIR__ . $filePath;
	if (is_uploaded_file($tempFile)) {
		if (!file_exists($uploadPath) || !is_writable($uploadPath)) {
			throw new MyException('指定目录不存在且无法创建, 请检查目录', 1007);
		}
		if (move_uploaded_file($uploadPath . $fileName)) {
			echo '<strong><span style="color:red";>文件上传成功</span></strong>';
		} else {
			throw new MyException('文件无法移动到指定目录, 请检查目录权限', 1008);
		}
	} else {
		throw new MyException('非法操作', 1009);
	}
} catch (MyException $e) { //捕获自定义的异常对象
	echo $e->errorInfo(); //通过自定义的异常对象中的方法处理异常
}
?>
运行实例 »
点击 "运行实例" 按钮查看在线实例
实例  --- 客户端 上传页面 代码
<!DOCTYPE html>
<html>
<head>
	 <meta charset="UTF-8">
	 <script src="jquery-3.4.1.min.js"></script>
	<title>文件上传</title>
	<style type="text/css" media="screen">
		#tg {
			position: absolute;
			left:0;
			right:0;
			top:0;
			bottom:0;
			margin: auto;
		}
	</style>
</head>
<body>
	<form id="form1" action="upload.php" method="post" target="tg" enctype="multipart/form-data">
		<br>
		<input type="hidden" name="MAX_FILE_SIZE" value="3145728">
		<input type="file" name="myfile"><br><br>
		<input type="button" value="上传文件" onclick="upload()">
	</form>
	<iframe name="tg" id="tg" frameborder="no" border="0" style="display:none" width="500px" >
	</iframe> 
	<script type="text/javascript">
        function upload() {
            $("#form1").submit();
            var t = setInterval(function() {
                //获取iframe标签里body元素里的文字。即服务器响应过来的"上传成功"或"上传失败"
                var word = $("#tg").contents().find("body").text();
                if (word != "") {
                    $("#tg").show();
                    setTimeout(function(){$("#tg").hide();},1500);
                    clearInterval(t);   //清除定时器
                }
            }, 1000);
        }
    </script>

</body>
</html>
运行实例 »
点击 "运行实例" 按钮查看在线实例


【2】写一个与指定数据表绑定的类, 实现基本的模型功能,例如查询, 新增, 更新,删除等操作

按照数据表的字段结构编写一个同名的类,在使用PDO对数据库操作时,可以把表中查询到的一条记录映射到类的成员属性上,从而可以用类的属性操作方法来处理表中数据。一条数据记录对应一个类对象,不再是通常的默认的数组元素。

代码实例如下:

实例
<?php

// 类名与表名对应
class Staff {
	// 属性与表中的字段对应
	private $staff_id;
	private $name;
	private $age;
	private $sex;
	private $position;
	private $hiredate;

	// 属性重载
	public function __get($name) {
		return $this->$name;
	}
	public function __set($name, $value) {
		$this->$name = $value;
	}
	// 构造方法
	public function __construct() {
		$this->hiredate = date('Y/m/d', $this->hiredate);
		$this->sex = $this->sex ? '男' : '女';
	}
}

$pdo = new PDO('mysql:dbname=test', '*', '*');
$stmt = $pdo->prepare('SELECT * FROM `staff`');

$stmt->setFetchMode(PDO::FETCH_CLASS, Staff::class);

$stmt->execute();

$staff = $stmt->fetchAll();

var_dump($staff[0]->name);

?>
运行实例 »
点击 "运行实例" 按钮查看在线实例

对于数据内容的查询显示,使用场景在数据库外部,采用上述映射到类的方法有助于数据的操作。而修改、新增和删除操作是把数据放入数据表中,目标场景是在数据库内部,采用映射到类的方式并没有提升操作优势,这些操作本就有专用的库类或函数模块来实现。


总结:

1、通过本次练习,了解了文件上传的过程与方法,了解了自定义异常类的使用方法;

2、文件上传安全漏洞不容忽视,实际项目中的检测和防范知识还需更进一步学习;

3、接本例练习了一种上传后页面不 发生跳转 并给出信息的实现方法;

4、数据表绑定类的关键操作是:setFetchMode(PDO::FETCH_CLASS, Staff::class);

5、练习中发现:映射类不需要显式实例化 就可以直接使用。上例中:$staff[0]->name;

6、在此基础上,可以扩展学习PHP的数据映射模式内容来看看。




声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议