PHP开发手册

文章目录

一、PHP基础

PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器")是一种通用开源脚本语言。PHP 文件的默认文件扩展名是 “.php”,可包含文本、HTML、JavaScript代码和 PHP 代码,代码在服务器上执行,结果以纯 HTML 形式返回给浏览器。

1、语法

1.1、基本语法

// PHP 脚本可以放在文档中的任何位置。
// PHP 脚本以<?php开始,以?> 结束:
<?php
// PHP 代码
?>

PHP 文件的默认文件扩展名是 “.php”。

PHP 文件通常包含 HTML 标签和一些 PHP 脚本代码。

下面,我们提供了一个简单的 PHP 文件实例,它可以向浏览器输出文本 “Hello World!”:

<!DOCTYPE html> 
<html> 
<body> 
<h1>My first PHP page</h1> 
<?php 
    echo "Hello World!"; //PHP 中的每个代码行都必须以分号结束。分号是一种分隔符,用于把指令集区分开来。
?> 
</body> 
</html>

1.2、注释

<!DOCTYPE html>
<html>
<body>

<?php
// 这是 PHP 单行注释

/*
这是 
PHP 多行
注释
*/
?>

</body>
</html>

1.3、打印

  • echo

    echo 是一个语言结构,使用的时候可以不用加括号,也可以加上括号: echo 或 echo()。

  • print

    print 同样是一个语言结构,可以使用括号,也可以不使用括号: print 或 print()。

  • echo 和 print 区别

    • echo - 可以输出一个或多个字符串
    • print - 只允许输出一个字符串,返回值总为 1
    • cho 输出的速度比 print 快, echo 没有返回值,print有返回值1。
  • print_r

1.4、EOF(heredoc)

​ PHP EOF(heredoc)是一种在命令行shell(如sh、csh、ksh、bash、PowerShell和zsh)和程序语言(像Perl、PHP、Python和Ruby)里定义一个字符串的方法。

使用概述:

  • 必须后接分号,否则编译通不过。
  • EOF 可以用任意其它字符代替,只需保证结束标识与开始标识一致。
  • 结束标识必须顶格独自占一行(即必须从行首开始,前后不能衔接任何空白和字符)。
  • 开始标识可以不带引号或带单双引号,不带引号与带双引号效果一致,解释内嵌的变量和转义符号,带单引号则不解释内嵌的变量和转义符号。
  • 当内容需要内嵌引号(单引号或双引号)时,不需要加转义符,本身对单双引号转义,此处相当与q和qq的用法。
<?php
echo <<<EOF
    <h1>我的第一个标题</h1>
    <p>我的第一个段落。</p>
EOF;
// 结束需要独立一行且前后不能空格
?>

注意:

  1. 以 <<<EOF 开始标记开始,以 EOF 结束标记结束,结束标记必须顶头写,不能有缩进和空格,且在结束标记末尾要有分号 。

  2. 2.开始标记和结束标记相同,比如常用大写的 EOT、EOD、EOF 来表示,但是不只限于那几个(也可以用:JSON、HTML等),只要保证开始标记和结束标记不在正文中出现即可。

  3. 位于开始标记和结束标记之间的变量可以被正常解析,但是函数则不可以。在 heredoc 中,变量不需要用连接符 . 或 , 来拼接,如下:

    <?php
    $name="runoob";
    $a= <<<EOF
        "abc"$name
        "123"
    EOF;
    // 结束需要独立一行且前后不能空格
    echo $a;
    ?>
    

2、变量

2.1、变量声明

/* PHP没有声明变量的命令,变量在您第一次赋值给它的时候被创建。
 * PHP一门弱类型语言,声明变量时不必声明变量类型,PHP根据变量的值自动把变量转换为相应的数据类型。
 */
<?php
	$txt="Hello world!"; //当您赋一个文本值给变量时,请在文本值两侧加上引号。
	$x=5;
	$y=10.5;

2.2、变量规则

  • 变量以 $ 符号开始,后面跟着变量的名称
  • 变量名必须以字母或者下划线字符开始
  • 变量名只能包含字母数字字符以及下划线(A-z、0-9 和 _ )
  • 变量名不能包含空格
  • 变量名是区分大小写的($y 和 $Y 是两个不同的变量)

2.3、变量作用域

/*全局变量与局部变量*/
// 在所有函数外部定义的变量,拥有全局作用域。除了函数外,全局变量可以被脚本中的任何部分访问,要在一个函数中访问一个全局变量,需要使用 global 关键字。
<?php
$x = 5;
$y = 50;
function myTest() {
    global $x; // 声明此函数中的$x为全局变量
    $x = 10; // 若此函数中没有用global关键字声明,此$x为局部变量。
}
myTest();
echo $x,"\n"; // 在PHP中PHP_EOL和"\n"为换行符,注意斜杠n用双引号,单引号则被解释为字符
echo $GLOBALS['x'],PHP_EOL,$GLOBALS['y']; // PHP 将所有全局变量存储在一个名为 $GLOBALS[index] 的数组中。 index 保存变量的名称。这个数组可以在函数内部访问,也可以直接用来更新全局变量。
?>
  
/*静态变量*/
// 当一个函数执行完成时,它的所有变量通常都会被删除。通过static关键字,可以局部变量不要被删除。
<?php
function myTest() {
    static $x = 0; //该变量仍然是函数的局部变量。
    echo $x;
    $x++;
    echo PHP_EOL;
}
myTest(); //输出0
myTest(); //输出1
myTest(); //输出2
// 若非静态变量,三次调用输出结果均为0
?>
  
/*参数作用域*/
//参数是通过调用代码将值传递给函数的局部变量。参数是在参数列表中声明的,作为函数声明的一部分
<?php
function myTest($x){
    echo $x;
}
myTest(5);

2.4、超级全局变量

​ PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们在一个脚本的全部作用域中都可用。 你不需要特别说明,就可以在函数及类中使用。

  • $GLOBALS - 所有全局变量都放在此组合数组中,变量就是组合数组的键。

    <?php
    $x = 3;
    $y = 2;
    $z = 100;
    function func1() {
        $GLOBALS['z'] = $GLOBALS['x'] + $GLOBALS['y'];
    }
    func1();
    echo $z; // 输出:5
    
  • $_SERVER - 包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。

  • $_REQUEST - 收集HTML表单提交的数据。

  • $_POST - 收集表单数据,在HTML form标签的指定该属性:"method=“post”。

  • $_GET - 用于收集表单数据,在HTML form标签的指定该属性:"method=“get”。

  • $_FILES

  • $_ENV

  • $_COOKIE

  • $_SESSION

3、常量

​ 常量值被定义后,在脚本的其他任何地方都不能被改变。

3.1、自定义常量

​ 常量是一个简单值的标识符。该值在脚本中不能改变。一个常量由英文字母、下划线、和数字组成,但数字不能作为首字母出现。 (常量名不需要加 $ 修饰符)。注意: 常量在整个脚本中都可以使用。

// 设置常量,使用 define() 函数,函数语法如下:
bool define ( string $name , mixed $value [, bool $case_insensitive = false ] )
该函数有三个参数:
	name:必选参数,常量名称,即标志符。
    value:必选参数,常量的值。
    case_insensitive :可选参数,如果设置为true,该常量则大小写不敏感。默认是flase大小写敏感的。
      
<?php
// 区分大小写的常量名
define("GREETING", "欢迎访问 Runoob.com");
echo GREETING;    // 输出 "欢迎访问 Runoob.com"

//不区分大小写的常量名
define("GREETING", "欢迎访问 Runoob.com",true);
echo GREETING;    // 输出 "欢迎访问 Runoob.com"
echo PHP_EOL;
echo greeting;   // 输出 "欢迎访问 Runoob.com"

3.2、魔术常量

​ PHP 向它运行的任何脚本提供了大量的预定义常量。不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了。有八个魔术常量它们的值随着它们在代码中的位置改变而改变。

  • __LINE__ - 文件中的当前行号。
  • __FILE__ - 文件的完整路径和文件名。
  • __DIR__ - 文件所在目录。
  • __FUNCTION__ - 函数名称
  • __CLASS__ - 类名称
  • __TRAIT__ - Trait 的名字
  • __METHOD__ - 类的方法名
  • __NAMESPACE__ - 当前命名空间的名称

4、数据类型

​ String(字符串), Integer(整型), Float(浮点型), Boolean(布尔型), Array(数组), Object(对象), NULL(空值)。

​ PHP中可以通过var_dump() 函数返回变量的数据类型和值。

4.1、字符串

// 一个字符串是一串字符的序列,就像 "Hello world!"。你可以将任何文本放在单引号或双引号中:
<?php 
$txt1 = "Hello world!";
$txt2="What a nice day!";
echo $txt1 . " " . $txt2;//在 PHP 中,只有一个字符串运算符,那就是并置运算符(.)用于把两个字符串值连接起来。
echo strlen($txt1); //strlen() 函数返回字符串的长度(字符数)。
echo strpos("Hello world!","world"); // strpos() 函数用于在字符串内查找一个字符或一段指定的文本。如果在字符串中找到匹配,该函数会返回第一个匹配的字符位置。如果未找到匹配,则返回 FALSE。字符串 "world"的位置是 6。之所以是6而不是7的原因是,字符串中第一个字符的位置是0,而不是1。

4.2、整型

  • 整数必须至少有一个数字 (0-9)
  • 整数不能包含逗号或空格
  • 整数是没有小数点的
  • 整数可以是正数或负数
  • 整型可以用三种格式来指定:十进制, 十六进制( 以 0x 为前缀)或八进制(前缀为 0)。

4.3、浮点型

浮点数是带小数部分的数字,或是指数形式。

4.4、布尔型

布尔型可以是 TRUE 或 FALSE。

$x=true;
$y=false;

4.5、数组

数组可以在一个变量中存储多个值。数组是一个能在单个变量中存储多个值的特殊变量。

4.5.1、数值数组

数组数组 - 带有数字 ID 键的数组

<?php 
//方法一:自动分配ID(ID 键总是从0开始)初始化
$cars=array("Volvo","BMW","Toyota");

//方法二:自动分配ID(ID 键总是从0开始)初始化
$cars=["Volvo","BMW","Toyota"]; 

//方法三:自动分配ID(ID 键总是从0开始)一般在循环里面赋值用
$cars[] = 'Volvo';
$cars[] = 'BMW';
$cars[] = 'Toyota';

//方法四:人工分配ID
$cars[0] = 'Volvo';
$cars[1] = 'BMW';
$cars[2] = 'Toyota';

$arrlength = count($cars); // 获取改数组长度

var_dump($cars);//查看变量类型及值。

// 遍历数组
for($x=0; $x<$arrlength; $x++){
    echo $cars[$x];
    echo "\n";
}
4.4.2、关联数组

关联数组 - 带有指定的键的数组,每个键关联一个值

//关联数组是使用您分配给数组的指定的键的数组

//创建关联数组方法一:
$age = array('Peter'=>35, 'Ben'=>38, 'Joe'=>41);

//创建关联数组方法二:
$age['Peter'] = 35;
$age['Ben'] = 38;
$age['Joe'] = 41;

//遍历关联数组
foreach ($age as $x=>$x_value){
    echo "key=" . $x . ",Value=" . $x_value;
    echo PHP_EOL;
}
4.5.3、多维数组

多维数组 - 包含一个或多个数组的数组;一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。依照这种方式,我们可以创建二维或者三维数组

<?php
// 二维数组
$cars = array
(
    array("Volvo",100,96),
    array("BMW",60,59),
    array("Toyota",110,100)
);

//在多维数组中,主数组中的每一个元素也可以是一个数组,子数组中的每一个元素也可以是一个数组
$sites = array (
    "runoob" => array (
        "菜鸟教程",
        "http://www.runoob.com"
    ),
    "google" => array (
        "Google 搜索",
        "http://www.google.com"
    ),
    "taobao" => array(
        "淘宝",
        "http://www.taobao.com"
    )
);
print_r($sites); //以容易理解的形式打印变量
4.5.4、数组排序
  • sort() - 对数组进行升序排列
  • rsort() - 对数组进行降序排列
  • asort() - 根据关联数组的值,对数组进行升序排列
  • ksort() - 根据关联数组的键,对数组进行升序排列
  • arsort() - 根据关联数组的值,对数组进行降序排列
  • krsort() - 根据关联数组的键,对数组进行降序排列

4.6、对象

用于存放类实例化后的对象的变量。

<?php
class Car{
    var $color = 'red';
    var $Brand = 'BMW';
    function show(){
        return $this->color;
    }
}
$car = new Car();
echo $car->show(); // 打印:red

4.7、NULL值

NULL 值表示变量没有值。NULL 是数据类型为 NULL 的值。

NULL 值指明一个变量是否为空值。 同样可用于数据空值和NULL值的区别。

可以通过设置变量值为 NULL 来清空变量数据

<?php
$x="Hello world!";
$x=null;
var_dump($x);

5、运算符

5.1、算术运算符

运算符名称描述实例
x + yx 和 y 的和echo 3 + 2;
x - yx 和 y 的差echo(3 - 2);
x * yx 和 y 的积print 3 * 2;
x / yx 和 y 的商print(3 / 2);
x % yx 除以 y 的余数echo 3%2;
-x取反x 取反echo -3;
a . b并置连接两个字符串echo ‘he’ . ‘llo’;

PHP7+ 版本新增整除运算符 intdiv(),使用实例:

<?php
var_dump(intdiv(10, 3));
// var_dump()函数用于输出变量的相关信息。

5.2、赋值运算符

