3. PHP的基础知识
3.1. 脚本的目录结构

3.2. PHP的配置
PHP 通过文本文件 [php.ini] 预先配置。所有这些配置均可通过编程进行修改。PHP 的配置对脚本的执行影响很大,因此了解它非常重要。 以下脚本 [phpinfo.php] 实现了这一功能:
注释
- 第 3 行:函数 [phpinfo] 显示 PHP 的配置;
执行结果
函数 [phpinfo] 在此显示了超过 800 行配置。我们不会对它们进行注释,因为其中大部分涉及 PHP 的高级用法。 其中第13行尤为重要:它指明了用于配置PHP的[php.ini]文件,而您将使用PHP来执行脚本。 若需修改 PHP 的运行配置,请修改此文件。该文件中包含大量注释,用于说明各项配置的作用。
3.3. 第一个示例
3.3.1. 代码
下面是一个名为 [bases-01.php] 的程序,它展示了 PHP 的基本特性。
<?php
// 这是一条注释
// 未声明即使用的变量
$nom = "dupont";
// 屏幕显示
print "nom=$nom\n";
// 包含不同类型元素的数组
$tableau = array("un", "deux", 3, 4);
// 其元素个数
$n = count($tableau);
// 一个循环
for ($i = 0; $i < $n; $i++) {
print "tableau[$i]=$tableau[$i]\n";
}
// 使用数组内容初始化两个变量
list($chaine1, $chaine2) = array("chaine1", "chaine2");
// 将两个字符串连接
$chaine3 = $chaine1 . $chaine2;
// 显示结果
print "[$chaine1,$chaine2,$chaine3]\n";
// 使用函数
affiche($chaine1);
// 变量的类型可以确定
afficheType("n", $n);
afficheType("chaine1", $chaine1);
afficheType("tableau", $tableau);
// 变量的类型在运行时可能会改变
$n = "a changé";
afficheType("n", $n);
// 函数可以返回结果
$res1 = f1(4);
print "res1=$res1\n";
// 函数可以返回一个值数组
list($res1, $res2, $res3) = f2();
print "(res1,res2,res3)=[$res1,$res2,$res3]\n";
// 本可以将这些值存入数组
$t = f2();
for ($i = 0; $i < count($t); $i++) {
print "t[$i]=$t[$i]\n";
}
// 测试
for ($i = 0; $i < count($t); $i++) {
// 仅显示字符串
if (getType($t[$i]) === "string") {
print "t[$i]=$t[$i]\n";
}
}
// 比较运算符 == 和 ===
if("2"==2){
print "avec l'opérateur ==, la chaîne 2 est égale à l'entier 2\n";
}else{
print "avec l'opérateur ==, la chaîne 2 n'est pas égale à l'entier 2\n";
}
if("2"===2){
print "avec l'opérateur ===, la chaîne 2 est égale à l'entier 2\n";
}
else{
print "avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2\n";
}
// 其他测试
for ($i = 0; $i < count($t); $i++) {
// 仅显示大于10的整数
if (getType($t[$i]) === "integer" and $t[$i] > 10) {
print "t[$i]=$t[$i]\n";
}
}
// 一个 while 循环
$t = [8, 5, 0, -2, 3, 4];
$i = 0;
$somme = 0;
while ($i < count($t) and $t[$i] > 0) {
print "t[$i]=$t[$i]\n";
$somme += $t[$i]; //$somme=$somme+$t[$i]
$i++; //$i=$i+1
}//while
print "somme=$somme\n";
// 程序结束
exit;
//----------------------------------
function affiche($chaine) {
// 显示 $chaine
print "chaine=$chaine\n";
}
//显示
//----------------------------------
function afficheType($name, $variable) {
// 显示 $variable 的类型
print "type[variable $" . $name . "]=" . getType($variable) . "\n";
}
//afficheType
//----------------------------------
function f1($param) {
// 将 10 加到 $param
return $param + 10;
}
//----------------------------------
function f2() {
// 返回 3 个值
return array("un", 0, 100);
}
?>
结果:
nom=dupont
tableau[0]=un
tableau[1]=deux
tableau[2]=3
tableau[3]=4
[chaine1,chaine2,chaine1chaine2]
chaine=chaine1
type[variable $n]=integer
type[variable $chaine1]=string
type[variable $tableau]=array
type[variable $n]=string
res1=14
(res1,res2,res3)=[un,0,100]
t[0]=un
t[1]=0
t[2]=100
t[0]=un
avec l'opérateur ==, la chaîne 2 est égale à l'entier 2
avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2
t[2]=100
t[0]=8
t[1]=5
somme=13
注释
- 第 5 行:在 PHP 中,变量类型未被声明。这些变量具有动态类型,其类型可能会随时间变化。$nom 表示标识符为 nom 的变量;
- 第7行:要向屏幕输出内容,可以使用指令 print 或 echo 指令;
- 第 9 行:关键字 array 用于定义数组。 变量 $nom[$i] 表示数组 $tableau 的元素 $i;
- 第 11 行:函数 count($tableau) 返回数组 $tableau 的元素个数;
- 第 13-15 行:一个循环。由于该循环仅包含一条语句,因此大括号是可选的。在本文档的后续内容中,无论语句数量多少,我们都将系统地使用大括号;
- 第 14 行:字符串用双引号 " 或单引号 ' 括起。在双引号内,变量 $variable 会被求值,但在单引号内则不会;
- 第 17 行:函数 list 可将变量汇集到列表中,并通过单次赋值操作为其赋值。此处 $chaine1="chaine1" 且 $chaine2="chaine2";
- 第19行:运算符 . 是字符串连接运算符;
- 第 83-86 行:关键字 function 定义了一个函数。函数是否返回值由语句 return 决定。调用代码可以忽略或获取函数的结果。函数可以在代码中的任意位置定义。
- 第 92 行:预定义函数 getType($variable) 返回一个字符串,该字符串表示 $variable 的类型。该类型可能会随时间变化;
- 第 45 行:运算符 === 进行严格比较:被比较的两个元素必须具有相同类型。运算符 == 则不那么严格:两个元素即使类型不同,也可能相等。第 50-60 行的语句就说明了这一点。 对于 == 运算符,比较是在将两个被比较的元素转换为同一类型之后进行的。此时会发生隐式转换。人们很容易“忽略”这些隐式转换的存在,从而导致意外结果,例如发现一个条件为真,而你原本预期它是假的。 为避免这一陷阱,我们将系统地使用 === 比较运算符;
- 第 64 行:也可以使用布尔运算符 or 和 !;
- 第 69 行:可以使用 [] 语法代替 array() 语法,从 PHP7 初始化数组;
- 第 80 行:预定义函数 exit 会终止脚本的执行;
- 第 107 行:标签 ?> 表示脚本 PHP 的结束。它并非必不可少。 此外,在网页环境中,如果该标签后跟空格或换行符,可能会引发问题,因为这些内容在文本编辑器中不可见,难以检测。因此,在本文档的后续内容中,我们将系统地省略该标签;
注:在本文档中,我们将使用关键字 [print] 在控制台显示文本。实现相同效果的另一种方法是使用关键字 [echo]。 这两个关键字之间存在细微差别,但在本文档的语境下并无区别。因此,如果您更倾向于使用 [echo],请随意使用。
3.3.2. NetBeans的使用
NetBeans会发出各种警告,建议您仔细检查。以脚本[bases-01.php]为例:

在第5行,NetBeans 发出警告,指出该文件未遵循 PSR-1 建议(PHP 标准建议第 1 条)。 PSR 是一套旨在生成标准代码的建议,从而促进由不同人员编写的代码之间的互操作性和可维护性。如果用户有意违反这些标准(例如项目团队有其他标准),这些警告可能会令人感到烦扰。 NetBeans 是否进行这些检查是可配置的:

- 在 [5] 中,列出了希望通过 NetBeans 进行检查的元素;
- 在 [6] 中,是 NetBeans 报告的错误所分配的严重性级别;

在 [7] 中,可以看到我们要求检查代码是否严格遵循 PSR-0 和 PSR-1 的建议。 这些检查并非强制要求。在学习编程语言的阶段,建议尽可能启用 NetBeans 提供的各项检查选项。这样可以学到很多东西。随后,我们将根据项目团队的编码规范调整 NetBeans 的检查设置。
下面我们来看一下 PSR-1 和 [8, 9] 编码规范:
选项 | 检查 |
| 类常量 MUST 应全部大写,并使用下划线分隔。 例如:const TAUX_TVA |
| 方法名称 MUST 应声明在 camelCase() 中。 示例:public function executeBatchImpots{} |
| 属性名称 SHOULD 应以 $StudlyCaps、$camelCase 或 $under_score 的格式(在同一作用域内保持一致)进行声明 例如:public SalaireAnnuel (StudlyCaps)、public salaireAnnuel (camelCase)、public salaire_annuel (under_score) |
| 文件 SHOULD 声明新符号且不产生其他副作用,或者它 SHOULD 执行具有副作用的逻辑,但 SHOULD NOT 两者兼有。 |
| 类型名称 MUST 应声明在 StudlyCaps 中 (为 5.2.x 及更早版本 SHOULD 编写的代码使用 Vendor_ 前缀作为类型名的伪命名空间约定)。每个类型都位于单独的文件中,并处于至少一个级别的命名空间中:一个顶级供应商名称。 例如:class EtudiantBoursier {} |
PSR-1 / 4 建议指出,在 PHP 文件中应包含:
- 要么是类型声明(类、接口);
- 或不含新类型声明的可执行代码;
此外还有其他不受 NetBeans 控制的 PHP 建议:PSR-3、PSR-4、 PSR-6、PSR-7 和 PSR-13。
为简化起见,本文中的示例并未全部验证建议 PSR-1,因为这要求将类和接口的代码分发到单独的文件中,对于基础示例而言过于繁琐。因此,将所有内容放在一个文件中更为简便。 对于本文作为主线介绍的应用程序示例,我们力求尽可能遵循建议 PSR-1。
NetBeans 中的某些警告提示了潜在的错误:

警告 [Unitialized Variables] 提示可能存在错误,通常是变量名称的输入错误。警告 [Unused Variables] 也是如此。
最后,建议检查所有由 NetBeans 报告的警告,这些警告通常以代码左侧边栏中的面板和右侧边栏中的黄色破折号形式显示:


3.4. 变量的作用域
3.4.1. 示例 1
脚本 [bases-02.php] 如下:
<?php
// 变量的作用域
function f1() {
// 使用全局变量 $i
global $i;
$i++;
$j = 10;
print "f1[i,j]=[$i,$j]\n";
}
function f2() {
// 使用全局变量 $i
global $i;
$i++;
$j = 20;
print "f2[i,j]=[$i,$j]\n";
}
function f3() {
// 使用局部变量 $i
$i = 4;
$j = 30;
print "f3[i,j]=[$i,$j]\n";
}
// 测试
$i = 0;
$j = 0; // 函数 f 无法识别这两个变量
// 除非该函数通过 global 语句显式声明
// 声明要使用它们
f1();
f2();
f3();
print "test[i,j]=[$i,$j]\n";
结果:
注释
- 第 29-30 行:定义了主程序中的两个变量 $i 和 $j。这些变量在函数内部不可见。 因此,第9行中,函数f1的变量$j是函数f1的局部变量,与主程序中的变量$j不同。 函数可以通过关键字 global 访问主程序中的变量 $variable;
- 第7行,该语句指向主程序的全局变量$i;
3.4.2. 示例 3
脚本 [bases-03.php] 如下:
<?php
// 变量的作用域在代码块中是全局的
$i = 0; {
$i = 4;
$i++;
}
print "i=$i\n";
结果:
注释
在某些语言中,大括号内定义的变量作用域仅限于大括号内部:在大括号外部无法访问该变量。 上述结果表明,在PHP中并非如此。第5行在大括号内定义的变量$i与第4行和第8行在大括号外使用的变量是同一个。
3.5. 类型变化
PHP 中的变量没有固定类型。该类型可能会在运行过程中根据变量的赋值而改变。在涉及多种类型数据的运算中,PHP 解释器会进行隐式转换,将操作数转换为一种通用类型。 如果开发人员不了解这些隐式转换,它们可能会成为难以发现的错误源。下面是一个展示隐式和显式转换的 [bases-04.php] 脚本:
<?php
// 参数传递中采用严格类型
declare(strict_types=1);
// 隐式类型转换
// 类型 --> bool
print "Conversion vers un booléen------------------------------\n";
showBool("abcd", "abcd");
showBool("", "");
showBool("[1, 2, 3]", [1, 2, 3]);
showBool("[]", []);
showBool("NULL", NULL);
showBool("0.0", 0.0);
showBool("0", 0);
showBool("4.6", 4.6);
function showBool(string $prefixe, $var) : void {
print "(bool) $prefixe : ";
// 在随后的测试中,$var 会自动转换为布尔值
if ($var) {
print "true";
} else {
print "false";
}
print "\n";
}
注释
- 第 4 行:要求在指定函数参数时进行严格的类型检查;
- 第 18 行:函数 [showBool] 的目的是展示当需要将任意类型的值转换为布尔值(第 21 行)时,解释器 PHP 执行的隐式(自动)转换;
- 第 18 行:参数 $var 未指定类型。因此,实际参数可以是任何类型。而参数 $prefixe 必须为字符串类型。函数 showBool 不返回任何值(void);
- 第21行:在if($var)语句中,$var的值必须转换为布尔值,才能对if进行求值。 令人惊讶的是,解释器 PHP 对于给定的任何类型的值都能给出响应;
- 第9-16行:函数[showBool]的参数值将依次为:
- 第 9 行:非空字符串:结果为 TRUE(区分大小写,TRUE=true);
- 第 10 行:一个空字符串:结果为 FALSE;
- 第 11 行:一个非空数组:结果为 TRUE;
- 第 12 行:一个空数组:结果为 FALSE;
- 第 14 行:实数 0:结果 FALSE;
- 第 15 行:整数 0:结果 FALSE;
- 第 16 行:不为 0 的实数(或整数):结果 TRUE;
以下是获得的屏幕显示结果:
Conversion vers un booléen------------------------------
(bool) abcd : true
(bool) : false
(bool) [1, 2, 3] : true
(bool) [] : false
(bool) NULL : false
(bool) 0.0 : false
(bool) 0 : false
(bool) 4.6 : true
继续编写脚本代码:
//
// 字符串类型隐式转换为数值类型
// 字符串 --> 数字
print "Conversion chaîne vers nombre------------------------------\n";
showNumber("12");
showNumber("45.67");
showNumber("abcd");
function showNumber(string $var) : void {
$nombre = $var + 1;
var_dump($nombre);
print "($var): $nombre\n";
}
注释
- 第 9 行:函数 [showNumber] 接受字符串类型的参数,且不返回结果(void);
- 第10行:该参数用于算术运算,这将迫使解释器PHP尝试将$var转换为数字;
- 第 5 行:将字符串“12”转换为整数 12;
- 第 6 行:将字符串“45.67”转换为实数 45.67;
- 第 7 行:将发出警告,但仍会将字符串“abcd”转换为数字 0;
以下是执行结果:
继续编写脚本代码:
// 显式类型转换
// 转换为 int
showInt("12.45");
showInt(67.8);
showInt(TRUE);
showInt(NULL);
function showInt($var) : void {
print "paramètre : ";
var_dump($var);
print "\n";
print "résultat de la conversion : ";
var_dump((int) $var);
print "\n";
}
注释
- 第 21 行:函数 [showInt] 接受任意类型的参数,且不返回结果。它尝试将参数 $var 转换为整数(第 26 行)。 通常,要将变量 $var 转换为类型 T,应写为 (T) $var,其中 T 可以是:int、integer、bool、boolean、float、double、real、string、array、object、unset;
- 第 16 行:将字符串“12.45”转换为整数 12;
- 第 17 行:将实数 67.8 转换为整数 67;
- 第 18 行:将布尔值 TRUE 转换为整数 1(布尔值 FALSE 转换为整数 0);
- 第 19 行:将指针 NULL 转换为整数 0;
屏幕显示如下:
我们将继续研究该脚本,重点是显式将值转换为float类型:
// 转换为 float
showFloat("12.45");
showFloat(67);
showFloat(TRUE);
showFloat(NULL);
function showFloat($var) : void {
print "paramètre : ";
var_dump($var);
print "\n";
print "résultat de la conversion : ";
var_dump((float) $var);
print "\n";
}
注释
- 第 35 行:函数 [showFloat] 接收任意类型的参数,且不返回结果;
- 第 40 行:该参数的值被显式转换为 float;
- 第 30 行:字符串“12.45”被转换为实数 12.45;
- 第 31 行:整数 67 被转换为实数 67;
- 第 32 行:布尔值 TRUE 被转换为实数 1(值 FALSE 被转换为 0);
- 第 33 行:指针 NULL 被转换为实数 0;
屏幕结果如下所示:
我们将继续介绍该脚本,研究向字符串类型的转换:
// 转为字符串
showstring(5);
showString(6.7);
showString(FALSE);
showString(NULL);
function showString($var) : void {
print "paramètre : ";
var_dump($var);
print "\n";
print "résultat de la conversion : ";
var_dump((string) $var);
print "\n";
}
- 第 49 行:函数 [showString] 接收任意类型的参数,且不返回结果;
- 第54行:参数值被转换为字符串类型;
- 第 44 行:整数 5 将被转换为字符串“5”;
- 第 45 行:实数 6.7 将被转换为字符串“6.7”;
- 第 46 行:布尔值 FALSE 将被转换为空字符串;
- 第 47 行:指针 NULL 将被转换为空字符串;
以下是屏幕显示结果:
3.6. 数组
3.6.1. 经典的一维数组
脚本 [bases-05.php] 如下:
<?php
// 经典数组
// 初始化
$tab1 = array(0, 1, 2, 3, 4, 5);
// 遍历 - 1
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
print "tab1[$i]=$tab1[$i]\n";
}
// 遍历 - 2
print "tab1 a " . count($tab1) . " éléments\n";
reset($tab1);
while (list($clé, $valeur) = each($tab1)) {
print "tab1[$clé]=$valeur\n";
}
// 添加元素
$tab1[] = $i++;
$tab1[] = $i++;
// 遍历 - 3
print "tab1 a " . count($tab1) . " éléments\n";
$i = 0;
foreach ($tab1 as $élément) {
print "tab1[$i]=$élément\n";
$i++;
}
// 删除最后一个元素
array_pop($tab1);
// 遍历 - 4
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
print "tab1[$i]=$tab1[$i]\n";
}
// 删除第一个元素
array_shift($tab1);
// 遍历 - 5
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
print "tab1[$i]=$tab1[$i]\n";
}
// 在数组末尾插入
array_push($tab1, -2);
// 遍历 - 6
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
print "tab1[$i]=$tab1[$i]\n";
}
// 在数组开头插入
array_unshift($tab1, -1);
// 遍历 - 7
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
print "tab1[$i]=$tab1[$i]\n";
}
结果:
tab1 a 6 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1 a 6 éléments
Deprecated: The each() function is deprecated. This message will be suppressed on further calls in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_04.php on line 14
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1 a 8 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1[7]=7
tab1 a 7 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1 a 6 éléments
tab1[0]=1
tab1[1]=2
tab1[2]=3
tab1[3]=4
tab1[4]=5
tab1[5]=6
tab1 a 7 éléments
tab1[0]=1
tab1[1]=2
tab1[2]=3
tab1[3]=4
tab1[4]=5
tab1[5]=6
tab1[6]=-2
tab1 a 8 éléments
tab1[0]=-1
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1[7]=-2
注释
上述程序展示了对数组值的操作。在 PHP 中,数组有两种表示法:
表 1 称为数组,表 2 称为字典或关联数组,其中元素以键 => 值的形式表示。 $contraires["beau"] 表示与键“beautiful”关联的值。因此,此处的值是字符串“ugly”。数组 1 只是字典的一种变体,也可以表示为:
因此,$tableau[2]="trois"。 归根结底,所有数据结构本质上都是字典。对于包含 n 个元素的经典数组,其键是 [0,n-1] 区间内的整数。
- 第 14 行:each($tableau) 函数用于遍历字典。每次调用时,它都会返回该字典中的一对 (键, 值)。如结果第 10 行所示,each 函数在 PHP 7 中已过时;
- 第 13 行:reset($dictionnaire) 函数将 each 函数定位到字典中的第一个 (键,值) 对。
- 第 14 行:当函数 each 在字典末尾返回一个空对时,循环 while 终止。 此处发生的是隐式转换:空元组被转换为布尔值 FALSE;
- 第 18 行:$tableau[]=value 语法将元素 valeur 作为 $tableau 的最后一个元素添加;
- 第 23 行:使用 foreach 遍历数组。该语法元素支持通过两种语法遍历字典(即数组):
第一种语法在每次迭代时都会返回一个元组 (clé,valeur),而第二种语法仅返回字典中的 valeur 这一项。
- 第 28 行:函数 array_pop($tableau) 删除了 $tableau 的最后一个元素;
- 第 35 行:函数 array_shift($tableau) 删除 $tableau 的第一个元素;
- 第 42 行:函数 array_push($tableau,value) 将 valeur 作为最后一个元素添加到 $tableau 中;
- 第 49 行:函数 array_unshift($tableau,value) 将 valeur 作为 $tableau 的第一个元素添加进去;
3.6.2. 字典或关联数组
脚本 [bases-06.php] 如下:
<?php
// 词典
$conjoints = ["Pierre" => "Gisèle", "Paul" => "Virginie", "Jacques" => "Lucette", "Jean" => ""];
// 遍历 - 1
print "Nombre d'éléments du dictionnaire : " . count($conjoints) . "\n";
reset($conjoints);
while (list($clé, $valeur) = each($conjoints)) {
print "conjoints[$clé]=$valeur\n";
}
// 按键值对排序字典
ksort($conjoints);
// 遍历 - 2
reset($conjoints);
while (list($clé, $valeur) = each($conjoints)) {
print "conjoints[$clé]=$valeur\n";
}
// 字典键列表
$clés = array_keys($conjoints);
for ($i = 0; $i < count($clés); $i++) {
print "clés[$i]=$clés[$i]\n";
}
// 字典值列表
$valeurs = array_values($conjoints);
for ($i = 0; $i < count($valeurs); $i++) {
print "valeurs[$i]=$valeurs[$i]\n";
}
// 查找键
existe($conjoints, "Jacques");
existe($conjoints, "Lucette");
existe($conjoints, "Jean");
// 删除键值对
unset($conjoints["Jean"]);
print "Nombre d'éléments du dictionnaire : " . count($conjoints) . "\n";
foreach ($conjoints as $clé => $valeur) {
print "conjoints[$clé]=$valeur\n";
}
// 结束
exit;
function existe($conjoints, $mari) {
// 检查键 $mari 是否存在于字典 $conjoints 中
if (isset($conjoints[$mari])) {
print "La clé [$mari] existe associée à la valeur [$conjoints[$mari]]\n";
} else {
print "La clé [$mari] n'existe pas\n";
}
}
结果:
Nombre d'éléments du dictionnaire : 4
Deprecated: The each() function is deprecated. This message will be suppressed on further calls in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_05.php on line 8
conjoints[Pierre]=Gisèle
conjoints[Paul]=Virginie
conjoints[Jacques]=Lucette
conjoints[Jean]=
conjoints[Jacques]=Lucette
conjoints[Jean]=
conjoints[Paul]=Virginie
conjoints[Pierre]=Gisèle
clés[0]=Jacques
clés[1]=Jean
clés[2]=Paul
clés[3]=Pierre
valeurs[0]=Lucette
valeurs[1]=
valeurs[2]=Virginie
valeurs[3]=Gisèle
La clé [Jacques] existe associée à la valeur [Lucette]
La clé [Lucette] n'existe pas
La clé [Jean] existe associée à la valeur []
Nombre d'éléments du dictionnaire : 3
conjoints[Jacques]=Lucette
conjoints[Paul]=Virginie
conjoints[Pierre]=Gisèle
注释
上述代码将之前针对简单数组的内容应用到了字典上。我们仅对新增内容进行说明:
- 第 12 行:ksort(键排序)函数可按键的自然顺序对字典进行排序;
- 第19行:函数 array_keys($dictionnaire) 将字典的键列表转换为数组形式;
- 第24行:函数 array_values($dictionnaire) 将字典的值列表以数组形式返回;
- 第 43 行:函数 isset($variable) 返回 TRUE(如果变量 $variable 已定义),否则返回 FALSE;
- 第33行:函数 unset($variable) 删除变量 $variable。
3.6.3. 多维数组
脚本 [bases-07.php] 如下:
<?php
// 经典多维数组
// 初始化
$multi = array(array(0, 1, 2), array(10, 11, 12, 13), array(20, 21, 22, 23, 24));
// 遍历
for ($i1 = 0; $i1 < count($multi); $i1++) {
for ($i2 = 0; $i2 < count($multi[$i1]); $i2++) {
print "multi[$i1][$i2]=" . $multi[$i1][$i2] . "\n";
}
}
// 多维字典
// 初始化
$multi = array("zéro" => array(0, 1, 2), "un" => array(10, 11, 12, 13), "deux" => array(20, 21, 22, 23, 24));
// 遍历
foreach ($multi as $clé => $valeur) {
for ($i2 = 0; $i2 < count($valeur); $i2++) {
print "multi[$clé][$i2]=" . $multi[$clé][$i2] . "\n";
}
}
结果:
multi[0][0]=0
multi[0][1]=1
multi[0][2]=2
multi[1][0]=10
multi[1][1]=11
multi[1][2]=12
multi[1][3]=13
multi[2][0]=20
multi[2][1]=21
multi[2][2]=22
multi[2][3]=23
multi[2][4]=24
multi[zéro][0]=0
multi[zéro][1]=1
multi[zéro][2]=2
multi[un][0]=10
multi[un][1]=11
multi[un][2]=12
multi[un][3]=13
multi[deux][0]=20
multi[deux][1]=21
multi[deux][2]=22
multi[deux][3]=23
multi[deux][4]=24
注释
- 第 5 行:数组 $multi 的元素本身也是数组;
- 第14行:数组$multi被转换为一个字典(clé,valeur),其中每个valeur都是一个数组;
3.7. 字符串
3.7.1. 表示法
脚本 [bases-08.php] 如下:
<?php
// 字符串表示法
$chaine1 = "un";
$chaine2 = 'un';
print "[$chaine1,$chaine2]\n";
?>
结果:
3.7.2. 比较
脚本 [bases-09.php] 如下:
<?php
// 严格遵守函数参数类型
declare(strict_types=1);
// 比较函数
function compareModele2Chaine(string $chaine1, string $chaine2): void {
// 比较字符串1和字符串2
if ($chaine1 === $chaine2) {
print "[$chaine1] est égal à [$chaine2]\n";
} else {
print "[$chaine1] est différent de [$chaine2]\n";
}
}
// 字符串比较测试
compareModele2Chaine("abcd", "abcd");
compareModele2Chaine("", "");
compareModele2Chaine("1", "");
exit;
结果:
[abcd] est égal à [abcd]
[] est égal à []
[1] est différent de []
注释
- 代码第9行:本可以使用 == 运算符代替 ===。后者限制更严格,因为它要求两个操作数必须是同一类型。需要注意的是,在此处可以使用 == 运算符代替,因为函数签名中已将两个参数的类型限定为 string;
3.7.3. 字符串与数组之间的关联
脚本 [bases-10.php] 如下:
<?php
// 字符串转数组
$chaine = "1:2:3:4";
$tab = explode(":", $chaine);
// 遍历数组
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
print "tab[$i]=$tab[$i]\n";
}
// 数组转字符串
$chaine2 = implode(":", $tab);
print "chaine2=$chaine2\n";
// 添加一个空字段
$chaine .= ":";
print "chaîne=$chaine\n";
$tab = explode(":", $chaine);
// 遍历数组
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
print "tab[$i]=$tab[$i]\n";
} // 现在有 5 个元素,最后一个为空
// 再次添加一个空字段
$chaine .= ":";
print "chaîne=$chaine\n";
$tab = explode(":", $chaine);
// 遍历数组
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
print "tab[$i]=$tab[$i]\n";
} // 现在共有 6 个元素,最后两个为空
结果:
tab a 4 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
chaine2=1:2:3:4
chaîne=1:2:3:4:
tab a 5 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
tab[4]=
chaîne=1:2:3:4::
tab a 6 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
tab[4]=
tab[5]=
注释
- 第 5 行:函数 explode($séparateur,$chaine) 可提取由 $séparateur 分隔的 $chaine 字段。 因此,explode(":",$chaine) 可将 $chaine 中以字符串 ":" 分隔的元素以数组形式提取出来;
- 第 12 行:函数 implode($séparateur,$tableau) 执行与函数 explode 相反的操作。 它返回一个字符串,该字符串由 $tableau 的元素组成,这些元素由 $séparateur 分隔;
3.7.4. 正则表达式
脚本 [bases-11.php] 如下:
<?php
// 函数参数的严格类型
declare (strict_types=1);
// PHP 中的正则表达式
// 提取字符串中的各个字段
// 模式:由任意字符包围的一串数字
// 仅提取数字序列
$modèle = "/(\d+)/";
// 将字符串与模式进行比对
compareModele2Chaine($modèle, "xyz1234abcd");
compareModele2Chaine($modèle, "12 34");
compareModele2Chaine($modèle, "abcd");
// 模式:由任意字符包围的一串数字
// 需要提取数字序列及其前后相邻的字段
$modèle = "/^(.*?)(\d+)(.*?)$/";
// 将字符串与模式进行匹配
compareModele2Chaine($modèle, "xyz1234abcd");
compareModele2Chaine($modèle, "12 34");
compareModele2Chaine($modèle, "abcd");
// 模式:日期格式为 dd/mm/yy
$modèle = "/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/";
compareModele2Chaine($modèle, "10/05/97");
compareModele2Chaine($modèle, " 04/04/01 ");
compareModele2Chaine($modèle, "5/1/01");
// 模式——一个十进制数
$modèle = "/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/";
compareModele2Chaine($modèle, "187.8");
compareModele2Chaine($modèle, "-0.6");
compareModele2Chaine($modèle, "4");
compareModele2Chaine($modèle, ".6");
compareModele2Chaine($modèle, "4.");
compareModele2Chaine($modèle, " + 4");
// 结束
exit;
// --------------------------------------------------------------------------
function compareModele2Chaine(string $modèle, string $chaîne): void {
// 将字符串 $chaîne 与模板 $modèle 进行比较
// 将字符串与模板进行比对
$champs = [];
$correspond = preg_match($modèle, $chaîne, $champs);
// 显示结果
print "\nRésultats($modèle,$chaîne)\n";
if ($correspond) {
for ($i = 0; $i < count($champs); $i++) {
print "champs[$i]=$champs[$i]\n";
}
} else {
print "La chaîne [$chaîne] ne correspond pas au modèle [$modèle]\n";
}
}
结果:
Résultats(/(\d+)/,xyz1234abcd)
champs[0]=1234
champs[1]=1234
Résultats(/(\d+)/,12 34)
champs[0]=12
champs[1]=12
Résultats(/(\d+)/,abcd)
La chaîne [abcd] ne correspond pas au modèle [/(\d+)/]
Résultats(/^(.*?)(\d+)(.*?)$/,xyz1234abcd)
champs[0]=xyz1234abcd
champs[1]=xyz
champs[2]=1234
champs[3]=abcd
Résultats(/^(.*?)(\d+)(.*?)$/,12 34)
champs[0]=12 34
champs[1]=
champs[2]=12
champs[3]= 34
Résultats(/^(.*?)(\d+)(.*?)$/,abcd)
La chaîne [abcd] ne correspond pas au modèle [/^(.*?)(\d+)(.*?)$/]
Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,10/05/97)
champs[0]=10/05/97
champs[1]=10
champs[2]=05
champs[3]=97
Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/, 04/04/01 )
champs[0]= 04/04/01
champs[1]=04
champs[2]=04
champs[3]=01
Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,5/1/01)
La chaîne [5/1/01] ne correspond pas au modèle [/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/]
Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,187.8)
champs[0]=187.8
champs[1]=
champs[2]=187.8
Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,-0.6)
champs[0]=-0.6
champs[1]=-
champs[2]=0.6
Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,4)
champs[0]=4
champs[1]=
champs[2]=4
Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,.6)
champs[0]=.6
champs[1]=
champs[2]=.6
Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,4.)
champs[0]=4.
champs[1]=
champs[2]=4.
Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/, + 4)
champs[0]= + 4
champs[1]=+
champs[2]=4
注释
- 此处我们使用正则表达式来提取字符串中的各个字段。正则表达式有助于突破函数 implode 的限制。 其原理是利用函数 preg_match,将一个字符串与另一个名为 modèle 的字符串进行比较:
$correspond = preg_match($modèle, $chaîne, $champs);
如果能在 chaîne 中找到 modèle,则函数 preg_match 会返回布尔值 TRUE。 如果可以,则 $champs[0] 表示与该模板对应的子字符串。 此外,如果 modèle 包含括号中的子模型,则 $champs[1] 是 $chaîne 中对应第一个子模型的部分, $champs[2] 是 $chaîne 中对应第二个子模式的部分,以此类推……
让我们来看第一个例子。该模式在第 10 行定义:它表示字符串中任意位置出现的一个或多个(+)数字(\d)。此外,该模式还定义了一个用圆括号包围的子模式;
- 第12行:模式 /(\d+)/(字符串中任意位置的一个或多个数字序列)与字符串“xyz1234abcd”进行匹配。可以看到子字符串1234与该模式匹配。 因此,$champs[0] 等于 "1234"。 此外,该正则表达式包含括号内的子模式。因此 $champs[1] 结果为 "1234";
- 第13行:将正则表达式 /(\d+)/ 与字符串 "12 34" 进行匹配。可见子字符串 12 和 34 均符合该正则表达式。匹配将在第一个符合正则表达式的子字符串处停止。 因此,我们将得到 $champs[0]=12 和 $champs[1]=12;
- 第 14 行:将正则表达式 /(\d+)/ 与字符串 "abcd" 进行比较。未找到匹配项;
下面详细说明代码后续部分中使用的正则表达式:
$modèle = "/^(.*?)(\d+)(.*?)$/";
表示字符串开头(^),随后是0个或多个(*)任意字符(.),接着是1个或多个(+)数字,最后又是0个或多个(*)任意字符(.)。 模式 (.*) 表示 0 个或多个任意字符。此类模式将匹配任何字符串。因此,模式 /^(.*)(\d+)(.*)$/ 永远无法被匹配,因为第一个子模式 (.*) 会吞噬整个字符串。 而正则表达式 (.*?)(\d+) 表示 0 个或多个任意字符,直到下一个子模式 (?)(即 \d+)。因此,数字现在不再被 (.* ) 模式捕获。上述正则表达式将匹配 [début de chaîne (^), une suite de caractères quelconques (.*?), une suite d'un ou plusieurs chiffres (\d+), une suite de caractères quelconques (.*?), la fin de la chaîne ($)]。
$modèle = "/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/";
则匹配 [début de chaîne (^), 2 chiffres (\d\d), le caractère / (\/), 2 chiffres, /, 2 chiffres, une suite de 0 ou plusieurs espaces (\s*), la fin de chaîne ($)]。
$modèle = "/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/";
对应字符串开头 (^),0 个或多个空格 (\s*),一个 + 或 - 符号 [+|-] 出现 0 次或 1 次 (?),0 个或多个空格 (\s*), 1 个或多个数字后跟小数点,再后跟零个或多个数字 (\d+\.\d*) 或 (|) 小数点 (\.) 后跟一个或多个数字 (\d+) 或 (|) 一个或多个数字 (\d+),以及零个或多个空格 (\s*)]。
注:正则表达式中的术语 [espace] 指代一组字符:空格、换行符 \n、制表符 \t、回车符 \r、分页符 \f…
3.8. 函数
3.8.1. 参数传递方式
脚本 [base-12.php] 如下:
<?php
// 函数参数传递方式
// 严格遵守参数类型
declare(strict_types=1);
function f(int &$i, int $j): void {
// $i 将通过引用传递
// $j 将按值传递
$i++;
$j++;
print "f[i,j]=[$i,$j]\n";
}
// 测试
$i = 0;
$j = 0;
//、$i 和 $j 已切换至 f 功能
f($i, $j);
print "test[i,j]=[$i,$j]\n";
结果:
注释
上述代码展示了向函数传递参数的两种方式。让我们来看以下示例:
- 第 1 行:定义函数 f 的形式参数 $a 和 $b。该函数处理这两个形式参数并返回结果;
- 第 7 行:调用函数 f,并传入两个实际参数 $i 和 $j。 形式参数($a、$b)与实际参数($i、$j)之间的映射关系由第1行和第7行定义:
- &$a:符号 & 表示形式参数 $a 的值将取自实际参数 $i 的地址。 换言之,$a 和 $i 是指向同一内存位置的两个引用。 操作形式参数 $a 相当于操作实际参数 $i。代码的执行结果也证明了这一点。这种传递方式适用于输出参数以及数组和字典等大容量数据。这种传递方式被称为引用传递。
- $b:形式参数 $b 的值将取自实际参数 $j。这属于值传递。 形式参数和实际参数是两个不同的变量。操作形式参数 $b 不会对实际参数 $j 产生任何影响。代码的执行结果也证明了这一点。这种传递方式适用于输入参数。
- 假设函数 échange 接受两个形式参数 $a 和 $b。 该函数会交换这两个参数的值。因此,在交换调用($i,$j)时,调用方代码期望两个实际参数的值被交换。因此,这些是输出参数(它们会被修改)。因此,应写为:
以下脚本 [base-13.php] 展示了其他示例:
<?php
// 严格模式类型
// declare(strict_types = 1);
// 参数传递模式
function f(&$i, $j) {
// $i 将通过引用传递
// $j 将按值传递
$i++;
$j++;
print "f[i,j]=[$i,$j]\n";
}
function g(int &$i, int $j) : void {
// $i 将通过引用传递
// $j 将按值传递
$i++;
$j++;
print "g[i,j]=[$i,$j]\n";
}
// 测试
$i = 0;
$j = 0;
// $i 和 $j 被传递给函数 f
f($i, $j);
print "test[i,j]=[$i,$j]\n";
// $i 和 $j 被传递给函数 g
g($i, $j);
print "test[i,j]=[$i,$j]\n";
// 向 f 传递了不正确的参数
$a=5.3;
$b=6.2;
f($a, $b);
print "test[a,b]=[$a,$b]\n";
// 向 f 传递了不正确的参数
$a=5.3;
$b=6.2;
g($a, $b);
print "test[a,b]=[$a,$b]\n";
注释
- 第 8-14 行:前一段中讨论的函数 f,但未指定参数类型;
- 第16-22行:函数g与函数f功能相同,但明确了预期参数的类型——这是新特性PHP 7。该函数预期接收两个int类型的参数。我们想观察当实际传递给函数的参数类型与函数预期不一致时会发生什么;
- 第 25-26 行:$i 和 $j 是两个整数;
- 第 28-29 行:使用预期类型的参数调用函数 f;
- 第 31-32 行:使用预期类型的参数调用函数 g;
- 第 34-35 行:变量 $a 和 $b 属于 float 类型;
- 第 36-37 行:使用非预期类型的参数调用函数 f;
- 第 41-42 行:使用非预期类型的参数调用函数 g;
结果
- 第5-6行表明函数f接受了两个float类型的参数并进行了处理;
- 第7-8行显示,函数g接受了两个float类型的参数,但将其转换为int类型(第7行);
现在取消第4行的注释:
该指令表明必须遵守形式参数的类型。若不遵守,则会报错。此时的执行结果变为:
- 第 9-10 行:解释器 PHP 7 抛出了异常,表明传递给函数 g 的第一个参数类型不正确。建议在可能的情况下严格检查参数类型,以便检测函数调用错误;
3.8.2. 函数返回的结果
脚本 [base-15.php] 如下:
<?php
// 严格模式下的类型
declare(strict_types=1);
// 函数返回的结果
// 函数可将多个值返回为数组
list($res1, $res2, $res3) = f1(10);
print "[$res1,$res2,$res3]\n";
$res = f1(10);
for ($i = 0; $i < count($res); $i++) {
print "f1 : res[$i]=$res[$i]\n";
}
// 函数可以返回一个对象
$res = f2(10);
print "f2 : [$res->res1,$res->res2,$res->res3]\n";
// 何种类型的对象?
print "nature de l'objet : ";
var_dump($res);
print "\n";
// 对函数 f3 进行同样的操作
$res = f3(10);
print "f3 : [$res->res1,$res->res2,$res->res3]\n";
// 是什么类型的对象?
print "nature de l'objet : ";
var_dump($res);
print "\n";
// 结束
exit;
// 函数 f1
function f1(int $valeur): array {
// 返回一个数组 ($valeur+1,$valeur+2,$valeur+3)
return array($valeur + 1, $valeur + 2, $valeur + 3);
}
// 函数 f2
function f2(int $valeur): object {
// 返回一个对象 ($valeur+1,$valeur+2,$valeur+3)
$res->res1 = $valeur + 1;
$res->res2 = $valeur + 2;
$res->res3 = $valeur + 3;
// 返回对象
return $res;
}
// 函数 f3 - 与函数 f2 功能相同
function f3(int $valeur): object {
// 返回一个对象 ($valeur+1,$valeur+2,$valeur+3)
$res = new stdclass();
$res->res1 = $valeur + 1;
$res->res2 = $valeur + 2;
$res->res3 = $valeur + 3;
// 返回该对象
return $res;
}
结果
[11,12,13]
f1 : res[0]=11
f1 : res[1]=12
f1 : res[2]=13
Warning: Creating default object from empty value in C:\Data\st-2019\dev\php7\php5-exemples\exemples\bases\base-15.php on line 43
f2 : [11,12,13]
nature de l'objet : object(stdClass)#1 (3) {
["res1"]=>
int(11)
["res2"]=>
int(12)
["res3"]=>
int(13)
}
f3 : [11,12,13]
nature de l'objet : object(stdClass)#2 (3) {
["res1"]=>
int(11)
["res2"]=>
int(12)
["res3"]=>
int(13)
}
注释
- 前面的程序表明,函数 PHP 可以返回一组结果(而非单一结果),形式为数组或对象。对象的概念将在稍后部分详细说明;
- 第35-38行:函数f1以数组(array)的形式返回多个值;
- 第41-48行:函数f2以对象(object)的形式返回多个值;
- 第 51-59 行:函数 f3 与函数 f2 完全相同,只是它在第 53 行显式创建了一个对象;
- 结果中的第6行发出警告,指出PHP在代码第43行被迫创建了一个默认对象,即在使用[$res→res1]这种写法时。 代码第20行的函数var_dump可访问该对象的类型及其内容。在结果中,我们可以看到:
- 第 8 行:默认创建的对象类型为 stdClass;
- 第 9-10 行:属性 res1 的类型为 entier,且值为 11;
- 等等……
- 为避免结果中第6行的警告,我们在函数f3的第53行显式创建了一个类型为stdClass的对象;
3.9. 文本文件
脚本 [bases-16.php] 如下:
<?php
// 严格遵守函数参数类型
declare (strict_types=1);
// 文本文件的顺序处理
// 该文件由多行组成,每行格式为 login:pwd:uid:gid:infos:dir:shell
// 每行数据以 login => uid:gid:infos:dir:shell 的形式存入字典
// 确定文件名
$INFOS = "infos.txt";
// 以新建方式打开
if (!$fic = fopen($INFOS, "w")) {
print "Erreur d'ouverture du fichier $INFOS en écriture\n";
exit;
}
// 生成任意内容
for ($i = 0; $i < 100; $i++) {
fputs($fic, "login$i:pwd$i:uid$i:gid$i:infos$i:dir$i:shell$i\n");
}
// 关闭文件
fclose($fic);
// 利用漏洞 - fgets 保留换行符
// 这可避免在读取空行时获取空字符串
// 以读取模式打开文件
if (!$fic = fopen($INFOS, "r")) {
print "Erreur d'ouverture du fichier $INFOS en lecture\n";
exit;
}
// 每行少于 1000 个字符
// 行读取在换行符处停止
// 或文件结束标记处
while ($ligne = fgets($fic, 1000)) {
// 若存在行尾标记,则将其删除
$ligne = cutNewLineChar($ligne);
// 将该行放入数组中
$infos = explode(":", $ligne);
// 获取登录名
$login = array_shift($infos);
// 忽略密码
array_shift($infos);
// 在字典中创建一条条目
$dico[$login] = $infos;
}
// 关闭该表
fclose($fic);
// 利用字典
afficheInfos($dico, "login10");
afficheInfos($dico, "X");
// 结束
exit;
// --------------------------------------------------------------------------
function afficheInfos(array $dico, string $clé): void {
// 若存在,则显示字典中与键关联的值 $dico
if (isset($dico[$clé])) {
// 值存在——是数组吗?
$valeur = $dico[$clé];
if (is_array($valeur)) {
print "[$clé," . join(":", $valeur) . "]\n";
} else {
// $valeur 不是数组
print "[$clé,$valeur]\n";
}
} else {
// $clé 不是字典 $dico 中的键
print "la clé [$clé] n'existe pas\n";
}
}
// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne): string {
// 如果存在,则删除 $ligne 的行尾标记
$L = strlen($ligne); // 行长度
while (substr($ligne, $L - 1, 1) == "\n" or substr($ligne, $L - 1, 1) == "\r") {
$ligne = substr($ligne, 0, $L - 1);
$L--;
}
// 结束
return($ligne);
}
文件 infos.txt:
结果:
[login10,uid10:gid10:infos10:dir10:shell10]
la clé [X] n'existe pas
注释
- 第 12 行:fopen(nom_fichier,"w") 以写入模式打开文件 nom_fichier(w=write)。 如果文件不存在,则创建该文件。如果文件已存在,则清空其内容。如果创建失败,fopen将返回值false。 在语句 if (!$fic = fopen($INFOS, "w")) {…} 中,包含两项连续操作: 1) $fic=fopen(..) 2) if( ! $fic) {…} ;
- 第18行:fputs($fic,$chaîne) 将 chaîne 写入文件 $fic。 $chaine 后面带有换行符 \n;
- 第 21 行:fclose($fic) 关闭文件 $fic;
- 第 26 行:fopen(nom_fichier,"r") 以读取模式打开文件 nom_fichier(即 r=read)。 如果打开失败(例如文件不存在),fopen 返回 false;
- 第 34 行:fgets($fic,1000) 读取文件中的下一行,限制为 1000 个字符。 在 while ($ligne = fgets($fic, 1000)) {…} 语句中,包含两个连续的操作:1) $ligne = fgets(…) 2) while ( ! $ligne)。在读取完文件的最后一个字符后,函数 fgets 返回值 false,循环 while 随即终止。 函数 fgets 此处尝试读取最多 1000 个字符,但一旦遇到换行符即停止。由于此处所有行均少于 1000 个字符,因此 [fgets] 会读取一行文本(包含换行符)。 第75-84行的cutNewLineChar函数会去除可能存在的换行符;
- 第77行:函数strlen($chaîne) 返回$chaîne的字符数;
- 第 78 行:substr($ligne, $position, $taille) 返回 $taille 个字符,这些字符取自 $ligne,从第 $position 个字符开始,其中第 1 个字符编号为 0。 在 Windows 系统上,换行符为“\r\n”。在 Unix 系统上,换行符为字符串“\n”;
- 第 40 行:函数 array_shift($tableau) 移除 $tableau 的第一个元素并将其作为结果返回。此处忽略 array_shift 返回的结果;
- 第62行:函数 is_array($variable) 返回 true(若 $variable 为数组),否则返回 false;
- 第 63 行:函数 join 的作用与之前提到的函数 implode 相同;
3.10. 编码/解码 jSON

