PHP代码审计
基础知识
魔术变量
魔术方法/魔术变量都是随着不同环境可变的,跟变魔术一样
PHP 向它运行的任何脚本提供了大量的预定义常量。
不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了。
有八个魔术常量它们的值随着它们在代码中的位置改变而改变。
变量 | 解释 |
---|---|
LINE | 文件中的当前行号 |
FILE | 文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名 |
DIR | 文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录 |
FUNCTION | 函数名称 |
CLASS | 类的名称,本常量返回该类被定义时的名字(区分大小写) |
TRAIT | Trait 的名字 |
METHOD | 类的方法名,返回该方法被定义时的名字(区分大小写) |
NAMESPACE | 当前命名空间的名称(区分大小写)。此常量是在编译时定义的 |
超级全局变量
PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们在一个脚本的全部作用域中都可用
- $GLOBALS
$GLOBALS 是一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
<?php
$x = 75;
$y = 25;
function addition()
{
$GLOBALS['z'] = $GLOBALS['x'] + $GLOBALS['y'];
}
addition();
echo $z;
?>
- $_SERVER
$_SERVER 是一个包含了诸如**头信息(header)、路径(path)、以及脚本位置(script locations)**等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目。
元素/代码 | 描述 |
---|---|
$_SERVER[‘PHP_SELF’] | 当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/test.php/foo.bar 的脚本中使用 $_SERVER[‘PHP_SELF’] 将得到 /test.php/foo.bar。FILE 常量包含当前(例如包含)文件的完整路径和文件名。 从 PHP 4.3.0 版本开始,如果 PHP 以命令行模式运行,这个变量将包含脚本名。之前的版本该变量不可用。 |
$_SERVER[‘GATEWAY_INTERFACE’] | 服务器使用的 CGI 规范的版本;例如,“CGI/1.1”。 |
$_SERVER[‘SERVER_ADDR’] | 当前运行脚本所在的服务器的 IP 地址。 |
$_SERVER[‘SERVER_NAME’] | 当前运行脚本所在的服务器的主机名。如果脚本运行于虚拟主机中,该名称是由那个虚拟主机所设置的值决定。(如: www.runoob.com) |
$_SERVER[‘SERVER_SOFTWARE’] | 服务器标识字符串,在响应请求时的头信息中给出。 (如:Apache/2.2.24) |
$_SERVER[‘SERVER_PROTOCOL’] | 请求页面时通信协议的名称和版本。例如,“HTTP/1.0”。 |
$_SERVER[‘REQUEST_METHOD’] | 访问页面使用的请求方法;例如,“GET”, “HEAD”,“POST”,“PUT”。 |
$_SERVER[‘REQUEST_TIME’] | 请求开始时的时间戳。从 PHP 5.1.0 起可用。 (如:1377687496) |
$_SERVER[‘QUERY_STRING’] | query string(查询字符串),如果有的话,通过它进行页面访问。 |
$_SERVER[‘HTTP_ACCEPT’] | 当前请求头中 Accept: 项的内容,如果存在的话。 |
$_SERVER[‘HTTP_ACCEPT_CHARSET’] | 当前请求头中 Accept-Charset: 项的内容,如果存在的话。例如:“iso-8859-1,*,utf-8”。 |
$_SERVER[‘HTTP_HOST’] | 当前请求头中 Host: 项的内容,如果存在的话。 |
$_SERVER[‘HTTP_REFERER’] | 引导用户代理到当前页的前一页的地址(如果存在)。由 user agent 设置决定。并不是所有的用户代理都会设置该项,有的还提供了修改 HTTP_REFERER 的功能。简言之,该值并不可信。) |
$_SERVER[‘HTTPS’] | 如果脚本是通过 HTTPS 协议被访问,则被设为一个非空的值。 |
$_SERVER[‘REMOTE_ADDR’] | 浏览当前页面的用户的 IP 地址。 |
$_SERVER[‘REMOTE_HOST’] | 浏览当前页面的用户的主机名。DNS 反向解析不依赖于用户的 REMOTE_ADDR。 |
$_SERVER[‘REMOTE_PORT’] | 用户机器上连接到 Web 服务器所使用的端口号。 |
$_SERVER[‘SCRIPT_FILENAME’] | 当前执行脚本的绝对路径。 |
$_SERVER[‘SERVER_ADMIN’] | 该值指明了 Apache 服务器配置文件中的 SERVER_ADMIN 参数。如果脚本运行在一个虚拟主机上,则该值是那个虚拟主机的值。(如:someone@runoob.com) |
$_SERVER[‘SERVER_PORT’] | Web 服务器使用的端口。默认值为 “80”。如果使用 SSL 安全连接,则这个值为用户设置的 HTTP 端口。 |
$_SERVER[‘SERVER_SIGNATURE’] | 包含了服务器版本和虚拟主机名的字符串。 |
$_SERVER[‘PATH_TRANSLATED’] | 当前脚本所在文件系统(非文档根目录)的基本路径。这是在服务器进行虚拟到真实路径的映像后的结果。 |
$_SERVER[‘SCRIPT_NAME’] | 包含当前脚本的路径。这在页面需要指向自己时非常有用。FILE 常量包含当前脚本(例如包含文件)的完整路径和文件名。 |
$_SERVER[‘SCRIPT_URI’] | URI 用来指定要访问的页面。例如 “/index.html”。 |
- $_REQUEST
PHP $_REQUEST 用于收集HTML表单提交的数据。
<html>
<body>
<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>
<?php
$name = $_REQUEST['fname'];
echo $name;
?>
</body>
</html>
当用户通过点击 “Submit” 按钮提交表单数据时, 表单数据将发送至``标签中 action 属性中指定的脚本文件。 $_SERVER['PHP_SELF']
说明指定的脚本文件为当前脚本文件。
想了解更多关于html中的表单知识,请点 这里
- $_POST
$_POST 被广泛应用于收集表单数据,在HTML form标签的指定该属性:"method=“post”。
<html>
<body>
<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit"》
</form>
<?php
$name = $_POST['fname'];
echo $name;
?>
</body>
</html>
- $_GET
$_GET 同样被广泛应用于收集表单数据,在HTML form标签的指定该属性:"method=“get”。
$_GET 也可以收集URL中发送的数据。
\1. 在web服务器的对应目录下创建
get_test.php
文件<html> <body> <?php echo "Study " . $_GET['subject'] . " at " . $_GET['web']; ?> </body> </html>
\2. 用URL向
get_test.php
文件发送的数据。在浏览器地址栏输入
ip:port/learningPHP/your_dir/get_test.php?subject=PHP&web=runoob.com
得到输出:
Study PHP at runoob.com
字符串/数组操作函数
strlen( ):返回字符串的长度(字符数)
strpos( ):用于在字符串内查找一个字符或一段指定的文本
sort() - 对数组进行升序排列
rsort() - 对数组进行降序排列
asort() - 根据关联数组的值,对数组进行升序排列
ksort() - 根据关联数组的键,对数组进行升序排列
arsort() - 根据关联数组的值,对数组进行降序排列
krsort() - 根据关联数组的键,对数组进行降序排列
命名空间
PHP 命名空间可以解决以下两类问题:
- 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
- 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
命名空间通过关键字namespace 来声明,命名空间必须是程序脚本的第一条语句
<?php
// 定义代码在 'MyProject' 命名空间中
namespace MyProject;
// ... 代码 ...
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }
声明命名空间
注意:\为公共空间
<?php
namespace Nets; //声明命名空间
function getInfo()
{
echo "i am a Nets player<br>";
}
namespace Rocket;
function getInfo(){
echo "i am a Rocket player<br>";
}
// 调用
getInfo();
\Rocket\getInfo();
\Nets\getInfo();
子命名空间
与目录和文件的关系很像,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义:
<?php
namespace MyProject\Sub\Level; //声明分层次的单个命名空间
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }
?>
上面的例子创建了常量 MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection 和函数 MyProject\Sub\Level\Connect
命名空间使用
PHP 命名空间中的类名可以通过三种方式引用
-
非限定名称,或不包含前缀的名称
-
限定名称,或包含前缀的名称
-
完全限定名称,或包含了全局前缀操作符的名称
<?php
namespace USA\Rocket\Nets;
function getInfo()
{
echo "i am a Nets player<br>";
}
namespace USA\Rocket;
function getInfo(){
echo "i am a Rocket player<br>";
}
getInfo(); // 非限定名称
\USA\Rocket\getInfo(); // 完全限定名称
Nets\getInfo(); // 限定名称
命名空间的引入
<?php
namespace USA\Nets;
function getInfo()
{
echo "i am a Nets player<br>";
}
namespace USA\Rocket;
function getInfo(){
echo "i am a Rocket player<br>";
}
// 引入命名空间
use USA\Nets;
getInfo();
Nets\getInfo();
重要:
- 命名空间只能存放类,函数,const常量
- namespace前不能有任何的代码,空白字符,包括header()
- 包含文件不影响当前命名空间
面向对象
三大特性
- 封装:有选择性地提供数据,通过访问修饰符(private/protected/public)来实现
- 继承:extends关键字
- PHP不允许多重继承,(继承多个父亲)
- 私有属性可以继承但不能被重写
- 多态:多种形态,分为方法重写和方法重载
- 方法重写:子类重写了父类的同名的方法
- 方法重载:同个类中不同参数类型或数量的同名方法
- 对象是由属性和方法组成的
- 类是所有对象的相同属性和方法的集合
- 在开发的时候先写类,通过类创建对象,通过对象调用方法和属性
- 一个类可以创建多个对象
class Student {
}
// 实例化对象
$stu1 = new Student(); //括号可以省略,类名大小写不敏感
$stu2 = new Student;
$stu3 = $stu2; //对象传递的是地址
var_dump($stu1, $stu2, $stu3);
//object(Student)#1 (0) { } object(Student)#2 (0) { } object(Student)#2 (0) { }
var_dump($stu1 == $stu2);
var_dump($stu1 === $stu2);
var_dump($stu3 === $stu2); //全等
//bool(true) bool(false) bool(true)
访问控制修饰符
- public(公有):公有的类成员可以在任何地方被访问。
- protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问(继承链)。
- private(私有):私有的类成员则只能被其定义所在的类访问。
一般情况下,属性都是私有的,通过对象的公有方法来赋值或取值
类和对象的内存中的分布
class Student {
public $name;
public $sex;
public function show()
{
echo "OOP!";
}
}
$stu1 = new Student();
$stu2 = new Student;
$stu2->show();
- 对象本质是一个复杂的变量
- 类的本质是一个自定义的复杂数据类型
- 栈小堆大,栈速度快堆慢
- 实例化的过程就是分配内存空间的过程
- 对象保存在堆区,将堆区的地址保存在栈区,方法依然在代码区
构造方法与析构方法
- 构造方法:主要用来在创建对象时初始化对象,即为对象成员变量赋初始值
- 析构方法:当对象结束其生命周期时,系统自动执行析构函数。
class Student {
public $name;
public $sex;
// 构造方法
public function __construct($name)
{
$this->name = $name; //$this代表对当前对象的引用
echo "{$name}出生了!<br>";
}
// 析构方法
public function __destruct()
{
echo "{$this->name}去世了.-.";
}
}
// 对象实例化
$stu1 = new Student("wan");
// 运行
//wan出生了!
//wan去世了.-.
继承中的构造函数
class Person {
public function __construct()
{
echo "这是父类.<br>";
}
}
class Student extends Person {
public function __construct()
{
echo "这是子类.<br>";
Person::__construct(); // 掉用父类的构造函数
parent::__construct(); // parent关键字代指父类类名,降低耦合性
}
}
// 对象实例化
$stu1 = new Student();
// 输出
这是子类.
这是父类.
这是父类.
实际应用:
class Person{
protected $age;
public function __construct($age)
{
$this->age = $age;
}
}
class Student extends Person{
public $name;
public $sex;
public function __construct($name, $sex, $age)
{
$this->name = $name;
$this->sex = $sex;
parent::__construct($age); //调用父类的构造函数
}
public function getInfo()
{
echo "姓名:{$this->name}<br>";
echo "性别:{$this->sex}<br>";
echo "年龄:{$this->age}";
}
}
// 对象实例化
$stu1 = new Student('yao', 'man' ,88);
$stu1->getInfo();
方法修饰符
多个修饰符之间没有先后顺序
- static:静态(内存中仅一份),两个作用
- 表示静态
- static代表当前对象所属的类
static::属性/方法名 //static代表当前对象所属的类
$this->属性/方法名 //$this代表当前对象
self::属性/方法名 //self总是表示当前类的类名
-
final:最后的
- final修饰的方法不能被重写
- final修饰的类不能被继承
-
abstract 作用:定义命名规范 / 阻止实例化
- 抽象类不允许实例化
- 若要继承抽象类,必须重写抽象父类中的所有抽象方法
- 抽象方法必须重写
abstract class Person {
public abstract function setInfo();
public function getInfo()
{
echo "抽象方法<br>";
}
}
class Student extends Person {
public function setInfo()
{
echo "重写父类抽象方法<br>";
}
}
//实例化
$stu = new Student;
$stu->setInfo();
$stu->getInfo();
类常量
class Student {
public const SSS='lll';
}
// 访问类常量
echo Student::SSS;
接口
- 接口是一种特殊的抽象类,接口中全都是抽象方法
- 接口允许多重实现
- 类可以继承的同时实现接口
// 定义接口
interface Person{
const NAME = "f4ke";
function fun(); // 抽象方法
}
// 接口实现
class Student implements Person {
public function fun(){
}
}
// 访问接口常量
echo Person::NAME;
异常处理
try:监测代码块
catch:捕获异常
throw:抛出异常
finally:无论有无异常均执行,可省略
Exception:所有异常的基类
Exception {
/* 属性 */
protected string $message;
protected int $code;
protected string $file;
protected int $line;
/* 方法 */
public __construct([ string $message = ""[, int $code = 0[, Throwable $previous = NULL]]] )
final public getMessage( void) : string
final public getPrevious( void) : Throwable
final public getCode( void) : int
final public getFile( void) : string
final public getLine( void) : int
final public getTrace( void) : array
final public getTraceAsString( void) : string
public __toString( void) : string
final private __clone( void) : void
}
try {
//code...
} catch (\Throwable $th) {
//throw $th;
}
finally{
}
例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<?php
if(isset($_POST['button'])){
$age = $_POST['age'];
try {
if($age == '')
throw new Exception('年龄不能为空');
if(!(is_numeric($age)))
throw new Exception('年龄只能是数字');
} catch (Exception $ex) {
echo $ex->getMessage();
}
finally{
echo "都输出";
}
}
?>
<form action="" method="post">
年龄:<input type="text" name="age"> <br />
<input type="submit" name="button" value="提交">
</form>
</body>
</html>
自动加载类
当缺少类的时候自动调用__autoload()
函数,并且将缺少的类名作为参数传递给__autoload()
__autoload — 尝试加载未定义的类 // 7.2以后废弃
spl_autoload_register() // 5.1版本新增
手动加载类
// Nba.php
<?php
abstract class Nba {
protected $player;
final public function setPlayer($player){
$this->player = $player;
}
public abstract function getplayer();
}
// Nets.php
<?php
class Nets extends Nba {
protected $player;
public function getPlayer()
{
echo $this->player."<br>";
}
}
// Rocket.php
<?php
class Rocket extends Nba {
protected $player;
public function getPlayer()
{
echo $this->player."<br>";
}
}
// test.php
<?php
require './Nba.php'; //手动加载类
require './Nets.php';
require './Rocket.php';
$nets = new Nets;
$nets->setPlayer("harden");
$rocket = new Rocket;
$rocket->setPlayer("house");
$nets->getPlayer();
$rocket->getPlayer();
// 结果
harden
house
自动加载类
<?php
// require './nba.php'; //手动加载类
// require './nets.php';
// require './rocket.php';
// 自动加载类
function __autoload($class_name) {
// require './'.$class_name.'.php';
require "./{$class_name}.php";
}
$nets = new Nets;
$nets->setPlayer("harden");
$rocket = new Rocket;
$rocket->setPlayer("house");
$nets->getPlayer();
$rocket->getPlayer();
注册加载类
// 注册加载类
/// 方法一:
// // 加载类函数
function loadClass($class_name) {
require "./{$class_name}.php";
}
// // 注册加载类函数
spl_autoload_register('loadClass');
/// 方法二:
闭包在PHP5.3才引进
spl_autoload_register(function($class_name){
require "./{$class_name}.php";
});
创建对象的方式
- 实例化
- 克隆
- clone
- __clone()
class Student{
public function __clone()
{
echo "正在克隆对象";
}
}
$stu1 = new Student;
$stu2 = clone $stu1; // 执行clone指令时自动调用__clone()方法
var_dump($stu1, $stu2);
设计模式
单例模式
特征:一个类只能有一个对象
三私一公:
- 私有静态单例属性 标识此单例的在内存中唯一存在
- 私有构造方法阻止类的实例化
- 私有的__clone()来阻止在类的外部clone对象
- 公有静态方法来实现这个实例
<?php
//单例模式:
//一个类只能有一个对象
class Student {
// 私有静态单例属性 标识此单例的在内存中永远存在
private static $instance;
// 私有构造方法阻止类的实例化
private function __construct()
{
}
// 私有的__clone()来阻止在类的外部clone对象
private function __clone()
{
}
// 因为需要一个对象且不能被直接实例化获得,因此使用静态方法来实现这一个实例
public static function getInstance() {
// 判断$instance的值是否属于Student类的类型,不属于就进行实例化返回,否则直接返回
if(!(self::$instance instanceof self))
self::$instance = new self();
return self::$instance;
}
}
$stu1 = Student::getInstance();
$stu2 = Student::getInstance();
$stu3 = Student::getInstance();
var_dump($stu1, $stu2, $stu3);
工厂模式
特征:传递不同的参数,获取不同的对象
<?php
class productsA{
}
class productsB{
}
class productFactory{
public function getproduct($num){
switch ($num) {
case 1:
return new productsA;
case 2:
return new productsB;
default:
return null;
}
}
}
$factory = new productFactory();
$pro1 = $factory->getproduct(1);
$pro2 = $factory->getproduct(2);
var_dump($pro1, $pro2);
策略模式
特征:传递不同的参数执行不同的策略(方法)