在 PHP 中,基本的赋值运算符是 “=”。它意味着左操作数被设置为右侧表达式的值。也就是说,"$x = 5" 的值是 5。

运算符等同于描述
x = yx = y左操作数被设置为右侧表达式的值
x += yx = x + y
x -= yx = x - y
x *= yx = x * y
x /= yx = x / y
x %= yx = x % y模(除法的余数)
a .= ba = a . b连接两个字符串

5.3、自增/自减运算符

运算符名称描述
++ x预递增x 加 1,然后返回 x
x ++后递增返回 x,然后 x 加 1
– x预递减x 减 1,然后返回 x
x –后递减返回 x,然后 x 减 1

5.4、比较运算符

运算符名称描述实例
x == y等于如果 x 等于 y,则返回 true5==8 返回 false
x === y绝对等于如果 x 等于 y,且它们类型相同,则返回 true5===“5” 返回 false
x != y不等于如果 x 不等于 y,则返回 true5!=8 返回 true
x <> y不等于如果 x 不等于 y,则返回 true5<>8 返回 true
x !== y绝对不等于如果 x 不等于 y,或它们类型不相同,则返回 true5!==“5” 返回 true
x > y大于如果 x 大于 y,则返回 true5>8 返回 false
x < y小于如果 x 小于 y,则返回 true5<8 返回 true
x >= y大于等于如果 x 大于或者等于 y,则返回 true5>=8 返回 false
x <= y小于等于如果 x 小于或者等于 y,则返回 true5<=8 返回 true

5.5、逻辑运算符

运算符名称描述实例
x and y如果 x 和 y 都为 true,则返回 truex=6y=3 (x < 10 and y > 1) 返回 true
x or y如果 x 和 y 至少有一个为 true,则返回 truex=6y=3 (x6 or y5) 返回 true
x xor y异或如果 x 和 y 有且仅有一个为 true,则返回 truex=6y=3 (x6 xor y3) 返回 false
x && y如果 x 和 y 都为 true,则返回 truex=6y=3(x < 10 && y > 1) 返回 true
x || y如果 x 和 y 至少有一个为 true,则返回 truex=6y=3(x5 || y5) 返回 false
! x如果 x 不为 true,则返回 truex=6y=3!(x==y) 返回 true

5.6、数组运算符

运算符名称描述
x + y集合x 和 y 的集合
x == y相等如果 x 和 y 具有相同的键/值对,则返回 true
x === y恒等如果 x 和 y 具有相同的键/值对,且顺序相同类型相同,则返回 true
x != y不相等如果 x 不等于 y,则返回 true
x <> y不相等如果 x 不等于 y,则返回 true
x !== y不恒等如果 x 不等于 y,则返回 true

5.7、三元运算符

<?php
$test = 'Hello world!';
// 普通写法
$username = isset($test) ? $test : 'nobody'; //isset()函数用于检测变量是否已设置并且非NULL。
echo $username, PHP_EOL;

// PHP 5.3+ 版本写法
$username = $test ?: 'nobody';
echo $username, PHP_EOL;

// PHP 7+ 版本的写法
$username = $test ?? 'nobody';
echo $username, PHP_EOL;

5.8、组合比较符(PHP7+)

PHP7+ 支持组合比较符(combined comparison operator)也称之为太空船操作符,符号为 <=>。组合比较运算符可以轻松实现两个变量的比较,当然不仅限于数值类数据的比较。

语法格式如下:

$c = $a <=> $b;

解析如下:

  • 如果 **$a > $b **, 则 $c 的值为 1
  • 如果 $a == $b, 则 $c 的值为 0
  • 如果 $a < $b, 则 $c 的值为 -1
<?php
// 整型
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
 
// 浮点型
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
 
// 字符串
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
echo 'ac' <=> 'ab'; // 1

5.9、运算符优先级

我们通过括号的配对来明确标明运算顺序,而非靠运算符优先级和结合性来决定,通常能够增加代码的可读性。

6、分支

6.1、if

<?php
$t = date("H");
if ($t < "10") {
    echo "Have a good morning!";
} 

6.2、if…esle

<?php
$t = date("H");
if ($t < "10") {
    echo "Have a good morning!";
} else {
    echo "Have a good night!";
}

6.3、if…elseif…else

<?php
$t = date("H");
if ($t < "10") {
    echo "Have a good morning!";
} elseif ($t < "20") {
    echo "Have a good day!";
} else {
    echo "Have a good night!";
}

6.4、switch…case

工作原理:首先对一个简单的表达式(通常是变量)进行一次计算。将表达式的值与结构中每个 case 的值进行比较。如果存在匹配,则执行与 case 关联的代码。代码执行后,使用 break 来阻止代码跳入下一个 case 中继续执行。default 语句用于不存在匹配(即没有 case 为真)时执行。

<?php
$favcolor = "red";
switch ($favcolor) {
    case "red":
        echo "你喜欢的颜色是红色!";
        break;
    case "blue":
        echo "你喜欢的颜色是蓝色!";
        break;
    case "green":
        echo "你喜欢的颜色是绿色!";
        break;
    default:
        echo "你喜欢的颜色不是 红, 蓝, 或绿色!";
}

7、循环

在您编写代码时,您经常需要让相同的代码块一次又一次地重复运行。我们可以在代码中使用循环语句来完成这个任务。

在 PHP 中,提供了下列循环语句:

  • **while **- 只要指定的条件成立,则循环执行代码块
  • do…while - 首先执行一次代码块,然后在指定的条件成立时重复这个循环
  • **for **- 循环执行代码块指定的次数
  • **foreach **- 根据数组中每个元素来循环代码块

7.1、while

​ while 循环将重复执行代码块,直到指定的条件不成立。

<?php
$i = 1;
while ($i <= 5) {    
    echo "The number is " . $i . PHP_EOL;
  	$i++;
}

/*
输出:
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
*/

7.2、do…while

​ do…while 语句会至少执行一次代码,然后检查条件,只要条件成立,就会重复进行循环。

<?php
$i = 1;
do {
    echo "The number is " . $i . PHP_EOL;
    $i++;
} while ($i <= 5);

/*
输出:
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
*/

7.3、for

for 循环用于您预先知道脚本需要运行的次数的情况。

语法

for (初始值; 条件; 增量)
{
    要执行的代码;
}

参数:

  • 初始值:主要是初始化一个变量值,用于设置一个计数器(但可以是任何在循环的开始被执行一次的代码)。
  • 条件:循环执行的限制条件。如果为 TRUE,则循环继续。如果为 FALSE,则循环结束。
  • 增量:主要用于递增计数器(但可以是任何在循环的结束被执行的代码)。

注释:上面的初始值增量参数可为空,或者有多个表达式(用逗号分隔)。

<?php
for ($i = 1; $i <= 5; $i++) {
    echo "The number is " . $i . "\n";
}

7.4、foreach

foreach 循环用于遍历数组。

<?php
$x = array('one', 'two', 'three');
foreach ($x as $value) {
    print $value . "\n";
}
// 每进行一次循环,当前数组元素的值就会被赋值给 $value 变量(数组指针会逐一地移动),在进行下一次循环时,您将看到数组中的下一个值。

8、函数

​ PHP 的真正威力源自于它的函数。在 PHP 中,提供了超过 1000 个内建的函数。

8.1、内建函数

​ 如需查看所有数组函数的完整参考手册和实例,访问 PHP 参考手册

8.2、自定义函数

  • 函数的名称应该提示出它的功能
  • 函数名称以字母或下划线开头(不能以数字开头)
<?php
function writeName() {
    echo 'Kai Jim Refsnes';
}
echo 'My name is ';
writeName();

// 输出:My name is Kai Jim Refsnes
8.2.1、参数

​ 为了给函数添加更多的功能,我们可以添加参数,参数类似变量。参数就在函数名称后面的一个括号内指定。

<?php
function writeName($fname) {
    echo 'My name is ',$fname;
}
writeName('Kai Jim Refsnes');

// 输出:My name is Kai Jim Refsnes
8.2.2、返回值

如需让函数返回一个值,请使用 return 语句。

<?php
function add($x, $y) {
    return $x + $y;
}
print '3 + 2 = ' . add(3,2);

// 输出:3 + 2 = 5

9、命名空间

PHP 命名空间可以解决以下两类问题:

  1. 用户编写的代码与PHP内部的类、函数、常量或第三方类、函数、常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

9.1、定义命名空间

​ 默认情况下,所有常量、类和函数名都放在全局空间下。

​ 命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。

  • 同一个文件单个命名空间

    <?php  
    // 定义代码在 'MyProject' 命名空间中  
    namespace MyProject;  
     
    // ... 代码 ... 
    
  • 同一个文件多个命名空间

    建议将每个命名空间的所属代码用大括号括起来,当然不加也没有语法错误但可读性差点。

    <?php
    namespace MyProject {
        const CONNECT_OK = 1;
        class Connection { /* ... */ }
        function connect() { /* ... */  }
    }
    
    namespace AnotherProject {
        const CONNECT_OK = 1;
        class Connection { /* ... */ }
        function connect() { /* ... */  }
    }
    
  • 全局代码

    将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法。全局代码必须用一个不带名称的 namespace 语句加上大括号括起来,例如:

    <?php
    namespace MyProject {
    	const CONNECT_OK = 1;
    	class Connection { /* ... */ }
    	function connect() { /* ... */  }
    }
    
    namespace { // 全局代码
    	session_start();
    	$a = MyProject\connect();
    	echo MyProject\Connection::start();
    }
    
  • 命名空间之前的代码

    在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句。所有非 PHP 代码包括空白符都不能出现在命名空间的声明之前。

    <?php
    declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码
    
    namespace MyProject {
    	const CONNECT_OK = 1;
    	class Connection { /* ... */ }
    	function connect() { /* ... */  }
    }
    
    //以下代码会出现语法错误:
    <html>
    <?php
    namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句
    

9.2、子命名空间

​ 与目录和文件的关系很像,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义。

<?php
namespace MyProject\Sub\Level; //声明分层次的单个命名空间
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */  }

9.3、命名空间的使用

PHP 命名空间中的类名可以通过三种方式引用:

  1. **非限定名称,或不包含前缀的类名称,**例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。

  2. **限定名称,或包含前缀的名称,**例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。

  3. **完全限定名称,或包含了全局前缀操作符的名称,**例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。

9.4、受动态语言特征影响

​ PHP 命名空间的实现受到其语言自身的动态特征的影响。因此,如果要将下面的代码转换到命名空间中,动态访问元素。

<?php
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "global";

$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global

​ 必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的。

​ 动态访问命名空间的元素

<?php
namespace namespacename;
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "namespaced";

include 'example1.php';

$a = 'classname';
$obj = new $a; // 输出 classname::__construct
$b = 'funcname';
$b(); // 输出函数名
echo constant('constname'), "\n"; // 输出 global

/* 如果使用双引号,使用方法为 "\\namespacename\\classname"*/
$a = '\namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // 输出 namespacename\funcname
$b = '\namespacename\funcname';
$b(); // 输出 namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // 输出 namespaced
echo constant('namespacename\constname'), "\n"; // 输出 namespaced

9.5、内部元素的访问

​ PHP支持两种抽象的访问当前命名空间内部元素的方法,__NAMESAPCE__魔术常量和namespace关键字。常量__NAMESPACE__的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。

  • __NAMESPACE__示例, 在命名空间中的代码

    <?php
    namespace MyProject;
    
    echo '"', __NAMESPACE__, '"'; // 输出 "MyProject"
    
  • __NAMESPACE__示例, 全局代码

    <?php
    
    echo '"', __NAMESPACE__, '"'; // 输出 ""
    
  • __NAMESPACE__动态创建名称

    <?php
    namespace MyProject;
    
    function get($classname)
    {
        $a = __NAMESPACE__ . '\\' . $classname;
        return new $a;
    }
    
  • namespace操作符,命名空间中的代码

    关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符。

    <?php
    namespace MyProject;
    
    use blah\blah as mine; // see "Using namespaces: importing/aliasing"
    
    blah\mine(); // calls function blah\blah\mine()
    namespace\blah\mine(); // calls function MyProject\blah\mine()
    
    namespace\func(); // calls function MyProject\func()
    namespace\sub\func(); // calls function MyProject\sub\func()
    namespace\cname::method(); // calls static method "method" of class MyProject\cname
    $a = new namespace\sub\cname(); // instantiates object of class MyProject\sub\cname
    $b = namespace\CONSTANT; // assigns value of constant MyProject\CONSTANT to $b
    
  • namespace操作符, 全局代码

    <?php
    namespace\func(); // calls function func()
    namespace\sub\func(); // calls function sub\func()
    namespace\cname::method(); // calls static method "method" of class cname
    $a = new namespace\sub\cname(); // instantiates object of class sub\cname
    $b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b
    

9.6、别名/导入

​ PHP 命名空间支持 有两种使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。在PHP中,别名是通过操作符 use 来实现的。

  1. 使用use操作符导入/使用别名

    <?php
    namespace foo;
    use My\Full\Classname as Another;
    
    // 下面的例子与 use My\Full\NSname as NSname 相同
    use My\Full\NSname;
    
    // 导入一个全局类
    use \ArrayObject;
    
    $obj = new namespace\Another; // 实例化 foo\Another 对象
    $obj = new Another; // 实例化 My\Full\Classname 对象
    NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
    $a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
    // 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
    
  2. 一行中包含多个use语句

    <?php
    use My\Full\Classname as Another, My\Full\NSname;
    
    $obj = new Another; // 实例化 My\Full\Classname 对象
    NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
    
  3. 导入和动态名称

    导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是。

    <?php
    use My\Full\Classname as Another, My\Full\NSname;
    
    $obj = new Another; // 实例化一个 My\Full\Classname 对象
    $a = 'Another';
    $obj = new $a;      // 实际化一个 Another 对象
    
  4. 导入和完全限定名称

    导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。

    <?php
    use My\Full\Classname as Another, My\Full\NSname;
    
    $obj = new Another; // 实例化 My\Full\Classname 类
    $obj = new \Another; // 实例化 Another 类
    $obj = new Another\thing; // 实例化 My\Full\Classname\thing 类
    $obj = new \Another\thing; // 实例化 Another\thing 类
    

