Blogger Information
Blog 35
fans 0
comment 0
visits 44161
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
PHP-自定义异常类处理上传错误--2019年10月12日
Victor的博客
Original
1148 people have browsed it

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的数据映射模式内容来看看。




Correction status:qualified

Teacher's comments:自定义样式写得不错
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post