PHP之所有的文件常见操作
文件操作
将字符串写入文件
$str = "床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。";
file_put_contents('./test.txt', $str);
结论与知识点:
1. 所有的写操作都是清空重写
2. 在文件中换行是\r\n
3. \r:回车 光标移动到当前行的最前面
4. \n:换行 将光标向下移动一行
5. 按键盘的回车键做了两步,第一步将光标移动到当前行的最前面,第二步下移一行
6. \r\n都是特殊字符,必须放在双引号内
将整个文件读入一个字符串
echo file_get_contents('./test.txt'), '<br>';
readfile('./test.txt');
echo '<br>';
打开文件并操作
$fp = fopen('./test.txt', 'w');
var_dump($fp);
echo '<br>';
for ($i = 1; $i <= 10; $i++)
fputs($fp, '关关雎鸠' . "\n");
fclose($fp);
打开文件读取
$fp = fopen('./test.txt', 'r');
while ($line = fgets($fp)) {
echo $line . '<br>';
}
fclose($fp);
打开文件追加
$fp = fopen('./test.txt', 'a');
fputs($fp, '在河之洲' . "\n");
fclose($fp);
知识点:
1. 打开文件,返回文件指针,(文件指针就是文件地址),资源类型
2. 打开文件写、追加操作,如果文件不存在,就创建新的文件
3. 打开文件读操作,文件不存在就会报错
4. fputs()写一行,fgets()读一行,fclose()关闭文件
与文件有关的判断
echo is_file('./test.txt') ? '是文件' : '不是文件', '<br>';
echo file_exists('./test.txt') ? '文件存在' : '文件不存在', '<br>';
删除操作
$path = './test.txt';
if (file_exists($path)):
if (is_dir($path))
rmdir($path);
elseif(is_file($path))
unlink($path);
endif;
二进制文件操作
知识点:
1. 二进制文件读【fread(文件指针,文件大小)】
2. 文件存储有两种,字符流和二进制流
3. 二进制流的读取按文件大小来读的
$path='./clk.jpg';
$fp=fopen($path,'r');
header('Content-Type:image/jpeg');
echo fread($fp,filesize($path));
header('Content-Type:image/jpeg');
echo file_get_contents($path);
小结:
1. 文本流有明确的结束符,二进制流没有明确的结束符,通过文件大小判断文件是否读取完毕
2. file_get_contents()既可以进行字符流读取,也可以进行二进制流读取
GET和POST的区别与之对应的请求
知识点:
1. 外观上看:get提交在地址栏上可以看到参数,post提交在地址栏看不到参数
2. 安全性:get不安全,post安全
3. 提交原理:get提交是参数一个一个的提交,post提交是所有参数作为一个整体一起提交
4. 提交数据大小:get提交一般不超过255个字节,post提交的大小取决于服务器
5. 在php.ini中可以设置post提交的大小post_max_size=8M
6. 灵活性:get很灵活,只要有页面的跳转就可以传递参数,post不灵活,post提交需要有表单的参与
跳转方式
html跳转
<a href="index.php?name=tom&age=20">跳转</a>
js跳转
<script type="text/javascript">
location.href='index.php?name=tom&age=20';
location.assign('index.php?name=tom&age=20');
location.replace('index.php?name=tom&age=20');
</script>
php跳转
header('Location:index.php?name=tom&age=20')
post、get、request提交请求的异同
HTML页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="servers.php" method="post">
语文:<input type="text" name="ch"><br>
数学:<input type="text" name="math"><br>
<input type="submit" value="提交" name="button"><br><br>
</form>
<a href="servers.php?ch=85&math=99">跳转</a>
<br><br>
<input type="submit" name="button" onclick="location.href='servers.php?ch=66&math=22'">
</body>
</html>
PHP页面
服务器提交数据的方式知识点:
1. 通过名字获取名字对应的值
2. $_POST:数组类型,保存的POST提交的值
3. $_GET:数组类型,保存的GET提交的值
4. $_REQUEST:数组类型,保存的GET和POST提交的值
if (!empty($_POST)) {
echo '这是post提交的数据', '<br>';
echo '语文:' . $_POST['ch'] . '<br>';
echo '数学:' . $_POST['math'] . '<br>';
}
echo '<hr>';
if (!empty($_GET)) {
echo '这是get提交的数据', '<br>';
echo '语文:' . $_GET['ch'] . '<br>';
echo '数学:' . $_GET['math'] . '<br>';
}
echo '<hr>';
echo '这是request' . '<br>';
echo '语文:' . $_REQUEST['ch'] . '<br>';
echo '数学:' . $_REQUEST['math'] . '<br>';
使用request的注意点
知识点:
1. 在一个请求中,既有get又有post,get和post的名字是一样的,这时候通过$_REQUEST获取的数据会覆盖
2. 覆盖取决于配置文件的配置,request_order='GP',先获取get,再获取post
例子
if (!empty($_POST)){
echo '姓名为:'.$_REQUEST['username'];
}
echo "<form method='post' action='?username=berry'>
姓名:<input type='text' name='username'><br>
<input type='submit' name='button' value='提交'>
</form>";
POST表单练习
复选框值的传递
if (isset($_POST['button'])) {
print_r($_POST['hobby']);
echo $_POST['hobby'][0];
}
echo "<form method='post' action=''>
爱好:
<input type='checkbox' name='hobby[]' value='爬山'>爬山
<input type='checkbox' name='hobby[]' value='跑步'>跑步
<input type='checkbox' name='hobby[]' value='打羽毛球'>打羽毛球
<input type='checkbox' name='hobby[]' value='喝酒'>喝酒
<input type='submit' value='提交' name='button'>
</form>";
知识点:
1. 表单提交到本页面需要判断一下是否有post提交
2. 数组的提交表单元素的名字需要带有[],就是复选框
向服务端提交数据练习
if (isset($_POST['button'])){
echo '姓名:'.$_POST['username'].'<br>';
echo '密码:'.$_POST['pwd'].'<br>';
echo '性别:'.$_POST['sex'].'<br>';
echo '爱好:'.isset($_POST['username'])?implode(',',$_POST['hobby']):'没有爱好'.'<br>';
echo '籍贯:'.$_POST['jiguan'].'<br>';
echo '留言:'.$_POST['words'].'<br>';
}
echo '<form action="" method="post">
姓名:<input type="text" name="username"><br>
密码:<input type="password" name="pwd"><br>
性别:<input type="radio" name="sex" value="1" checked="checked">男
<input type="radio" name="sex" value="0"> 女<br>
爱好:
爱好:
<input type=\'checkbox\' name=\'hobby[]\' value=\'爬山\'>爬山
<input type=\'checkbox\' name=\'hobby[]\' value=\'跑步\'>跑步
<input type=\'checkbox\' name=\'hobby[]\' value=\'打羽毛球\'>打羽毛球
<input type=\'checkbox\' name=\'hobby[]\' value=\'喝酒\'>喝酒
<br>
籍贯:
<select name="jiguan">
<option value="021">上海</option>
<option value="022">湖北</option>
<option value="033">南京</option>
</select><br>
留言:<textarea name="words" rows="10" cols="40"></textarea><br>
<input type="submit" name="button" value="提交">
</form>';
有关$_FILES的知识点
文件域
echo '<input type="file" name="image">';
表单的enctype属性
知识点:
默认情况下,表单传递是字符流,不能传递二进制流,通过设置表单的enctype属性传递复合数据。
enctype属性的值有:
1. application/x-www-form-urlencoded:默认,表示传递的是带格式的文本数据
2. multipart/form-data:复合的表单数据(字符串,文件)文件上传必须设置此值
3. text/plain:用于向服务器传递无格式的文本数据,主要是用户电子邮件
$_FILES的相关小知识
超全局变量$_FILES是一个二维数组,用来保存客户端上传到服务器的文件信息,二维数组的行是文件的名称,列有5个
1. $_FILES[]['name']:上传的文件名
2. $_FILES[]['type']:上传的类型,这个类型是MIME类型
3. $_FILES[]['size']:文件的大小,以字节为单位
4. $_FILES[]['tmp_name']:文件上传时的临时文件
5. $_FILES[]['error']:错误编码值,有0,1,2,3,4,6,7;0表示正确
$_FILES[][‘error’]详解
1. 0:正确
2. 1:文件超过了php.ini中允许的最大值
3. 2:文件大小超过了表单允许的最大值
4. 3:只有部分文件上传
5. 4:没有文件上传
6. 6:找不到临时文件
7. 7:文件写入失败
MAX_FILE_SIZE知识点
echo '<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="2">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';
move_upload_file()的使用
解释:
1. 将上传文件移动到指定位置
2. 函数:move_upload_file(临时地址,目标地址)
if(!empty($_POST)){
if ($_FILES['face']['error']==0){
move_uploaded_file($_FILES['face']['tmp_name'],'./'.$_FILES['face']['name']);
}else{
echo '上传有误';
echo ',错误码:'.$_FILES['face']['error'];
}
}
echo '<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="2158556">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';
注意:上传同名的文件会将原有的文件覆盖
与文件有关的配置
1. post_max_size=8M:表单允许的最大值
2. upload_max_filesize=2M:允许上传文件大小
3. upload_tmp_dir=F:\PS\tmp:指定临时文件地址
4. file_uploads=On:是否允许文件上传
5. max_file_uploads=20:允许同时上传20个文件
优化文件上传
方法:
方法一:通过时间戳做文件名
方法二:通过uniqid()实现,获取一个带前缀、基于当前时间微秒数的唯一ID
$path='clk.jpg';
echo time().rand(100,9999).strrchr($path,'.');
$path='clk.jpg';
echo uniqid().strrchr($path,'.'),'<br>';
echo uniqid('goods_').strrchr($path,'.'),'<br>';
echo uniqid('goods_',true).strrchr($path,'.').'<br>';
验证文件格式
方法:
方法一:判断文件的扩展名(不能识别文件伪装)
方法二:使用mime类型(不能识别文件伪装)
方法三:php_fileinfo扩展(可以防止文件伪装)
if (!empty($_POST)) {
$allow = array('.jpg', '.png', '.gif');
$ext = strrchr($_FILES['face']['name'], '.');
if (in_array($ext, $allow))
echo '允许上传' . '<br>';
else
echo '不允许上传' . '<br>';
}
if (!empty($_POST)) {
$allow = array('image/jpeg', 'image/png', 'image/gif');
$ext = $_FILES['face']['type'];
if (in_array($ext, $allow))
echo '允许上传' . '<br>';
else
echo '不允许上传' . '<br>';
}
if (!empty($_POST)) {
$info = finfo_open(FILEINFO_MIME_TYPE);
var_dump($info);
$mime = finfo_file($info, $_FILES['face']['tmp_name']);
$allow = array('image/jpeg', 'image/png', 'image/gif');
echo in_array($mime,$allow)?'合法':'不合法';
}
三个方法所涉及的html代码
echo '<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="2158556">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';
方法三的注意点:
1. 在php.ini中开启php_info扩展extension=php_fileinfo.dll
2. 注意,开启fileinfo扩展后,就可以使用finfo_*的函数了。
验证格式的总结
1. 可以验证扩展名
2. 可以验证文件的类型
3. 通过file_info扩展(可以防止伪装)
有关文件上传的例题
function check($file){
if ($file['error']!=0){
switch ($file['error']){
case 1:
return '文件大小超过了php.ini中允许的最大值,最大值是:'.ini_get('upload_max_filesize');
case 2:
return '文件大小超过了表单允许的最大值';
case 3:
return '只有部分文件上传';
case 4:
return '没有文件上传';
case 6:
return '找不到临时文件';
case 7:
return '文件写入失败';
default:
return '未知错误';
}
}
$info=finfo_open(FILEINFO_MIME_TYPE);
$mime=finfo_file($info,$file['tmp_name']);
$allow=array('image/jpeg', 'image/png', 'image/gif');
if (!in_array($mime,$allow)){
return '只能上传'.implode(',',$allow).'格式';
}
$size=283558845;
if ($file['size']>$size){
return '文件大小不能超过'.number_format($size/1024,1).'K';
}
if (!is_uploaded_file($file['tmp_name'])){
return '文件不是HTTP_POST上传的';
}
return null;
}
echo date('Y-m-d H:i:s',time());
if (isset($_POST['button'])){
if (check($_FILES['face'])){
echo check($_FILES['face']);
}else{
$foldername=date('Y-m-d');
$folderpath="./upload/{$foldername}";
echo file_exists($folderpath);
if (!file_exists($folderpath)){
mkdir($folderpath,0777,true);
}
$filename=uniqid('',true).strrchr($_FILES['face']['name'],'.');
$filepath="{$folderpath}/{$filename}";
if(move_uploaded_file($_FILES['face']['tmp_name'],$filepath))
echo '上传成功';
else
echo '上传失败'.'<br>';
}
}
echo '<form method="post" enctype="multipart/form-data" action="">
<input type="hidden" name="MAX_FILE_SIZE" value="2158556">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';
其实这只是做到了相对安全的文件上传,黑客可以通过多种方式绕过: 1. 文件名大小写绕过(比如:AsP,PhP之类的文件名绕过黑名单检测 2. 名单列表绕过:用黑名单里没有的名单进行攻击,比如黑名单里没有cer或者asa之类的 3. ::$ DATA绕过 4. htaccess文件:配合名单列表绕过,上传一个自定义的.htaccess就可以轻松绕过检测 5. 空格绕过 6. 配合解析漏洞 7. 双后缀名绕过 8. mime绕过 9. %00,0x00,0x0a截断 10.特殊文件名test.php.或者test.asp_
解析漏洞:
Apache解析漏洞
test.php.aaa.bbb.ccc任意不属于黑名单且不属于Apache解析白名单之内的后缀名 Apache会从最后的位置开始解析,直到遇到一个能解析的扩展名为止
IIS解析漏洞
- IIS6.0在解析asp格式的时候有两个解析漏洞:
- 如果目录名以.asp、.asa、.cer、.cdx字符串结尾,那么这个目录下所有的文件都会按照asp去解析eg:“test.asp/1.jpg”
- 只要文件名中含有.asp、.asa、.cer、.cdx会优先按asp解析(/xx.asp;.jpg /xx.asp:.jpg)
- IIS7.0/7.5是对php解析时有一个类似于Nginx的解析漏洞对任意文件名只要在URL后面追加上字符串“/任意文件名.php”就会按照php的方式去解析
- Nginx<=0.8.37解析漏洞:在Fast-CGI关闭的情况下,在一个文件路径(/xx.jpg)后面加上%00.php会将 /xx.jpg%00.php 解析为 php 文件。
|