9.7、后备全局函数/常量

​ 在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称。

  1. 在命名空间中访问全局类

    namespace A\B\C;
    class Exception extends \Exception {}
    
    $a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
    $b = new \Exception('hi'); // $b 是类 Exception 的一个对象
    
    $c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类
    
  2. 命名空间中后备的全局函数/常量

    对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。

    <?php
    namespace A\B\C;
    
    const E_ERROR = 45;
    function strlen($str)
    {
        return \strlen($str) - 1;
    }
    
    echo E_ERROR, "\n"; // 输出 "45"
    echo INI_ALL, "\n"; // 输出 "7" - 使用全局常量 INI_ALL
    
    echo strlen('hi'), "\n"; // 输出 "2"
    if (is_array('hi')) { // 输出 "is not array"
        echo "is array\n";
    } else {
        echo "is not array\n";
    }
    

9.8、全局空间

​ 如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 \ 表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此。

<?php
namespace A\B\C;

/* 这个函数是 A\B\C\fopen */
function fopen() { 
     $f = \fopen(...); // 调用全局的fopen函数
     return $f;
} 

9.9、命名空间的顺序

自从有了命名空间之后,最容易出错的该是使用类的时候,这个类的寻找路径是什么样的了。

<?php
namespace A;
use B\D, C\E as F;

// 函数调用

foo();      // 首先尝试调用定义在命名空间"A"中的函数foo()
            // 再尝试调用全局函数 "foo"

\foo();     // 调用全局空间函数 "foo" 

my\foo();   // 调用定义在命名空间"A\my"中函数 "foo" 

F();        // 首先尝试调用定义在命名空间"A"中的函数 "F" 
            // 再尝试调用全局函数 "F"

// 类引用

new B();    // 创建命名空间 "A" 中定义的类 "B" 的一个对象
            // 如果未找到,则尝试自动装载类 "A\B"

new D();    // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
            // 如果未找到,则尝试自动装载类 "B\D"

new F();    // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
            // 如果未找到,则尝试自动装载类 "C\E"

new \B();   // 创建定义在全局空间中的类 "B" 的一个对象
            // 如果未发现,则尝试自动装载类 "B"

new \D();   // 创建定义在全局空间中的类 "D" 的一个对象
            // 如果未发现,则尝试自动装载类 "D"

new \F();   // 创建定义在全局空间中的类 "F" 的一个对象
            // 如果未发现,则尝试自动装载类 "F"

// 调用另一个命名空间中的静态方法或命名空间函数

B\foo();    // 调用命名空间 "A\B" 中函数 "foo"

B::foo();   // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
            // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"

D::foo();   // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
            // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"

\B\foo();   // 调用命名空间 "B" 中的函数 "foo" 

\B::foo();  // 调用全局空间中的类 "B" 的 "foo" 方法
            // 如果类 "B" 未找到,则尝试自动装载类 "B"

// 当前命名空间中的静态方法或函数

A\B::foo();   // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"

\A\B::foo();  // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>

名称解析遵循下列规则:

  1. 对完全限定名称的函数,类和常量的调用在编译时解析。例如 new \A\B 解析为类 A\B

  2. 所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为 A\B\C\D\e()

  3. 在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用 C\D\e(),则 C\D\e() 会被转换为 A\B\C\D\e()

  4. 非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 *new A\B\C() *。

  5. 在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:

    • 在当前命名空间中查找名为 A\B\foo() 的函数
    • 尝试查找并调用 全局(global) 空间中的函数 foo()
  6. 在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C()new D\E() 的解析过程:

    • *new C()*的解析:
      • 在当前命名空间中查找A\B\C类。
      • 尝试自动装载类A\B\C
    • *new D\E()*的解析:
      • 在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
      • 尝试自动装载类 A\B\D\E

    为了引用全局命名空间中的全局类,必须使用完全限定名称 new \C()

二、面向对象

​ 在面向对象的程序设计(英语:Object-oriented programming,缩写:OOP)中,对象是一个由信息及对信息进行处理的描述所组成的整体,是对现实世界的抽象。

对象的主要三个特性:

  • 对象的行为:可以对 对象施加那些操作,开灯,关灯就是行为。
  • 对象的形态:当施加那些方法是对象如何响应,颜色,尺寸,外型。
  • 对象的表示:对象的表示就相当于身份证,具体区分在相同的行为与状态下有什么不同。

比如 Animal(动物) 是一个抽象类,我们可以具体到一只狗跟一只羊,而狗跟羊就是具体的对象,他们有颜色属性,可以写,可以跑等行为状态。

1、面向对象内容

  • − 定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
  • 对象 − 是类的实例。
  • 成员变量 − 定义在类内部的变量。该变量的值对外是不可见的,但是可以通过成员函数访问,在类被实例化为对象后,该变量即可称为对象的属性。
  • 成员函数 − 定义在类的内部,可用于访问对象的数据。
  • 继承 − 继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。
  • 父类 − 一个类被其他类继承,可将该类称为父类,或基类,或超类。
  • 子类 − 一个类继承其他类称为子类,也可称为派生类。
  • 多态 − 多态性是指相同的函数或方法可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。
  • 重载 − 简单说,就是函数或者方法有同样的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
  • 抽象性 − 抽象性是指将具有一致的数据结构(属性)和行为(操作)的对象抽象成类。一个类就是这样一种抽象,它反映了与应用有关的重要性质,而忽略其他一些无关内容。任何类的划分都是主观的,但必须与具体的应用有关。
  • 封装 − 封装是指将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内。
  • 构造函数 − 主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
  • 析构函数 − 析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做"清理善后" 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。

2、PHP类定义

PHP 定义类通常语法格式如下:

<?php

class Site
{
    /*成员变量*/
    public $url;
    public $title = 'constant string'; // 初始化:定义变量时赋值。

    /*成员函数*/
    function setUrl($par)
    {
        $this->url = $par;
    }

    function setTitle($par)
    {
        $this->title = $par;
    }

    function getUrl()
    {
        print $this->url . PHP_EOL;
    }

    function getTitle()
    {
        print $this->title . PHP_EOL;
    }
}

解析如下:

  • 类使用 class 关键字后加上类名定义。
  • 类名后的一对大括号内可以定义变量和方法。
  • 类的变量使用 var 来声明, 变量也可以初始化值。
  • 函数定义类似 PHP 函数的定义,但函数只能通过该类及其实例化的对象访问。
  • 变量 $this 代表对象本身,指向该对象内存空间。

3、PHP 中创建对象

类创建后,我们可以使用 new 运算符来实例化该类的对象:

$runoob = new Site;//在PHP中实例化时类名后加不加小括号都可以。
$taobao = new Site; // $taobao = new Site();
$google = new Site; // $google = new Site();
//以上代码我们创建了三个对象,三个对象各自都是独立的.

4、调用成员方法

在实例化对象后,我们可以使用该对象调用成员方法,该对象的成员方法只能操作该对象的成员变量

// 调用成员函数,设置标题和URL
$runoob->setTitle( "菜鸟教程" );
$taobao->setTitle( "淘宝" );
$google->setTitle( "Google 搜索" );

$runoob->setUrl( 'www.runoob.com' );
$taobao->setUrl( 'www.taobao.com' );
$google->setUrl( 'www.google.com' );

// 调用成员函数,获取标题和URL
$runoob->getTitle();
$taobao->getTitle();
$google->getTitle();

$runoob->getUrl();
$taobao->getUrl();
$google->getUrl();

5、构造函数

构造函数是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,在创建对象的语句中与 new 运算符一起使用。

PHP5+ 允许开发者在一个类中定义一个方法作为构造函数,语法格式如下

void __construct ([ mixed $args [, $... ]] )
  
//在上面的例子中我们就可以通过构造方法来初始化 $url 和 $title 变量
<?php

class Site
{
    /*成员变量*/
    public $url;
    public $title = 'constant string'; // 初始化:定义变量时赋值。

    /*成员函数*/
    function getUrl()
    {
        print $this->url . PHP_EOL;
    }

    function getTitle()
    {
        print $this->title . PHP_EOL;
    }

    function __construct($par1,$par2) /*构造函数*/
    {
        $this->title = $par1;
        $this->url = $par2;
    }
}

$taobao = new Site('淘宝','http://www.taobao.com');
$taobao->getTitle();
$taobao->getUrl();

6、析构函数

析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。

PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,其语法格式如下:

<?php

class Site
{
    /*成员变量*/
    public $url;
    public $title = 'constant string'; // 初始化:定义变量时赋值。

    /*析构函数*/
    function __destruct()
    {
        // TODO: Implement __destruct() method.
        print '析构函数' . PHP_EOL;
    }

    /*成员函数*/
    function getUrl()
    {
        print $this->url . PHP_EOL;
    }

    /*构造函数*/
    function __construct($par1,$par2)
    {
        $this->title = $par1;
        $this->url = $par2;
    }
}

$taobao = new Site('淘宝','http://www.taobao.com');
$taobao->getUrl();

//输出如下:
http://www.taobao.com
析构函数

7、继承

PHP 使用关键字 extends 来继承一个类,PHP 不支持多继承。

<?php 
// 子类扩展站点类别
class Child_Site extends Site {
   var $category;

    function setCate($par){
        $this->category = $par;
    }
  
    function getCate(){
        echo $this->category . PHP_EOL;
    }
}

8、方法重写

​ 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

<?php 
// 子类扩展站点类别
class Child_Site extends Site {
   var $category;

    function setCate($par){
        $this->category = $par;
    }
  
    function getCate(){
        echo $this->category . PHP_EOL;
    }
  
  	function getUrl() { /* 重写了Site类中的getUrl方法 */ 
   		echo $this->url . PHP_EOL;
   		return $this->url;
	}
}

9、访问控制

​ PHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。

  • **public(公有):**公有的类成员,不限制访问。
  • **protected(受保护):**受保护的类成员则可以被其自身以及其子类访问。
  • **private(私有):**私有的类成员则只能被其定义所在的类访问。

9.1、属性的访问控制

类属性必须定义为公有,受保护,私有之一。如果用 var 定义,则被视为公有。

<?php
/**
 * Define MyClass
 */
class MyClass
{
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj = new MyClass();
echo $obj->public; // 这行能被正常执行
echo $obj->protected; // 这行会产生一个致命错误
echo $obj->private; // 这行也会产生一个致命错误
$obj->printHello(); // 输出 Public、Protected 和 Private


/**
 * Define MyClass2
 */
class MyClass2 extends MyClass
{
    // 可以对 public 和 protected 进行重定义,但 private 而不能
    protected $protected = 'Protected2';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj2 = new MyClass2();
echo $obj2->public; // 这行能被正常执行
echo $obj2->private; // 未定义 private
echo $obj2->protected; // 这行会产生一个致命错误
$obj2->printHello(); // 输出 Public、Protected2 和 Undefined

9.2、方法的访问控制

类中的方法可以被定义为公有,私有或受保护。如果没有设置这些关键字,则该方法默认为公有。

<?php
/**
 * Define MyClass
 */
class MyClass
{
    // 声明一个公有的构造函数
    public function __construct() { }

    // 声明一个公有的方法
    public function MyPublic() { }

    // 声明一个受保护的方法
    protected function MyProtected() { }

    // 声明一个私有的方法
    private function MyPrivate() { }

    // 此方法为公有
    function Foo()
    {
        $this->MyPublic();
        $this->MyProtected();
        $this->MyPrivate();
    }
}

$myclass = new MyClass;
$myclass->MyPublic(); // 这行能被正常执行
$myclass->MyProtected(); // 这行会产生一个致命错误
$myclass->MyPrivate(); // 这行会产生一个致命错误
$myclass->Foo(); // 公有,受保护,私有都可以执行


/**
 * Define MyClass2
 */
class MyClass2 extends MyClass
{
    // 此方法为公有
    function Foo2()
    {
        $this->MyPublic();
        $this->MyProtected();
        $this->MyPrivate(); // 这行会产生一个致命错误
    }
}

$myclass2 = new MyClass2;
$myclass2->MyPublic(); // 这行能被正常执行
$myclass2->Foo2(); // 公有的和受保护的都可执行,但私有的不行

class Bar 
{
    public function test() {
        $this->testPrivate();
        $this->testPublic();
    }

    public function testPublic() {
        echo "Bar::testPublic\n";
    }
    
    private function testPrivate() {
        echo "Bar::testPrivate\n";
    }
}

class Foo extends Bar 
{
    public function testPublic() {
        echo "Foo::testPublic\n";
    }
    
    private function testPrivate() {
        echo "Foo::testPrivate\n";
    }
}

$myFoo = new foo();
$myFoo->test(); // Bar::testPrivate 
                // Foo::testPublic
?>

10、接口

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

接口中定义的所有方法都必须是公有,这是接口的特性。

要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。

<?php

// 声明一个'iTemplate'接口
interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
}


// 实现接口
class Template implements iTemplate
{
    private $vars = array();
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
  
    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }
 