jSON 编码/解码(JavaScript 对象表示法)是本文主线练习中将大量使用的内容。[json-01.php, json-02.php, json-03.php] 脚本解释了后续需要了解的内容。
[json-01.php]脚本如下:
<?php
$array1 = ["nom" => "séléné", "prénom" => "bénédicte", "âge" => 34];
// array1 数组的 JSON 编码,其中 Unicode 字符已转义
print "encodage json du tableau array1 avec caractères Unicode échappés\n";
$json1 = json_encode($array1);
print "json1=$json1\n";
// array1 数组的 JSON 编码,其中 Unicode 字符未转义
print "encodage json du tableau array1 avec caractères Unicode non échappés\n";
$json2 = json_encode($array1, JSON_UNESCAPED_UNICODE);
print "json2=$json2\n";
// 将 jSON 解码为关联数组
print "décodage jSON de json2 dans tableau associatif\n";
$array2 = json_decode($json2, true);
var_dump($array2);
foreach ($array2 as $key => $value) {
print "$key:$value\n";
}
// 将 jSON 解码为对象
print "décodage jSON de json2 dans objet stdClass\n";
$array2 = json_decode($json2);
var_dump($array2);
print "prénom=$array2->prénom\n";
print "nom=$array2->nom\n";
print "âge=$array2->âge\n";
结果
注释
- 代码第 6 行:函数 [json_encode] 将其参数转换为字符串 jSON;
- 结果的第2行:生成的字符串为jSON。Unicode字符éâ已被替换为以\u开头的Unicode代码;
- 代码第 10 行:再次执行相同操作,但这次要求保留 Unicode 字符的原始形式;
- 结果第 4 行:生成的字符串为 jSON。该字符串可读性大大提高;
- 代码第14-18行:执行反向操作。将字符串 jSON 转换为关联数组;
- 结果第6-13行:可见已获取到一个关联数组;
- 代码第19-25行:将字符串 jSON 转换为类型为 [stdClass] 的对象;
- 结果第18-25行:可见已获取到一个类型为[stdClass]的对象;
- 代码第23-25行:对象O的属性A被标记为[O→A];
可以像以下脚本 [json-02.php] 所示,将多级数组编码为 jSON:
<?php
$array = ["nom" => "séléné", "prénom" => "bénédicte", "âge" => 34,
"mari" => ["nom" => "icariù", "prénom" => "ignacio", "âge" => 35],
"enfants" => [
["prénom" => "angèle", "age" => 8],
["prénom" => "andré", "age" => 2],
]];
// 对多级数组进行 jSON 编码
print "encodage jSON d'un tableau à plusieurs niveaux\n";
$json = json_encode($array, JSON_UNESCAPED_UNICODE);
print "json=$json\n";
结果
encodage jSON d'un tableau à plusieurs niveaux
json={"nom":"séléné","prénom":"bénédicte","âge":34,"mari":{"nom":"icariù","prénom":"ignacio","âge":35},"enfants":[{"prénom":"angèle","age":8},{"prénom":"andré","age":2}]}
注释
在字符串 jSON 中:
- 非关联数组用方括号 [] 包围;
- 关联数组用大括号 {} 括起;
脚本 [json-03.php] 演示了如何处理以下文件 jSON [famille.json]:
{
"épouse": {
"nom": "séléné",
"prénom": "bénédicte",
"âge": 34
},
"mari": {
"nom": "icariù",
"prénom": "ignacio",
"âge": 35
},
"enfants": [
{
"prénom": "angèle",
"age": 8
},
{
"prénom": "andré",
"age": 2
}
]
}
脚本 [json-03.php] 如下:
<?php
// 读取 jSON 文件
$json = file_get_contents("famille.json");
// 将 JSON 解码为对象
$famille1 = json_decode($json);
print "----famille1\n";
var_dump($famille1);
// 将 JSON 解码为关联数组
print "----famille2\n";
$famille2 = json_decode($json, true);
var_dump($famille2);
注释
- 第 4 行:函数 [file_get_contents] 读取名为 [famille.json] 的文件内容,并将其存入变量 [$json] 中;
- 随后将该变量解码为对象(第5-8行)和关联数组(第9-12行);
结果
----famille1
object(stdClass)#2 (3) {
["épouse"]=>
object(stdClass)#1 (3) {
["nom"]=>
string(9) "séléné"
["prénom"]=>
string(11) "bénédicte"
["âge"]=>
int(34)
}
["mari"]=>
object(stdClass)#3 (3) {
["nom"]=>
string(7) "icariù"
["prénom"]=>
string(7) "ignacio"
["âge"]=>
int(35)
}
["enfants"]=>
array(2) {
[0]=>
object(stdClass)#4 (2) {
["prénom"]=>
string(7) "angèle"
["age"]=>
int(8)
}
[1]=>
object(stdClass)#5 (2) {
["prénom"]=>
string(6) "andré"
["age"]=>
int(2)
}
}
}
----famille2
array(3) {
["épouse"]=>
array(3) {
["nom"]=>
string(9) "séléné"
["prénom"]=>
string(11) "bénédicte"
["âge"]=>
int(34)
}
["mari"]=>
array(3) {
["nom"]=>
string(7) "icariù"
["prénom"]=>
string(7) "ignacio"
["âge"]=>
int(35)
}
["enfants"]=>
array(2) {
[0]=>
array(2) {
["prénom"]=>
string(7) "angèle"
["age"]=>
int(8)
}
[1]=>
array(2) {
["prénom"]=>
string(6) "andré"
["age"]=>
int(2)
}
}
}
注释
- 第 1-38 行:由文件 jSON [famille.json] 解码生成的对象;
- 第39-76行:由文件jSON和[famille.json]解码生成的关联数组;