        return $template;
    }
}

11、常量

可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。

常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。

自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字(如 self,parent 或 static)。

<?php
class MyClass
{
    const constant = '常量值';

    function showConstant() {
        echo  self::constant . PHP_EOL;
    }
}

echo MyClass::constant . PHP_EOL;

$classname = "MyClass";
echo $classname::constant . PHP_EOL; // 自 5.3.0 起

$class = new MyClass();
$class->showConstant();

echo $class::constant . PHP_EOL; // 自 PHP 5.3.0 起

12、抽象类

任何一个类,如果它里面有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。

定义为抽象的类不能被实例化。

被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。

继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。

<?php
abstract class AbstractClass
{
    // 强制要求子类定义这些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // 普通方法(非抽象方法)
    public function printOut() {
        print $this->getValue() . PHP_EOL;
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') . PHP_EOL;

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') . PHP_EOL;
?>

13、Static关键字

  • 静态属性或方法可以通过类名访问。
  • 静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)
  • 由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。

自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字 self,parent 或 static。

<?php

class Foo
{
    public static $my_static = 'foo';
    public static function staticValue()
    {
        return self::$my_static;
    }
}

print Foo::$my_static . PHP_EOL;
print Foo::staticValue() . PHP_EOL;

$foo = new Foo();
print $foo->staticValue() . PHP_EOL; 

14、Final 关键字

​ PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。

<?php
class BaseClass {
   public function test() {
       echo "BaseClass::test() called" . PHP_EOL;
   }
   
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called"  . PHP_EOL;
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo "ChildClass::moreTesting() called"  . PHP_EOL;
   }
}
// 报错信息 Fatal error: Cannot override final method BaseClass::moreTesting()
?>

15、调用父类构造方法

​ PHP 不会在子类的构造方法中自动的调用父类的构造方法。要执行父类的构造方法,需要在子类的构造方法中调用 parent::__construct()

<?php
class BaseClass {
   function __construct() {
       print "BaseClass 类中构造方法" . PHP_EOL;
   }
}
class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();  // 子类构造方法不能自动调用父类的构造方法
       print "SubClass 类中构造方法" . PHP_EOL;
   }
}
class OtherSubClass extends BaseClass {
    // 继承 BaseClass 的构造方法
}

// 调用 BaseClass 构造方法
$obj = new BaseClass();

// 调用 BaseClass、SubClass 构造方法
$obj = new SubClass();

// 调用 BaseClass 构造方法
$obj = new OtherSubClass();
?>

三、PHP表单

1、表单验证

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>胡嚞衎的网站</title>
    <style>
        .error {
            color: #FF0000
        }
    </style>
</head>
<body>

<?php
// 定义变量并默认设置为空值
$nameErr = $emailErr = $genderErr = $websiteErr = '';
$name = $email = $gender = $comment = $website = '';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    if (empty($_POST['name'])) {
        $nameErr = '名字是必须的';
    } else {
        $name = test_input($_POST['name']);
        /*检查名字是否值包含字母和空格*/
        if (!preg_match('/^[a-zA-Z ]*$/', $name)) {
            $nameErr = '只允许字母和空格';
        }

    }

    if (empty($_POST['email'])) {
        $emailErr = '邮箱是必须的';
    } else {
        $email = test_input($_POST['email']);
        /*检查邮箱是否合法*/
        if (!preg_match('/([\w\-]+\@[\w\-]+[\w\-]+)/', $email)) {
            $emailErr = '非法邮箱格式';
        }
    }

    if (empty($_POST["website"])) {
        $website = "";
    } else {
        $website = test_input($_POST["website"]);
        // 检测 URL 地址是否合法
        if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i", $website)) {
            $websiteErr = "非法的 URL 的地址";
        }
    }

    if (empty($_POST["comment"])) {
        $comment = "";
    } else {
        $comment = test_input($_POST["comment"]);
    }

    if (empty($_POST["gender"])) {
        $genderErr = "性别是必需的";
    } else {
        $gender = test_input($_POST["gender"]);
    }
}

function test_input($data)
{
    $data = trim($data); //函数去除用户输入数据中不必要的字符 (如:空格,tab,换行)。
    $data = stripcslashes($data); //去除用户输入数据中的转义符(\)。
    $data = htmlspecialchars($data); //把一些预定义的字符转换为 HTML 实体。
    return $data;
}

?>

<h2>PHP 表单验证</h2>
<p><span class="error">* 必需字段。</span></p>
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
    <label for="name">名字: </label>
    <input id="name" type="text" name="name" value="<?php echo $name; ?>">
    <span class="error">* <?php echo $nameErr; ?></span>
    <br><br>

    <label for="email">E-mail: </label>
    <input id="email" type="text" name="email" value="<?php echo $email; ?>">
    <span class="error">* <?php echo $emailErr; ?></span>
    <br><br>

    <label for="website">网址: </label>
    <input id="website" type="text" name="website" value="<?php echo $website; ?>">
    <span class="error"><?php echo $websiteErr; ?></span>
    <br><br>

    <label for="conment">备注: </label>
    <textarea id="conment" name="comment" rows="5" cols="40"><?php echo $comment; ?></textarea>
    <br><br>


    <label for="gender">性别:</label>
    <input id="gender" type="radio" name="gender" <?php if (isset($gender) && $gender == "female") echo "checked"; ?>value="female"><input id="gender" type="radio" name="gender" <?php if (isset($gender) && $gender == "male") echo "checked"; ?> value="male"><span class="error">* <?php echo $genderErr; ?></span>
    <br><br>

    <input type="submit" name="submit" value="Submit">
</form>

<?php
echo "<h2>您输入的内容是:</h2>";
echo $name;
echo "<br>";
echo $email;
echo "<br>";
echo $website;
echo "<br>";
echo $comment;
echo "<br>";
echo $gender;
?>

</body>
</html>

2、$_SERVER[“PHP_SELF”]

$_SERVER["PHP_SELF"]是超级全局变量,返回当前正在执行脚本的文件名,与 document root相关。所以, $_SERVER["PHP_SELF"]会发送表单数据到当前页面,而不是跳转到不同的页面。

​ 当黑客使用跨网站脚本的HTTP链接来攻击时,$_SERVER["PHP_SELF"]服务器变量也会被植入脚本。原因就是跨网站脚本是附在执行文件的路径后面的,因此$_SERVER["PHP_SELF"]的字符串就会包含HTTP链接后面的JavaScript程序代码。

XSS又叫 CSS (Cross-Site Script) ,跨站脚本攻击。恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。

3、htmlspecialchars()

​ htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体。预定义的字符是:

  • & (和号) 成为 &amp;
  • " (双引号) 成为&quot;
  • ’ (单引号) 成为&#039;
  • < (小于) 成为 &lt;
  • > (大于) 成为 &gt;

$_SERVER["PHP_SELF"] 可以通过 htmlspecialchars() 函数来避免被利用。

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
  
// htmlspecialchars() 把一些预定义的字符转换为 HTML 实体。现在如果用户想利用 PHP_SELF 变量, 结果将输出如下所示:
<form method="post" action="test_form.php/&quot;&gt;&lt;script&gt;alert('hacked')&lt;/script&gt;">

四、PHP高级特性

1、日期

​ PHP date() 函数可把时间戳格式化为可读性更好的日期和时间。时间戳是一个字符序列,表示一定的事件发生的日期/时间。

1.1、语法

string date ( string $format [, int $timestamp ] )
参数描述
format必需。规定时间戳的格式。
timestamp可选。规定时间戳。默认是当前的日期和时间。

1.2、格式化日期

​ date() 函数的第一个必需参数 format 规定了如何格式化日期/时间。这里列出了一些可用的字符:

  • d - 代表月中的天 (01 - 31)
  • m - 代表月 (01 - 12)
  • Y - 代表年 (四位数)
<?php
// 获取当前时间  
print date("Y/m/d") . PHP_EOL;
print date("Y.m.d") . PHP_EOL;
print date("Y-m-d") . PHP_EOL;
  
//上面代码的输出如下所示:
2019/04/26
2019.04.26
2019-04-26
  
// 设置一个时间戳  
print date('m-d',611161200) . PHP_EOL;

1.3、时区设置

  • 修改php.ini,在php.ini中找到data.timezone =去掉它前面的;号,然后设置data.timezone = “Asia/Shanghai”;即可。

  • 在程序PHP 5以上版本的程序代码中使用函数ini_set(‘date.timezone’,‘Asia/Shanghai’);或者date_default_timezone_set(‘Asia/Shanghai’),注意在文件开头加入。

  • 一些常用的时区标识符说明

    • Asia/Shanghai – 上海

    • Asia/Chongqing – 重庆

    • Asia/Urumqi – 乌鲁木齐

    • Asia/Hong_Kong – 香港

    • Asia/Macao – 澳门

    • Asia/Taipei – 台北

    • Asia/Singapore – 新加坡

2、包含

2.1、include 和 require 语句

在 PHP 中,您可以在服务器执行 PHP 文件之前在该文件中插入一个文件的内容。

include 和 require 语句用于在执行流中插入写在其他文件中的有用的代码。

include 和 require 除了处理错误的方式不同之外,在其他方面都是相同的:

  • require 生成一个致命错误(E_COMPILE_ERROR),在错误发生后脚本会停止执行。
  • include 生成一个警告(E_WARNING),在错误发生后脚本会继续执行。

因此,如果您希望继续执行,并向用户输出结果,即使包含文件已丢失,那么请使用 include。否则,在框架、CMS 或者复杂的 PHP 应用程序编程中,请始终使用 require 向执行流引用关键文件。这有助于提高应用程序的安全性和完整性,在某个关键文件意外丢失的情况下。

包含文件省去了大量的工作。这意味着您可以为所有网页创建标准页头、页脚或者菜单文件。然后,在页头需要更新时,您只需更新这个页头包含文件即可。

2.2、include和require的区别

  • require 一般放在 PHP 文件的最前面,程序在执行前就会先导入要引用的文件;

  • include 一般放在程序的流程控制中,当程序执行时碰到才会引用,简化程序的执行流程。

  • require 引入的文件有错误时,执行会中断,并返回一个致命错误;

  • include 引入的文件有错误时,会继续执行,并返回一个警告。

3、文件

3.1、打开文件

fopen() 函数用于在 PHP 中打开文件。

<html>
<body>

<?php
$file=fopen("welcome.txt","r");
?>

</body>
</html>

**注释:**如果 fopen() 函数无法打开指定文件,则返回 0 (false)。

文件可能通过下列模式来打开:

模式描述
r只读。在文件的开头开始。
r+读/写。在文件的开头开始。
w只写。打开并清空文件的内容;如果文件不存在,则创建新文件。
w+读/写。打开并清空文件的内容;如果文件不存在,则创建新文件。
a追加。打开并向文件末尾进行写操作,如果文件不存在,则创建新文件。
a+读/追加。通过向文件末尾写内容,来保持文件内容。
x只写。创建新文件。如果文件已存在,则返回 FALSE 和一个错误。
x+读/写。创建新文件。如果文件已存在,则返回 FALSE 和一个错误。

3.2、关闭文件

fclose() 函数用于关闭打开的文件

<?php
$file = fopen("test.txt","r");

//执行一些代码

fclose($file);
?>

3.3、检测文件末尾(EOF)

feof() 函数检测是否已到达文件末尾(EOF)。

在循环遍历未知长度的数据时,feof() 函数很有用。

**注释:**在 w 、a 和 x 模式下,您无法读取打开的文件!

if (feof($file)) echo "文件结尾";

3.4、逐行读取文件

fgets() 函数用于从文件中逐行读取文件。

**注释:**在调用该函数之后,文件指针会移动到下一行。

<?php
$file = fopen("welcome.txt", "r") or exit("无法打开文件!");
// 读取文件每一行,直到文件结尾
while(!feof($file))
{
    echo fgets($file). "<br>";
}
fclose($file);
?>

3.5、逐字读取文件

fgetc() 函数用于从文件中逐字符地读取文件。

**注释:**在调用该函数之后,文件指针会移动到下一个字符。

<?php
$file=fopen("welcome.txt","r") or exit("无法打开文件!");
while (!feof($file))
{
    echo fgetc($file);
}
fclose($file);
?>

3.6、文件上传

通过 PHP,可以把文件上传到服务器,测试项目目录结构如下:

untitled
|-----upload             # 文件上传的目录
|-----form.html          # 表单文件
|-----upload_file.php    # php 上传代码
  • form.html文件

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>文件上传</title>
    </head>
    <body>
    
    <form action="upload_file.php" method="post" enctype="multipart/form-data">
        <label for="file">文件名:</label>
        <input type="file" name="file" id="file"><br>
        <input type="submit" name="submit" value="提交">
    </form>
    
    </body>
    </html>
    

    有关上面的 HTML 表单的一些注意项列举如下:

    • <form> 标签的 enctype 属性规定了在提交表单时要使用哪种内容类型。在表单需要二进制数据时,比如文件内容,请使用 “multipart/form-data”。
    • <input> 标签的 type=“file” 属性规定了应该把输入作为文件来处理。举例来说,当在浏览器中预览时,会看到输入框旁边有一个浏览按钮。
  • upload_file.php文件

    <?php
    // 允许上传的图片后缀
    $allowedExts = array("gif", "jpeg", "jpg", "png");
    $temp = explode(".", $_FILES["file"]["name"]);
    echo $_FILES["file"]["size"];
    $extension = end($temp);     // 获取文件后缀名
    if ((($_FILES["file"]["type"] == "image/gif")
            || ($_FILES["file"]["type"] == "image/jpeg")
            || ($_FILES["file"]["type"] == "image/jpg")
            || ($_FILES["file"]["type"] == "image/pjpeg")
            || ($_FILES["file"]["type"] == "image/x-png")
            || ($_FILES["file"]["type"] == "image/png"))
        && ($_FILES["file"]["size"] < 204800)   // 小于 200 kb
        && in_array($extension, $allowedExts)) {
        if ($_FILES["file"]["error"] > 0) {
            echo "错误:: " . $_FILES["file"]["error"] . "<br>";
        } else {
            echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
            echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
            echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
            echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>";
    
            // 判断当期目录下的 upload 目录是否存在该文件
            // 如果没有 upload 目录,你需要创建它,upload 目录权限为 777
            if (file_exists("upload/" . $_FILES["file"]["name"])) {
                echo $_FILES["file"]["name"] . " 文件已经存在。 ";
            } else {
                // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下
                move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
                echo "文件存储在: " . "upload/" . $_FILES["file"]["name"];
            }
        }
    } else {
        echo "非法的文件格式";
    }
    

    通过使用 PHP 的全局数组 $_FILES,你可以从客户计算机向远程服务器上传文件。

    第一个参数是表单的 input name,第二个下标可以是 “name”、“type”、“size”、“tmp_name” 或 “error”。如下所示:

    • $_FILES["file"]["name"] - 上传文件的名称
    • $_FILES["file"]["type"] - 上传文件的类型
    • $_FILES["file"]["size"] - 上传文件的大小,以字节计
    • $_FILES["file"]["tmp_name"] - 存储在服务器的文件的临时副本的名称
    • $_FILES["file"]["error"] - 由文件上传导致的错误代码

4、Cookie

​ cookie 常用于识别用户。cookie 是一种服务器留在用户计算机上的小文件。每当同一台计算机通过浏览器请求页面时,这台计算机将会发送 cookie。通过 PHP,您能够创建并取回 cookie 的值。

4.1、创建Cookie

setcookie() 函数用于设置 cookie。语法:setcookie(name, value, expire, path, domain);

  • setcookie() 函数必须位于<html>标签之前。
  • 在发送 cookie 时,cookie 的值会自动进行 URL 编码,在取回时进行自动解码。(为防止 URL 编码,请使用 setrawcookie() 取而代之。)
<?php
$expire=time()+60*60*24*30; //Cookie过期时间,单位是秒
setcookie("user", "runoob", $expire);
?>

<html>
.....

4.2、获取Cookie值

PHP 的 $_COOKIE 变量用于取回 cookie 的值。

//设置Cooke
<?php
setcookie("user", "runoob", time() + 3600);
?>
  
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Cookie</title>
</head>
<body>

<?php
// 输出 cookie 值
echo $_COOKIE["user"] . '<br>';

// 查看所有 cookie
print_r($_COOKIE);
print '<br>';
?>

<?php
if (isset($_COOKIE["user"]))
    echo "欢迎 " . $_COOKIE["user"] . "!<br>";
else
    echo "普通访客!<br>";
?>

</body>
</html>

4.3、删除Cookie

当删除 cookie 时,您应当使过期日期变更为过去的时间点。

<?php
// 设置 cookie 过期时间为过去 1 小时
setcookie("user", "", time()-3600);
?>

4.4、浏览器不支持Cookie

​ 如果您的应用程序需要与不支持 cookie 的浏览器打交道,那么您不得不使用其他的办法在您的应用程序中的页面之间传递信息。一种方式是通过表单传递数据。下面的表单在用户单点击 “Submit” 按钮时,向 “welcome.php” 提交了用户输入:

  • index.html文件

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Cookie</title>
    </head>
    <body>
    
    <form action="welcome.php" method="post">
        <label for="name">名字:</label><input id="name" type="text" name="name">
        <label for="age">年龄:</label><input id="age" type="text" name="age">
        <input type="submit">
    </form>
    
    </body>
    </html>
    
  • welcome.php文件

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Cookie</title>
    </head>
    <body>
    
    欢迎 <?php echo $_POST["name"]; ?>.<br><?php echo $_POST["age"]; ?> 岁了。
    
    </body>
    </html>
    

5、Session

​ PHP session 变量用于存储关于用户会话(session)的信息,或者更改用户会话(session)的设置。Session 变量存储单一用户的信息,并且对于应用程序中的所有页面都是可用的。

5.1、Session变量

​ 您在计算机上操作某个应用程序时,您打开它,做些更改,然后关闭它。这很像一次对话(Session)。计算机知道您是谁。它清楚您在何时打开和关闭应用程序。然而,在因特网上问题出现了:由于 HTTP 地址无法保持状态,Web 服务器并不知道您是谁以及您做了什么。

​ PHP session 解决了这个问题,它通过在服务器上存储用户信息以便随后使用(比如用户名称、购买商品等)。然而,会话信息是临时的,在用户离开网站后将被删除。如果您需要永久存储信息,可以把数据存储在数据库中。

​ Session 的工作机制是:为每个访客创建一个唯一的 id (UID),并基于这个 UID 来存储变量。UID 存储在 cookie 中,或者通过 URL 进行传导。

5.2、开启Session

在您把用户信息存储到 PHP session 中之前,首先必须启动会话。

**注意:**session_start() 函数必须位于<html>标签之前:

<?php session_start(); ?>

<html>
...

上面的代码会向服务器注册用户的会话,以便您可以开始保存用户信息,同时会为用户会话分配一个 UID。

5.3、存储Session

存储和取回 session 变量的正确方法是使用 PHP $_SESSION 变量

<?php
session_start();

// 存储 session 数据
if (isset($_SESSION['views'])) {
    $_SESSION['views'] = $_SESSION['views'] + 1;
} else {
    $_SESSION['views'] = 1;
}
?>


<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Cookie</title>
</head>
<body>

<?php
// 检索 session 数据
echo "浏览量:" . $_SESSION['views'];
?>

</body>
</html>

5.4、销毁Session

如果您希望删除某些 session 数据,可以使用 unset() 或 session_destroy() 函数。

  • unset() 函数用于释放指定的 session 变量

    <?php
    session_start();
    if(isset($_SESSION['views'])) {
        unset($_SESSION['views']);
    }
    ?>
    
  • session_destroy() 函数彻底销毁 session

    <?php
    session_destroy(); //session_destroy() 将重置session,将失去所有已存储的 session 数据。
    ?>
    

6、E-mail

PHP 允许您从脚本直接发送电子邮件。

6.1、mail()函数

mail() 函数用于从脚本中发送电子邮件,语法:mail(to,subject,message,headers,parameters)

参数描述
to必需。规定 email 接收者。
subject必需。规定 email 的主题。**注释:**该参数不能包含任何新行字符。
message必需。定义要发送的消息。应使用 LF (\n) 来分隔各行。每行应该限制在 70 个字符内。
headers可选。规定附加的标题,比如 From、Cc 和 Bcc。应当使用 CRLF (\r\n) 分隔附加的标题。
parameters可选。对邮件发送程序规定额外的参数。

**注意:**PHP 运行邮件函数需要一个已安装且正在运行的邮件系统(如:sendmail、postfix、qmail等)。所用的程序通过在 php.ini 文件中的配置设置进行定义。

6.2、简易的E-mail

​ 通过 PHP 发送电子邮件的最简单的方式是发送一封文本 email。

​ 在下面的实例中,我们首先声明变量($to, $subject, $message, $from, $headers),然后我们在 mail() 函数中使用这些变量来发送了一封 E-mail:

<?php
$to = "someone@example.com";         // 邮件接收者
$subject = "参数邮件";                // 邮件标题
$message = "Hello! 这是邮件的内容。";  // 邮件正文
$from = "someonelse@example.com";   // 邮件发送者
$headers = "From:" . $from;         // 头部信息设置
mail($to,$subject,$message,$headers);
echo "邮件已发送";
?>

6.3、E-mail表单

通过 PHP,您能够在自己的站点制作一个反馈表单。下面的实例向指定的 e-mail 地址发送了一条文本消息:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>E-mail表单</title>
</head>
<body>

<?php
if (isset($_REQUEST['email'])) { // 如果接收到邮箱参数则发送邮件
    // 发送邮件
    $email = $_REQUEST['email'];
    $subject = $_REQUEST['subject'];
    $message = $_REQUEST['message'];
    mail("someone@example.com", $subject,
        $message, "From:" . $email);
    echo "邮件发送成功";
} else { // 如果没有邮箱参数则显示表单
    echo "<form method='post' action='mailform.php'>
    Email: <input name='email' type='text'><br>
    Subject: <input name='subject' type='text'><br>
    Message:<br>
    <textarea name='message' rows='15' cols='40'>
    </textarea><br>
    <input type='submit'>
    </form>";
}
?>

</body>
</html>
  • 首先,检查是否填写了邮件输入框
  • 如果未填写(比如在页面被首次访问时),输出 HTML 表单
  • 如果已填写(在表单被填写后),从表单发送电子邮件
  • 当填写完表单点击提交按钮后,页面重新载入,可以看到邮件输入被重置,同时显示邮件发送成功的消息

6.4、E-mail注入

​ 以上代码(E-mail表单)存在的问题是,未经授权的用户可通过输入表单在邮件头部插入数据。假如用户在表单中的输入框内加入如下文本到电子邮件中,会出现什么情况呢?

someone@example.com%0ACc:person2@example.com
%0ABcc:person3@example.com,person3@example.com,
anotherperson4@example.com,person5@example.com
%0ABTo:person6@example.com

​ 与往常一样,mail() 函数把上面的文本放入邮件头部,那么现在头部有了额外的 Cc:、Bcc: 和 To: 字段。当用户点击提交按钮时,这封 e-mail 会被发送到上面所有的地址!

6.5、防止E-mail注入

防止 e-mail 注入的最好方法是对输入进行验证。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>防止E-mail注入</title>
</head>
<body>

<?php
function spamcheck($field)
{
    // filter_var() 过滤 e-mail
    // 使用 FILTER_SANITIZE_EMAIL
    $field = filter_var($field, FILTER_SANITIZE_EMAIL);

    //filter_var() 过滤 e-mail
    // 使用 FILTER_VALIDATE_EMAIL
    if (filter_var($field, FILTER_VALIDATE_EMAIL)) {
        return TRUE;
    } else {
        return FALSE;
    }
}

if (isset($_REQUEST['email'])) {
    // 如果接收到邮箱参数则发送邮件

    // 判断邮箱是否合法
    $mailcheck = spamcheck($_REQUEST['email']);
    if ($mailcheck == FALSE) {
        echo "非法输入";
    } else {
        // 发送邮件
        $email = $_REQUEST['email'];
        $subject = $_REQUEST['subject'];
        $message = $_REQUEST['message'];
        mail("someone@example.com", "Subject: $subject",
            $message, "From: $email");
        echo "Thank you for using our mail form";
    }
} else {
    // 如果没有邮箱参数则显示表单
    echo "<form method='post' action='mailform.php'>
    Email: <input name='email' type='text'><br>
    Subject: <input name='subject' type='text'><br>
    Message:<br>
    <textarea name='message' rows='15' cols='40'>
    </textarea><br>
    <input type='submit'>
    </form>";
}
?>

</body>
</html>

在上面的代码中,我们使用了 PHP 过滤器来对输入进行验证:

  • FILTER_SANITIZE_EMAIL 过滤器从字符串中删除电子邮件的非法字符
  • FILTER_VALIDATE_EMAIL 过滤器验证电子邮件地址的值

7、错误

​ 在 PHP 中,默认的错误处理很简单。一条错误消息会被发送到浏览器,这条消息带有文件名、行号以及描述错误的消息。

7.1、PHP错误处理

​ 在创建脚本和 Web 应用程序时,错误处理是一个重要的部分。如果您的代码缺少错误检测编码,那么程序看上去很不专业,也为安全风险敞开了大门。错误处理方法有:

  • 简单的 “die()” 语句
  • 自定义错误和错误触发器
  • 错误报告

7.2、用die()函数处理错误

​ 如果打开一个不存在的文件,会得到类似这样的错误:Warning: fopen(welcome.txt) [function.fopen]: failed to open stream:No such file or directory in ...

​ 为了避免用户得到类似上面的错误消息,我们在访问文件之前检测该文件是否存在:

<?php
if (!file_exists("welcome.txt")) {
    die("文件不存在");
} else {
    $file = fopen("welcome.txt", "r");
}

​ 相比之前的代码,上面的代码更有效,这是由于它采用了一个简单的错误处理机制在错误之后终止了脚本。然而,简单地终止脚本并不总是恰当的方式。让我们研究一下用于处理错误的备选的 PHP 函数。

7.3、自定义错误处理器

​ 创建一个自定义的错误处理器非常简单。我们很简单地创建了一个专用函数,可以在 PHP 中发生错误时调用该函数。该函数必须有能力处理至少两个参数 (error level 和 error message),但是可以接受最多五个参数(可选的:file, line-number 和 error context)。

  • 语法:

    error_function(error_level,error_message, error_file,error_line,error_context)

  • 错误报告级别

    这些错误报告级别是用户自定义的错误处理程序处理的不同类型的错误:

    常量描述
    2E_WARNING非致命的 run-time 错误。不暂停脚本执行。
    8E_NOTICErun-time 通知。在脚本发现可能有错误时发生,但也可能在脚本正常运行时发生。
    256E_USER_ERROR致命的用户生成的错误。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_ERROR。
    512E_USER_WARNING非致命的用户生成的警告。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_WARNING。
    1024E_USER_NOTICE用户生成的通知。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_NOTICE。
    4096E_RECOVERABLE_ERROR可捕获的致命错误。类似 E_ERROR,但可被用户定义的处理程序捕获。(参见 set_error_handler())
    8191E_ALL所有错误和警告。(在 PHP 5.4 中,E_STRICT 成为 E_ALL 的一部分)

    现在,让我们创建一个处理错误的函数

    function customError($errno, $errstr)
    {
        echo "<b>Error:</b> [$errno] $errstr<br>";
        echo "脚本结束";
        die();
    }
    

    ​ 上面的代码是一个简单的错误处理函数。当它被触发时,它会取得错误级别和错误消息。然后它会输出错误级别和消息,并终止脚本。

    ​ 现在,我们已经创建了一个错误处理函数,我们需要确定在何时触发该函数。

  • 设置错误处理程序

    ​ PHP 的默认错误处理程序是内建的错误处理程序。我们打算把上面的函数改造为脚本运行期间的默认错误处理程序。

    ​ 可以修改错误处理程序,使其仅应用到某些错误,这样脚本就能以不同的方式来处理不同的错误。然而,在本例中,我们打算针对所有错误来使用我们自定义的错误处理程序:

    <?php
    // 错误处理函数
    function customError($errno, $errstr)
    {
        echo "<b>Error:</b> [$errno] $errstr";
    }
    
    // 设置错误处理函数
    set_error_handler("customError"); //由于我们希望我们的自定义函数能处理所有错误,set_error_handler() 仅需要一个参数,可以添加第二个参数来规定错误级别。
    
    // 触发错误
    echo($test);
    ?>
    

7.4、触发错误

​ 在脚本中用户输入数据的位置,当用户的输入无效时触发错误是很有用的。在 PHP 中,这个任务由 trigger_error() 函数完成。

<?php
// 错误处理函数
function customError($errno, $errstr)
{
    echo "<b>Error:</b> [$errno] $errstr<br>";
    echo "脚本结束";
    die();
}

// 设置错误处理函数
set_error_handler("customError",E_USER_WARNING);

// 触发错误
$test=2;
if ($test>1) {
    trigger_error("变量值必须小于等于 1",E_USER_WARNING);
}

错误类型:

  • E_USER_ERROR - 致命的用户生成的 run-time 错误。错误无法恢复。脚本执行被中断。
  • E_USER_WARNING - 非致命的用户生成的 run-time 警告。脚本执行不被中断。
  • E_USER_NOTICE - 默认。用户生成的 run-time 通知。在脚本发现可能有错误时发生,但也可能在脚本正常运行时发生。

7.5、错误记录

​ 在默认的情况下,根据在 php.ini 中的 error_log 配置,PHP 向服务器的记录系统或文件发送错误记录。通过使用 error_log() 函数,您可以向指定的文件或远程目的地发送错误记录。

​ 通过电子邮件向您自己发送错误消息,是一种获得指定错误的通知的好办法。

7.6、通过E-mail发送错误

​ 在下面的例子中,如果特定的错误发生,我们将发送带有错误消息的电子邮件,并结束脚本

<?php
// 错误处理函数
function customError($errno, $errstr)
{
    echo "<b>Error:</b> [$errno] $errstr<br>";
    echo "已通知网站管理员";
    error_log("Error: [$errno] $errstr",1,
    "someone@example.com","From: webmaster@example.com");
}

// 设置错误处理函数
set_error_handler("customError",E_USER_WARNING);

// 触发错误
$test=2;
if ($test>1) {
    trigger_error("变量值必须小于等于 1",E_USER_WARNING);
}
?>

这个方法不适合所有的错误。常规错误应当通过使用默认的 PHP 记录系统在服务器上进行记录。

8、异常

PHP 5 提供了一种新的面向对象的错误处理方法。

异常处理用于在指定的错误(异常)情况发生时改变脚本的正常流程。这种情况称为异常。

当异常被触发时,通常会发生:

  • 当前代码状态被保存
  • 代码执行被切换到预定义(自定义)的异常处理器函数
  • 根据情况,处理器也许会从保存的代码状态重新开始执行代码,终止脚本执行,或从代码中另外的位置继续执行脚本

我们将展示不同的错误处理方法:

  • 异常的基本使用
  • 创建自定义的异常处理器
  • 多个异常
  • 重新抛出异常
  • 设置顶层异常处理器

**注意:**异常应该仅仅在错误情况下使用,而不应该用于在一个指定的点跳转到代码的另一个位置。

8.1、异常的基本使用

​ 当异常被抛出时,其后的代码不会继续执行,PHP 会尝试查找匹配的 “catch” 代码块。

​ 如果异常没有被捕获,而且又没用使用 set_exception_handler() 作相应的处理的话,那么将发生一个严重的错误(致命错误),并且输出 “Uncaught Exception” (未捕获异常)的错误消息。

​ 让我们尝试抛出一个异常,同时不去捕获它:

<?php
// 创建一个有异常处理的函数
function checkNum($number)
{
    if($number>1) {
        throw new Exception("Value must be 1 or below");
    }
    return true;
}
 
// 触发异常
checkNum(2);

上面的代码会得到类似这样的一个错误:

Fatal error: Uncaught exception 'Exception' with message 'Value must be 1 or below' in /www/runoob/test/test.php:7 Stack trace: #0 /www/runoob/test/test.php(13): checkNum(2) #1 {main} thrown in /www/runoob/test/test.php on line 7
8.1.1、Try、throw 和 catch

​ 要避免上面实例中出现的错误,我们需要创建适当的代码来处理异常。适当的处理异常代码应该包括:

  1. Try - 使用异常的函数应该位于 “try” 代码块内。如果没有触发异常,则代码将照常继续执行。但是如果异常被触发,会抛出一个异常。
  2. Throw - 里规定如何触发异常。每一个 “throw” 必须对应至少一个 “catch”。
  3. Catch - “catch” 代码块会捕获异常,并创建一个包含异常信息的对象。
<?php
// 创建一个有异常处理的函数
function checkNum($number)
{
    if ($number > 1) {
        throw new Exception("变量值必须小于等于 1");
    }
    return true;
}

// 在 try 块 触发异常
try {
    checkNum(2);
    // 如果抛出异常,以下文本不会输出
    echo '如果输出该内容,说明 $number 变量';
} // 捕获异常
catch (Exception $e) {
    echo 'Message: ' . $e->getMessage();
}

上面代码将得到类似这样一个错误:

Message: 变量值必须小于等于 1
8.1.2、实例解释

上面的代码抛出了一个异常,并捕获了它:

  1. 创建 checkNum() 函数。它检测数字是否大于 1。如果是,则抛出一个异常。
  2. 在 “try” 代码块中调用 checkNum() 函数。
  3. checkNum() 函数中的异常被抛出。
  4. “catch” 代码块接收到该异常,并创建一个包含异常信息的对象 ($e)。
  5. 通过从这个 exception 对象调用 $e->getMessage(),输出来自该异常的错误消息。

8.2、自定义Exception类

​ 创建自定义的异常处理程序非常简单。我们简单地创建了一个专门的类,当 PHP 中发生异常时,可调用其函数。该类必须是 exception 类的一个扩展。

​ 这个自定义的 customException 类继承了 PHP 的 exception 类的所有属性,您可向其添加自定义的函数。

<?php

class customException extends Exception
{
    public function errorMessage()
    {
        // 错误信息
        $errorMsg = '错误行号 ' . $this->getLine() . ' in ' . $this->getFile()
            . ': <b>' . $this->getMessage() . '</b> 不是一个合法的 E-Mail 地址';
        return $errorMsg;
    }
}

$email = "someone@example...com";

try {
    // 检测邮箱
    if (filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
        // 如果是个不合法的邮箱地址,抛出异常
        throw new customException($email);
    }
} catch (customException $e) {
//display custom message
    echo $e->errorMessage();
}

​ 这个新的类是旧的 exception 类的副本,外加 errorMessage() 函数。正因为它是旧类的副本,因此它从旧类继承了属性和方法,我们可以使用 exception 类的方法,比如 getLine()、getFile() 和 getMessage()。

实例解释:

上面的代码抛出了一个异常,并通过一个自定义的 exception 类来捕获它:

  1. customException() 类是作为旧的 exception 类的一个扩展来创建的。这样它就继承了旧的 exception 类的所有属性和方法。
  2. 创建 errorMessage() 函数。如果 e-mail 地址不合法,则该函数返回一条错误消息。
  3. 把 $email 变量设置为不合法的 e-mail 地址字符串。
  4. 执行 “try” 代码块,由于 e-mail 地址不合法,因此抛出一个异常。
  5. “catch” 代码块捕获异常,并显示错误消息。

8.3、多个异常

​ 可以为一段脚本使用多个异常,来检测多种情况。

​ 可以使用多个 if…else 代码块,或一个 switch 代码块,或者嵌套多个异常。这些异常能够使用不同的 exception 类,并返回不同的错误消息

<?php

class customException extends Exception
{
    public function errorMessage()
    {
        // 错误信息
        $errorMsg = '错误行号 ' . $this->getLine() . ' in ' . $this->getFile()
            . ': <b>' . $this->getMessage() . '</b> 不是一个合法的 E-Mail 地址';
        return $errorMsg;
    }
}

$email = "someone@example.com";

try {
    // 检测邮箱
    if (filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
        // 如果是个不合法的邮箱地址,抛出异常
        throw new customException($email);
    }
    // 检测 "example" 是否在邮箱地址中
    if (strpos($email, "example") !== FALSE) {
        throw new Exception("$email 是 example 邮箱");
    }
} catch (customException $e) {
    echo $e->errorMessage();
} catch (Exception $e) {
    echo $e->getMessage();
}

实例解释:

上面的代码测试了两种条件,如果其中任何一个条件不成立,则抛出一个异常:

  1. customException() 类是作为旧的 exception 类的一个扩展来创建的。这样它就继承了旧的 exception 类的所有属性和方法。
  2. 创建 errorMessage() 函数。如果 e-mail 地址不合法,则该函数返回一个错误消息。
  3. 把 $email 变量设置为一个字符串,该字符串是一个有效的 e-mail 地址,但包含字符串 “example”。
  4. 执行 “try” 代码块,在第一个条件下,不会抛出异常。
  5. 由于 e-mail 含有字符串 “example”,第二个条件会触发异常。
  6. “catch” 代码块会捕获异常,并显示恰当的错误消息。

如果 customException 类抛出了异常,但没有捕获 customException,仅仅捕获了 base exception,则在那里处理异常。

8.4、重新抛出异常

​ 有时,当异常被抛出时,您也许希望以不同于标准的方式对它进行处理。可以在一个 “catch” 代码块中再次抛出异常。

​ 脚本应该对用户隐藏系统错误。对程序员来说,系统错误也许很重要,但是用户对它们并不感兴趣。为了让用户更容易使用,您可以再次抛出带有对用户比较友好的消息的异常:

<?php

class customException extends Exception
{
    public function errorMessage()
    {
        // 错误信息
        $errorMsg = $this->getMessage() . ' 不是一个合法的 E-Mail 地址。';
        return $errorMsg;
    }
}

$email = "someone@example.com";

try {
    try {
        // 检测 "example" 是否在邮箱地址中
        if (strpos($email, "example") !== FALSE) {
            // 如果是个不合法的邮箱地址,抛出异常
            throw new Exception($email);
        }
    } catch (Exception $e) {
        // 重新抛出异常
        throw new customException($email);
    }
} catch (customException $e) {
    // 显示自定义信息
    echo $e->errorMessage();
}

实例解释:

上面的代码检测在邮件地址中是否含有字符串 “example”。如果有,则再次抛出异常:

  1. customException() 类是作为旧的 exception 类的一个扩展来创建的。这样它就继承了旧的 exception 类的所有属性和方法。
  2. 创建 errorMessage() 函数。如果 e-mail 地址不合法,则该函数返回一个错误消息。
  3. 把 $email 变量设置为一个字符串,该字符串是一个有效的 e-mail 地址,但包含字符串 “example”。
  4. “try” 代码块包含另一个 “try” 代码块,这样就可以再次抛出异常。
  5. 由于 e-mail 包含字符串 “example”,因此触发异常。
  6. “catch” 代码块捕获到该异常,并重新抛出 “customException”。
  7. 捕获到 “customException”,并显示一条错误消息。

如果在当前的 “try” 代码块中异常没有被捕获,则它将在更高层级上查找 catch 代码块。

8.5、设置顶层异常处理器

set_exception_handler() 函数可设置处理所有未捕获异常的用户定义函数。

<?php
function myException($exception)
{
    echo "<b>Exception:</b> " , $exception->getMessage();
}
 
set_exception_handler('myException');
 
throw new Exception('Uncaught Exception occurred');
?>

以上代码的输出如下所示:

Exception: Uncaught Exception occurred

在上面的代码中,不存在 “catch” 代码块,而是触发顶层的异常处理程序。应该使用此函数来捕获所有未被捕获的异常。

8.6、异常的规则

  • 需要进行异常处理的代码应该放入 try 代码块内,以便捕获潜在的异常。
  • 每个 try 或 throw 代码块必须至少拥有一个对应的 catch 代码块。
  • 使用多个 catch 代码块可以捕获不同种类的异常。
  • 可以在 try 代码块内的 catch 代码块中抛出(再次抛出)异常。

简而言之:如果抛出了异常,就必须捕获它。

9、过滤器

  • 什么是PHP过滤器?

    1. PHP 过滤器用于验证和过滤来自非安全来源的数据。
    2. 测试、验证和过滤用户输入或自定义数据是任何 Web 应用程序的重要组成部分。
    3. PHP 的过滤器扩展的设计目的是使数据过滤更轻松快捷。
  • 为什么使用过滤器?

    几乎所有的 Web 应用程序都依赖外部的输入。这些数据通常来自用户或其他应用程序(比如 web 服务)。通过使用过滤器,您能够确保应用程序获得正确的输入类型。

    您应该始终对外部数据进行过滤!

    输入过滤是最重要的应用程序安全课题之一。

  • 什么是外部数据?

    • 来自表单的输入数据
    • Cookies
    • Web services data
    • 服务器变量
    • 数据库查询结果

9.1、函数和过滤器

如需过滤变量,请使用下面的过滤器函数之一:

  • filter_var() - 通过一个指定的过滤器来过滤单一的变量
  • filter_var_array() - 通过相同的或不同的过滤器来过滤多个变量
  • filter_input - 获取一个输入变量,并对它进行过滤
  • filter_input_array - 获取多个输入变量,并通过相同的或不同的过滤器对它们进行过滤
<?php
$int = '123'; //或$int=123,在下面检查中都是合法的,因为检查的是值而非类型
if (filter_var($int, FILTER_VALIDATE_INT)) {
    print '是个合法的整数';
} else {
    print '不合法的整数';
}
// 上面的代码使用了 "FILTER_VALIDATE_INT" 过滤器来过滤变量
  • validating类型过滤器
    • 用于验证用户输入
    • 严格的格式规则(比如 URL 或 E-Mail 验证)
    • 如果成功则返回预期的类型,如果失败则返回 FALSE
  • sanitizing类型过滤器
    • 用于允许或禁止字符串中指定的字符
    • 无数据格式规则
    • 始终返回字符串
9.1.1、PHP Filter 函数
函数描述
filter_has_var()检查是否存在指定输入类型的变量。
filter_id()返回指定过滤器的 ID 号。
filter_input()从脚本外部获取输入,并进行过滤。
filter_input_array()从脚本外部获取多项输入,并进行过滤。
filter_list()返回包含所有得到支持的过滤器的一个数组。
filter_var_array()获取多个变量,并进行过滤。
filter_var()获取一个变量,并进行过滤。
9.1.2、PHP Filters
ID 名称描述
FILTER_CALLBACK调用用户自定义函数来过滤数据。
FILTER_SANITIZE_STRING去除标签,去除或编码特殊字符。
FILTER_SANITIZE_STRIPPED“string” 过滤器的别名。
FILTER_SANITIZE_ENCODEDURL-encode 字符串,去除或编码特殊字符。
FILTER_SANITIZE_SPECIAL_CHARSHTML 转义字符 '"<>& 以及 ASCII 值小于 32 的字符。
FILTER_SANITIZE_EMAIL删除所有字符,除了字母、数字以及 !#$%&’*±/=?^_`{|}~@.[]
FILTER_SANITIZE_URL删除所有字符,除了字母、数字以及 $-_.+!*’(),{}|^~[]`<>#%";/??&=
FILTER_SANITIZE_NUMBER_INT删除所有字符,除了数字和 ±
FILTER_SANITIZE_NUMBER_FLOAT删除所有字符,除了数字、± 以及 .,eE
FILTER_SANITIZE_MAGIC_QUOTES应用 addslashes()。
FILTER_UNSAFE_RAW不进行任何过滤,去除或编码特殊字符。
FILTER_VALIDATE_INT把值作为整数来验证。
FILTER_VALIDATE_BOOLEAN把值作为布尔选项来验证。如果是 “1”、“true”、“on” 和 “yes”,则返回 TRUE。如果是 “0”、“false”、“off”、“no” 和 “”,则返回 FALSE。否则返回 NULL。
FILTER_VALIDATE_FLOAT把值作为浮点数来验证。
FILTER_VALIDATE_REGEXP根据 regexp(一种兼容 Perl 的正则表达式)来验证值。
FILTER_VALIDATE_URL把值作为 URL 来验证。
FILTER_VALIDATE_EMAIL把值作为 e-mail 地址来验证。
FILTER_VALIDATE_IP把值作为 IP 地址来验证,只限 IPv4 或 IPv6 或 不是来自私有或者保留的范围。

9.2、选项和标志

选项和标志用于向指定的过滤器添加额外的过滤选项。不同的过滤器有不同的选项和标志。

<?php
$var = 100;

$int_options = [
    'options' => [
        'min_range' => 0,
        'max_range' => 256
    ]
];

if (filter_var($var, FILTER_VALIDATE_INT, $int_options)) {
    print '是个合法的整数';
} else {
    print '不合法的整数';
}
/**
 * 我们用 filter_var() 和 "min_range" 以及 "max_range" 选项验证了一个整数
 * 选项必须放入一个名为 "options" 的相关数组中。如果使用标志,则不需在数组内
 * 由于整数是 "300",它不在指定的范围内,以上代码的输出将是:"不是一个合法的整数"
 */

9.3、验证输入

让我们试着验证来自表单的输入。

我们需要做的第一件事情是确认是否存在我们正在查找的输入数据。

然后我们用 filter_input() 函数过滤输入的数据。

<?php
if (!filter_has_var(INPUT_GET, "email")) {
    echo("没有 email 参数");
} else {
    if (!filter_input(INPUT_GET, "email", FILTER_VALIDATE_EMAIL)) {
        echo "不是一个合法的 E-Mail";
    } else {
        echo "是一个合法的 E-Mail";
    }
}
/**
 * 检测是否存在 "GET" 类型的 "email" 输入变量
 * 如果存在输入变量,检测它是否是有效的 e-mail 地址
 */
  1. 启动PHPWeb程序
  2. 在浏览器地址栏输入:“http://localhost:8080/HelloWorld.php?email=test@163.com”,浏览器显示:“是一个合法的E-Mail”

9.4、净化输入

让我们试着清理一下从表单传来的 URL。

首先,我们要确认是否存在我们正在查找的输入数据。

然后,我们用 filter_input() 函数来净化输入数据。

<?php
if (!filter_has_var(INPUT_GET, "url")) {
    echo("没有 url 参数");
} else {
    $url = filter_input(INPUT_GET,
        "url", FILTER_SANITIZE_URL);
    echo $url;
}
/**
 * 检测是否存在 "GET" 类型的 "url" 输入变量
 * 如果存在此输入变量,对其进行净化(删除非法字符),并将其存储在 $url 变量中
 */
  1. 启动PHPWeb程序
  2. 在浏览器地址栏输入:“http://localhost:8080/HelloWorld.php?url=http://www:båaåidøu.com”,浏览器显示:“http://www:baidu.com”

9.5、过滤多个输入

​ 表单通常由多个输入字段组成。为了避免对 filter_var 或 filter_input 函数重复调用,我们可以使用 filter_var_array 或 the filter_input_array 函数。

​ 在本例中,我们使用 filter_input_array() 函数来过滤三个 GET 变量。接收到的 GET 变量是一个名字、一个年龄以及一个 e-mail 地址

<?php
$filters = [
    "name" => [
        "filter" => FILTER_SANITIZE_STRING
    ],
    "age" => [
        "filter" => FILTER_VALIDATE_INT,
        "options" => [
            "min_range" => 1,
            "max_range" => 120
        ]
    ],
    "email" => FILTER_VALIDATE_EMAIL
];

$result = filter_input_array(INPUT_GET, $filters);

if (!$result["age"]) {
    echo("年龄必须在1到120之间。<br>");
} elseif (!$result["email"]) {
    echo("E-Mail不合法<br>");
} else {
    echo("输入正确");
}
/** 通过 "GET" 方法传送的输入变量 (name、age 和 email):
 * 1.设置一个数组,其中包含了输入变量的名称和用于指定的输入变量的过滤器
 * 2.调用 filter_input_array() 函数,参数包括 GET 输入变量及刚才设置的数组
 * 3.检测 $result 变量中的 "age" 和 "email" 变量是否有非法的输入。(如果存在非法输入,在使用 filter_input_array() 函数之后,输入变量为 FALSE。)
 */
/**filter_input_array() 函数的第二个参数可以是数组或单一过滤器的 ID。
 **如果该参数是单一过滤器的 ID,那么这个指定的过滤器会过滤输入数组中所有的值。
 **如果该参数是一个数组,那么此数组必须遵循下面的规则:
 * 1.必须是一个关联数组,其中包含的输入变量是数组的键(比如 "age" 输入变量)
 * 2.此数组的值必须是过滤器的 ID ,或者是规定了过滤器、标志和选项的数组
 */

9.6、Filter Callback

​ 通过使用 FILTER_CALLBACK 过滤器,可以调用自定义的函数,把它作为一个过滤器来使用。这样,我们就拥有了数据过滤的完全控制权。

​ 您可以创建自己的自定义函数,也可以使用已存在的 PHP 函数。

​ 将您准备用到的过滤器的函数,按指定选项的规定方法进行规定。在关联数组中,带有名称 “options”。

​ 在下面的实例中,我们使用了一个自定义的函数把所有 “_” 转换为 “.”:

<?php
function convertSpace($string)
{
    return str_replace("_", ".", $string);
}

$string = "www_runoob_com!";

echo filter_var($string, FILTER_CALLBACK, ["options" => "convertSpace"]);

/**上面的实例把所有 "_" 转换成 "." :
 * 1.创建一个把 "_" 替换为 "." 的函数
 * 2.调用 filter_var() 函数,它的参数是 FILTER_CALLBACK 过滤器以及包含我们的函数的数组
 */

9.7、高级过滤器

  • 检测一个数字是否在一个范围内

    <?php
    $int = 123;
    $min = 1;
    $max = 256;
    
    if (filter_var($int, FILTER_VALIDATE_INT, ['options' => ['min_range'=>$min, 'max_range'=>$max]])) {
        print '变量值在合法范围内';
    } else {
        print '变量值不在合法范围内';
    }
    
  • 检测IPv6地址

    <?php
    $ip = 'fe80::cc43:20ff:fe8d:f7aa';
    
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
        print $ip . '是IPv6地址';
    } else {
        print $ip . '不是IPv6地址';
    }
    
  • 检测URL-必须包含QUERY_STRING(查询字符串)

    <?php
    $url = 'http://www.baidu.com';
    
    if (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED)) {
        print $url . '是一个合法的URL';
    } else {
        print $url . '不是一个合法的URL';
    }
    
  • 移除ASCII值大于127的字符

    <?php
    $str = '<h1>Hello WorldÆØÅ!</h1>';
    
    $newstr = filter_var($str, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH);
    print $newstr;
    

10、JSON

在 php5.2.0 及以上版本已经内置 JSON 扩展。

函数描述
json_encode对变量进行 JSON 编码
json_decode对 JSON 格式的字符串进行解码,转换为 PHP 变量
json_last_error返回最后发生的错误

10.1、json_encode

PHP json_encode() 用于对变量进行 JSON 编码,该函数如果执行成功返回 JSON 数据,否则返回 FALSE 。

  • 语法: string json_encode ( $value [, $options = 0 ] )

  • 参数

    • value: 要编码的值。该函数只对 UTF-8 编码的数据有效。
    • options:由以下常量组成的二进制掩码:JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK,JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT
  • 实例

    将 PHP 数组转换为 JSON 格式数据

    <?php
    $arr = [
        'a' => 1,
        'b' => 2,
        'c' => 3,
        'd' => 4,
        'e' => 5
    ];
    print json_encode($arr);//输出结果:{"a":1,"b":2,"c":3,"d":4,"e":5}
    

    将 PHP 对象转换为 JSON 格式数据

    <?php
    ini_set('date.timezone','Asia/Shanghai');
    class Emp {
        public $name = "";
        public $hobbies  = "";
        public $birthdate = "";
    }
    $e = new Emp();
    $e->name = "Frank Hu";
    $e->hobbies  = "Belle";
    $e->birthdate = date('Y-m-d',611161200);
    
    echo json_encode($e);//输出结果:{"name":"Frank Hu","hobbies":"Belle","birthdate":"1989-05-15"}
    

10.2、json_decode

PHP json_decode() 函数用于对 JSON 格式的字符串进行解码,并转换为 PHP 变量。

  • 语法:

    mixed json_decode ($json_string [,$assoc = false [, $depth = 512 [, $options = 0 ]]])
    
  • 参数

    • json_string: 待解码的 JSON 字符串,必须是 UTF-8 编码数据
    • assoc: 当该参数为 TRUE 时,将返回数组,FALSE 时返回对象。
    • depth: 整数类型的参数,它指定递归深度
    • options: 二进制掩码,目前只支持 JSON_BIGINT_AS_STRING 。
  • 实例

    <?php
    $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
    
    var_dump(json_decode($json)); //返回对象
    var_dump(json_decode($json, true)); //返回数组
    

五、PHP操作MySQL

1、连接

PHP 5 及以上版本建议使用以下方式连接 MySQL :

  • MySQLi extension (“i” 意为 improved)
  • PDO (PHP Data Objects)

在 PHP 早期版本中我们使用 MySQL 扩展。但该扩展在 2012 年开始不建议使用。

在我们访问 MySQL 数据库前,我们需要先连接到数据库服务器。

重点:

  • 检查php目录的ext目录下是否有该扩展文件,如php_mysqli.dll,php_pdo_mysql.dll,如果没有下载放入该目录下
  • 打开php目录的配置文件php.ini,将extension=mysqli和extension=pdo_mysql前面你的分号去掉
  • 5.3+默认已经安装了MySQL扩展,在php.ini里面配置下即可使用;5.3以下的需要先按照MySQL扩展之后再在php.ini里面配置。

1.1、连接(面向过程)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';

/*创建连接*/
$conn = mysqli_connect($host, $username, $password);

/*检查连接*/
if ($conn) {
    echo '连接成功';
} else {
    die('连接失败: ' . mysqli_connect_error());
}

1.2、连接(面向对象)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';

/*创建连接*/
$conn = new mysqli($host, $username, $password);

/*检查连接*/
if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
} else {
    echo '连接成功';
}

//注意在以上面向对象的实例中 $connect_error 是在 PHP 5.2.9 和 5.3.0 中添加的。如果你需要兼容更早版本 请使用以下代码替换:
/**
if (mysqli_connect_error()) {
    die("数据库连接失败: " . mysqli_connect_error());
}*/

1.3、连接(PDO)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

try {
    $conn = new PDO("mysql:host=$host;", $username, $password);
} catch (PDOException $e) {
    echo $e->getMessage();
}

// 注意在以上 PDO 实例中我们已经指定了数据库 (myDB)。PDO 在连接过程需要设置数据库名。如果没有指定,则会抛出异常。

1.4、关闭连接

  • 面向过程:mysqli_close($conn);
  • 面向对象:$conn->close();
  • PDO:$conn = null;

2、建库

CREATE DATABASE 语句用于在 MySQL 中创建数据库。

2.1、建库(面向过程)
<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';

/*创建连接*/
$conn = mysqli_connect($host, $username, $password);

/*检查连接*/
if (!$conn) {
    die('连接失败: ' . mysqli_connect_error());
}

/*创建数据库*/
$sql = "CREATE DATABASE myDB";
if (mysqli_query($conn,$sql)) {
    echo '数据库创建成功';
} else {
    echo '数据库创建失败:' . mysqli_error($conn);
}

2.2、建库(面向对象)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';

/*创建连接*/
$conn = new mysqli($host, $username, $password);

/*检查连接*/
if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
}

/*创建数据库*/
$sql = "CREATE DATABASE myDB";
if ($conn->query($sql)) {
    echo '数据库创建成功';
} else {
    echo '数据库创建失败:' . $conn->error;
}

2.3、建库(PDO)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';


try {
    $conn = new PDO("mysql:host=$host", $username, $password);

    // 设置 PDO 错误模式为异常
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $sql = "CREATE DATABASE myDBPDO";

    // 使用 exec() ,因为没有结果返回
    $conn->exec($sql);

    echo "数据库创建成功<br>";
} catch (PDOException $e) {
    echo $sql . "\n" . $e->getMessage();
}

提示: 使用 PDO 的最大好处是在数据库查询过程出现问题时可以使用异常类来 处理问题。如果 try{ } 代码块出现异常,脚本会停止执行并会跳到第一个 catch(){ } 代码块执行代码。 在以上捕获的代码块中我们输出了 SQL 语句并生成错误信息。

3、建表

CREATE TABLE 语句用于创建 MySQL 表。创建表前,我们需要使用 use myDB 来选择要操作的数据库:

3.1、建表(面向过程)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

/*创建连接*/
$conn = mysqli_connect($host, $username, $password, $dbname);

/*检查连接*/
if (!$conn) {
    die('连接失败: ' . mysqli_connect_error());
}

/*使用 sql 创建数据表*/
$sql = "CREATE TABLE MyGuests (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
firstname VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
email VARCHAR(50),
reg_date TIMESTAMP
)";

/*创建表*/
if (mysqli_query($conn, $sql)) {
    echo "数据表 MyGuests 创建成功";
} else {
    echo "创建数据表错误:" . mysqli_error($conn);
}

/*关闭连接*/
mysqli_close($conn);

3.2、建表(面向对象)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

/*创建连接*/
$conn = new mysqli($host, $username, $password, $dbname);

/*检查连接*/
if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
}

/*使用 sql 创建数据表*/
$sql = "CREATE TABLE MyGuests (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
firstname VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
email VARCHAR(50),
reg_date TIMESTAMP
)";

/*创建表*/
if ($conn->query($sql)) {
    echo "数据表 MyGuests 创建成功";
} else {
    echo "创建数据表错误:" . $conn->error;
}


/*关闭连接*/
$conn->close();

3.3、建表(PDO)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

try {
    $conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);

    /*设置 PDO 错误模式,用于抛出异常*/
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION
    );
    /*使用 sql 创建数据表*/
    $sql = "CREATE TABLE MyGuests (
    id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
    firstname VARCHAR(30) NOT NULL,
    lastname VARCHAR(30) NOT NULL,
    email VARCHAR(50),
    reg_date TIMESTAMP
    )";

    /*使用 exec() ,没有结果返回*/
    $conn->exec($sql);
    echo "数据表 MyGuests 创建成功";
} catch (PDOException $e) {
    echo $sql . "\n" . $e->getMessage();
}

$conn = null;

4、插入数据

在创建完数据库和表后,我们可以向表中添加数据。

以下为一些语法规则:

  • PHP 中 SQL 查询语句必须使用引号
  • 在 SQL 查询语句中的字符串值必须加引号
  • 数值的值不需要引号
  • NULL 值不需要引号

4.1、增(面向过程)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

/*创建连接*/
$conn = mysqli_connect($host, $username, $password, $dbname);

/*检查连接*/
if (!$conn) {
    die('连接失败: ' . mysqli_connect_error());
}

/*插入数据SQL语言*/
$sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('John', 'Doe', 'john@example.com')";

if (mysqli_query($conn, $sql)) {
    echo "插入成功!";
} else {
    echo "Error: " . $sql . "\n" . mysqli_error($conn);
}

/**
 * 插入多个值
 * SQL语句:
 *   $sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('John', 'Doe', 'john@example.com');";
 *   $sql .= "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('Mary', 'Moe', 'mary@example.com');";
 *   $sql .= "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('Julie', 'Dooley', 'julie@example.com')";
 */

4.2、增(面向对象)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

/*创建连接*/
$conn = new mysqli($host, $username, $password, $dbname);

/*检查连接*/
if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
}

/*插入数据SQL语言*/
$sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('John', 'Doe', 'john@example.com')";

/*插入数据*/
if ($conn->query($sql)) {
    echo "数据表 MyGuests 创建成功";
} else {
    echo "创建数据表错误:" . $conn->error;
}

/*关闭连接*/
$conn->close();

4.3、增(PDO)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

try {
    $conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);

    /*设置 PDO 错误模式,用于抛出异常*/
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION
    );
    /*插入数据SQL语言*/
    $sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('John', 'Doe', 'john@example.com')";

    /*使用 exec() ,没有结果返回*/
    $conn->exec($sql);
    echo "插入数据成功!!!";
} catch (PDOException $e) {
    echo $sql . "\n" . $e->getMessage();
}

$conn = null;

4.5、执行多条语句

  • SQL语句拼接

    $sql = "INSERT INTO MyGuests (firstname, lastname, email)
    VALUES ('John', 'Doe', 'john@example.com');";
    $sql .= "INSERT INTO MyGuests (firstname, lastname, email)
    VALUES ('Mary', 'Moe', 'mary@example.com');";
    $sql .= "INSERT INTO MyGuests (firstname, lastname, email)
    VALUES ('Julie', 'Dooley', 'julie@example.com')";
    
  • 面向过程用mysqli_multi_query($conn, $sql) ,面向对象用 $conn->multi_query($sql) ,这里的$sql 就是上面拼接出来的sql语句字符串。

  • PDO

    <?php
    /*定义数据库信息变量*/
    $host = '192.168.1.111';
    $username = 'phpuser';
    $password = '123qaz!@#QAZ';
    $dbname = 'phpdb';
    
    try {
        $conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
        /*设置 PDO 错误模式,用于抛出异常*/
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
        // 开始事务
        $conn->beginTransaction();
        // SQL 语句
        $conn->exec("INSERT INTO MyGuests (firstname, lastname, email) 
        VALUES ('John', 'Doe', 'john@example.com')");
        $conn->exec("INSERT INTO MyGuests (firstname, lastname, email) 
        VALUES ('Mary', 'Moe', 'mary@example.com')");
        $conn->exec("INSERT INTO MyGuests (firstname, lastname, email) 
        VALUES ('Julie', 'Dooley', 'julie@example.com')");
    
        // 提交事务
        $conn->commit();
        echo "新记录插入成功";
    } catch(PDOException $e) {
        // 如果执行失败回滚
        $conn->rollback();
        echo $e->getMessage();
    }
    
    $conn = null;
    

5、预处理语句

​ 预处理语句对于防止 MySQL 注入是非常有用的。预处理语句用于执行多个相同的 SQL 语句,并且执行效率更高。预处理语句的工作原理如下:

  1. 预处理:创建 SQL 语句模板并发送到数据库。预留的值使用参数 “?” 标记 。例如:

    INSERT INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?)
    
  2. 数据库解析,编译,对SQL语句模板执行查询优化,并存储结果不输出。

  3. 执行:最后,将应用绑定的值传递给参数("?" 标记),数据库执行语句。应用可以多次执行语句,如果参数的值不一样。

**相比于直接执行SQL语句,预处理语句有两个主要优点: **

  • 预处理语句大大减少了分析时间,只做了一次查询(虽然语句多次执行)。
  • 绑定参数减少了服务器带宽,你只需要发送查询的参数,而不是整个语句。
  • 预处理语句针对SQL注入是非常有用的,因为参数值发送后使用不同的协议,保证了数据的合法性。

5.1、MySQLi预处理语句

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

/*创建连接*/
$conn = new mysqli($host, $username, $password, $dbname);

/*检查连接*/
if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
}

/*预处理及绑定*/
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);

/*设置参数并执行*/
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();

$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();

$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();

echo "新记录插入成功";

$stmt->close();
$conn->close();
  • bind_param() 函数

    $stmt->bind_param("sss", $firstname, $lastname, $email);
    // 该函数绑定了 SQL 的参数,且告诉数据库参数的值。 "sss" 参数列处理其余参数的数据类型。s 字符告诉数据库该参数为字符串。
    
  • 预处理数据类型

    • i - integer(整型)
    • d - double(双精度浮点型)
    • s - string(字符串)
    • b - BLOB(binary large object:二进制大对象)
  • 每个参数都需要指定类型。通过告诉数据库参数的数据类型,可以降低 SQL 注入的风险。

5.2、PDO中的预处理语句

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

try {
    $conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
    // 设置 PDO 错误模式为异常
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 预处理 SQL 并绑定参数
    $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) 
    VALUES (:firstname, :lastname, :email)");
    $stmt->bindParam(':firstname', $firstname);
    $stmt->bindParam(':lastname', $lastname);
    $stmt->bindParam(':email', $email);

    // 插入行
    $firstname = "John";
    $lastname = "Doe";
    $email = "john@example.com";
    $stmt->execute();

    // 插入其他行
    $firstname = "Mary";
    $lastname = "Moe";
    $email = "mary@example.com";
    $stmt->execute();

    // 插入其他行
    $firstname = "Julie";
    $lastname = "Dooley";
    $email = "julie@example.com";
    $stmt->execute();

    echo "新记录插入成功";
} catch(PDOException $e) {
    echo "Error: " . $e->getMessage();
}
$conn = null;

6、查询数据

6.1、查(面向过程)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

/*创建连接*/
$conn = mysqli_connect($host, $username, $password, $dbname);

/*检查连接*/
if (!$conn) {
    die('连接失败: ' . mysqli_connect_error());
}

$sql = "SELECT id, firstname, lastname FROM MyGuests";
$result = mysqli_query($conn, $sql);

if (mysqli_num_rows($result)) {
    /*输出数据*/
    while ($row = mysqli_fetch_assoc($result)) {
        echo "id: " . $row["id"]. " - Name: " . $row["firstname"]. " " . $row["lastname"]. "\n";
    }
} else {
    echo "0 结果";
}

mysqli_close($conn);

6.2、查(面向对象)

<?php
/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

/*创建连接*/
$conn = new mysqli($host, $username, $password, $dbname);

/*检查连接*/
if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
}


$sql = "SELECT id, firstname, lastname FROM MyGuests";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    // 输出数据
    while ($row = $result->fetch_assoc()) {
        echo "id: " . $row["id"] . " - Name: " . $row["firstname"] . " " . $row["lastname"] . "\n";
    }
} else {
    echo "0 结果";
}
$conn->close();

​ 函数 num_rows() 判断返回的数据。如果返回的是多条数据,函数 fetch_assoc() 将结合集放入到关联数组并循环输出。

6.3、查(PDO)

<?php
echo "<table style='border: solid 1px black;'>";
echo "<tr><th>Id</th><th>Firstname</th><th>Lastname</th></tr>";
class TableRows extends RecursiveIteratorIterator
{
    function __construct($it)
    {
        parent::__construct($it, self::LEAVES_ONLY);
    }

    function current()
    {
        return "<td style='width:150px;border:1px solid black;'>" . parent::current() . "</td>";
    }

    function beginChildren()
    {
        echo "<tr>";
    }

    function endChildren()
    {
        echo "</tr>" . "\n";
    }
}


/*定义数据库信息变量*/
$host = '192.168.1.111';
$username = 'phpuser';
$password = '123qaz!@#QAZ';
$dbname = 'phpdb';

try {
    $conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $stmt = $conn->prepare("SELECT id, firstname, lastname FROM MyGuests");
    $stmt->execute();

    // 设置结果集为关联数组
    $result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
    foreach (new TableRows(new RecursiveArrayIterator($stmt->fetchAll())) as $k => $v) {
        echo $v;
    }
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}
$conn = null;
echo "</table>";
  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值