PHP8-快速脚本参考-一-

133 阅读16分钟

PHP8 快速脚本参考(一)

原文:PHP 8 Quick Scripting Reference

协议:CC BY-NC-SA 4.0

一、使用 PHP

要开始用 PHP 开发,创建一个带有.php文件扩展名的纯文本文件,并在您选择的编辑器中打开它——例如,Notepad、jEdit、Dreamweaver、NetBeans 或 PHPEclipse。这个 PHP 文件可以包含任何 HTML,以及 PHP 脚本代码。首先为 HTML 5 web 文档输入以下最小标记。

<!doctype html>
<html>
 <head>
  <meta charset="UTF-8">
  <title>PHP Test</title>
 </head>
 <body></body>
</html>

嵌入 PHP

PHP 代码可以以几种不同的方式嵌入到 web 文档的任何地方。标准符号是用<?php?>来分隔代码。这被称为一个 PHP 代码块,或者仅仅是一个 PHP 块

<?php ... ?>

在一个 PHP 块内,PHP 引擎被称为处于 PHP 模式;在程序块之外,引擎处于 HTML 模式。在 PHP 模式下,所有内容都由 PHP 引擎解析(执行),而在 HTML 模式下,所有内容都不经过任何解析就被发送到生成的网页。

切换到 PHP 模式的第二个符号是第一个符号的简短版本,其中省略了php部分。虽然这种符号较短,但是如果 PHP 代码需要可移植性,那么较长的符号更好。这是因为可以在php.ini配置文件中禁用对短分隔符的支持。 1

<? ... ?>

第三种方法(现在已经过时)是将 PHP 代码嵌入到 HTML 脚本元素中,并将 language 属性设置为php。这种替代分隔符很少使用;PHP 7 中移除了对它的支持。

<script language="php">...</script>

您在遗留代码中可能遇到的另一个过时的符号是当脚本嵌入在 ASP 标记之间时。默认情况下,这种表示法是禁用的,但是可以从 PHP 配置文件中启用。长期以来,不鼓励使用这种符号。PHP 7 最终移除了启用它的功能。

<% ... %>

脚本文件中的最后一个结束标记可能会被省略,以使文件在 PHP 模式下结束。

<?php ... ?>
<?php ...

输出文本

用 PHP 打印文本是通过键入 echoprint 后跟输出来完成的。每条语句必须以分号(;)结尾,以便与其他语句区分开来。PHP 块中最后一个语句的分号是可选的,但是包含它是一个好习惯。

<?php
  echo  "Hello World";
  print "Hello World";
?>

也可以使用<?=打开分隔符生成输出。从 PHP 5.4 开始,即使禁用了短 PHP 分隔符,该语法仍然有效。

<?= "Hello World" ?>

请记住,网页上显示的文本应该始终位于 HTML body 元素中。

<body>
  <?php echo "Hello World"; ?>
</body>

安装 Web 服务器

要在浏览器中查看 PHP 代码,首先必须在安装了 PHP 模块的 web 服务器上解析代码。建立 PHP 环境的一个简单方法是下载并安装一个流行的 Apache web 服务器发行版,名为 XAMPP, 2 ,它预装了 PHP、Perl 和 MariaDB。它适用于 Windows、Linux 以及 OS X,并允许您在自己的计算机上试验 PHP。

安装 web 服务器后,将浏览器指向http://localhost以确保服务器在线。它应该显示index.php文件,默认情况下,该文件在 Windows 上位于C:\xampp\htdocs\下,在 Linux 上位于/opt/lampp/htdocs/下。htdocs是 Apache web 服务器在您的域中查找文件的文件夹。

你好世界

继续前面的内容,简单的 Hello World PHP web 文档应该如下所示:

<!doctype html>
<html>
 <head>
  <meta charset="UTF-8">
  <title>PHP Test</title>
 </head>
 <body>
  <?php echo "Hello World"; ?>
 </body>
</html>

要查看这个被解析成 HTML 的 PHP 文件,请将其保存到 web 服务器的htdocs文件夹(服务器的根目录)中,并使用诸如mypage.php之类的名称。然后将你的浏览器指向它的路径,这是本地网络服务器的http://localhost/mypage.php

当请求 PHP 网页时,脚本在服务器上被解析,并作为 HTML 发送给浏览器。如果查看网站的源代码,它将不会显示任何生成页面的服务器端代码,只显示 HTML 输出。

编译和解析

PHP 是一种解释型语言,不是编译型语言。访问者每次到达一个 PHP 网站,PHP 引擎都会编译代码并解析成 HTML,然后发送给访问者。这样做的主要优点是代码可以很容易地更改,而不必重新编译和重新部署网站。主要缺点是在运行时编译代码需要更多的服务器资源。

对于小型网站来说,缺少服务器资源很少是个问题。与执行数据库查询所需的时间等其他因素相比,编译 PHP 代码所需的时间也很少。然而,对于具有大量流量的大型 web 应用,编译 PHP 文件的服务器负载可能会很大。对于这样的站点,可以通过预编译 PHP 代码来消除脚本编译开销。这可以通过使用 PHP 加速器来实现,比如 WinCache、 3 ,它可以在编译状态下缓存 PHP 脚本。

一个只提供静态内容(对所有访问者都一样)的网站还有另一种可能,就是缓存完全生成的 HTML 页面。这提供了拥有动态站点的所有维护好处,并具有静态站点的速度。一个这样的缓存工具是 WordPress CMS 的 W3 总缓存 4 插件。

PHP 的每个新版本不仅改进了语言,还改进了 PHP 引擎的性能。特别是 PHP 8 增加了 JIT (just in time)编译器。这个编译器不断寻找频繁重复执行的 PHP 代码。然后,这些代码被编译成机器码并被缓存,这使得代码的执行速度甚至比常规的缓存预编译代码还要快。

评论

注释用于在代码中插入注释。它们对脚本的解析没有影响。PHP 有两种标准的 C++符号用于单行(//)和多行(/* */)注释。Perl 注释符号(#)也可以用来做单行注释。

<?php
  // single-line comment
  #  single-line comment
  /* multi-line
     comment */
?>

和 HTML 一样,空白字符——比如空格、制表符和注释——被 PHP 引擎忽略。这让你在如何格式化你的代码上有很大的自由度。

Footnotes 1

www.php.net/manual/en/configuration.file.php

  2

www.apachefriends.org

  3

https://sourceforge.net/projects/wincache/

  4

http://wordpress.org/extend/plugins/w3-total-cache

 

二、变量

变量用于存储数据,如数字或字符串,以便它们可以在代码中多次使用。

定义变量

变量以美元符号($)开始,后面跟着一个标识符,这是变量的名称。变量的一个常见命名约定是,除了第一个单词,每个单词最初都要大写。

$myVar;

可以使用等号或赋值运算符(=)给变量赋值。然后变量变成定义的初始化的

$myVar = 10;

一旦定义了变量,就可以通过引用变量名来使用它。例如,可以通过使用echo后跟变量名,将变量值打印到网页上。

echo $myVar; // "10"

请记住,变量名区分大小写。PHP 中的名称可以包含下划线和数字,但不能以数字开头。它们也不能包含空格或特殊字符,并且不能是保留关键字。

数据类型

PHP 是一种松散类型的语言。这意味着没有指定变量可以存储的数据类型。相反,变量的数据类型会自动更改,以保存分配给它的值。

$myVar = 1;   // int type
$myVar = 1.5; // float type

此外,变量值的计算方式也不同,这取决于使用它的上下文。

// Float type evaluated as string type
echo $myVar; // "1.5"

由于这些隐式类型转换,知道变量的基础类型并不总是必要的。然而,理解 PHP 在后台处理的数据类型是很重要的。这十种类型列于表 2-1 中。

表 2-1

PHP 数据类型

|

数据类型

|

种类

|

描述

| | --- | --- | --- | | (同 Internationalorganizations)国际组织 | 数量 | 整数。 | | 漂浮物 | 数量 | 浮点数。 | | 弯曲件 | 数量 | 布尔值。 | | 线 | 数量 | 一系列字符。 | | 排列 | 复合材料 | 值的集合。 | | 目标 | 复合材料 | 用户定义的数据类型。 | | 资源 | 特别的 | 外部资源。 | | 请求即付的 | 特别的 | 函数或方法。 | | 混合的 | 特别的 | 任何类型。 | | 空 | 特别的 | 没有价值。 |

整数类型

整数是一个整数。它们可以用十进制(基数 10)、十六进制(基数 16)、八进制(基数 8)或二进制(基数 2)表示法来指定。十六进制数字前面有一个0x,八进制数字前面有一个0,二进制数字前面有一个0b

$myInt = 1234; // decimal number
$myInt = 0b10; // binary number (2 in decimal)
$myInt = 0123; // octal number (83 in decimal)
$myInt = 0x1A; // hexadecimal number (26 in decimal)

PHP 中的整数总是有符号的,因此可以存储正值和负值。整数的大小取决于系统字长,所以在 32 位系统上,最大可存储值是 2 ³²-1 。如果 PHP 遇到一个更大的值,它会被解释为浮点型。

浮点型

float 或 floating-point 类型可以存储实数。这些可以用十进制或指数记数法来指定。

$myFloat = 1.234;
$myFloat = 3e2; // 3*10² = 300

浮点的精度取决于平台。通常使用 64 位 IEEE 格式,可以保存大约 14 位十进制数,最大十进制值为 1.8×10 308

布尔类型

bool 类型可以存储布尔值,该值只能为 true 或 false。这些值由关键字truefalse指定。

$myBool = true;

零点类型

不区分大小写的常量null用于表示没有值的变量。这种变量被认为是特殊的空数据类型。

$myNull = null; // variable is set to null

与其他值一样,null 值的计算方式也不同,这取决于变量使用的上下文。如果评估为 bool,它将变为 false 作为一个数,它变成零(0);而作为字符串,就变成了空字符串("")。

$myInt = $myNull + 0;      // numeric context (0)
$myBool = $myNull == true; // bool context (false)
echo $myNull;              // string context ("")

默认值

从 PHP 8 开始,变量在使用前必须被定义。尝试使用未定义的变量将触发错误异常,从而停止脚本的执行。

// PHP 8
$myDefined = null;
echo $myDefined; // ok
echo $myUndefined; // error exception

在 PHP 8 之前,可以使用没有赋值的变量。这种未定义的变量将自动创建一个空值。

// Before PHP 8
echo $myUndefined; // created and set to null

尽管这种行为是允许的,但在使用变量之前定义变量一直是一种良好的编码实践,即使变量只是被设置为 null。提醒一下,PHP 8 之前的 PHP 版本在使用未定义的变量时会发出错误通知。

Notice: Undefined variable: myUndefined in C:\xampp\htdocs\mypage.php on line 10

三、运算符

数字运算符是一个符号,它使脚本执行特定的数学或逻辑操作。数字运算符可以分为五种类型:算术、赋值、比较、逻辑和按位运算符。

算术运算符

算术运算符包括四种基本算术运算,以及用于获得除法余数的模数运算符(%)。

$x = 4 + 2; // 6, addition
$x = 4 - 2; // 2, subtraction
$x = 4 * 2; // 8, multiplication
$x = 4 / 2; // 2, division
$x = 4 % 2; // 0, modulus (division remainder)

PHP 5.6 中引入了一个求幂运算符(**)。它将左边的操作数提升到右边操作数的幂。

$x = 4 ** 2; // 16, exponentiation

赋值运算符

第二组是赋值操作符,最重要的是赋值操作符(=)本身,它给变量赋值。

$x = 1; // assignment

组合赋值运算符

赋值运算符和算术运算符的一个常见用途是对变量进行运算,然后将结果保存回同一个变量中。使用组合赋值操作符可以缩短这些操作。

$x += 5; // $x = $x+5;
$x -= 5; // $x = $x-5;
$x *= 5; // $x = $x*5;
$x /= 5; // $x = $x/5;
$x %= 5; // $x = $x%5;

PHP 5.6 中添加的取幂运算符也接受了一个简写赋值运算符。

$x **= 5; // $x = $x**5;

递增和递减运算符

另一种常见的操作是将变量加 1 或减 1。这可以用增量(++)和减量(--)操作符来简化。

$x++; // $x += 1;
$x--; // $x -= 1;

这两个运算符都可以用在变量之前或之后。

$x++; // post-increment
$x--; // post-decrement
++$x; // pre-increment
--$x; // pre-decrement

无论使用哪个变量,变量的结果都是相同的。区别在于后操作符在改变变量之前返回原始值,而前操作符首先改变变量,然后返回值。

$x = 5; $y = $x++; // $x=6, $y=5
$x = 5; $y = ++$x; // $x=6, $y=6

比较运算符

比较运算符比较两个值并返回truefalse。它们主要用于指定条件,即评估为truefalse的表达式。

$x = (2 == 3);  // equal to (false)
$x = (2 != 3);  // not equal to (true)
$x = (2 <> 3);  // not equal to (alternative)
$x = (2 === 3); // identical (false)
$x = (2 !== 3); // not identical (true)
$x = (2 > 3);   // false,greater than (false)
$x = (2 < 3);   // less than (true)
$x = (2 >= 3);  // greater than or equal to (false)
$x = (2 <= 3);  // less than or equal to (true)

严格相等运算符===!==用于比较类型和值。这是必要的,因为常规的“等于”(==)和“不等于”(!=)运算符在比较操作数之前会自动执行类型转换。当不需要“等于”运算的类型转换功能时,使用严格比较被认为是一种好的做法。

$x = (1 ==  "1"); // true (same value)
$x = (1 === "1"); // false (different types)

PHP 7 增加了一个新的比较操作符,叫做飞船操作符 ( <=>)。它比较两个值,如果两个值相等,则返回 0;如果左边的值较大,则返回 1;如果右边的值较大,则返回–1。

$x = 1 <=> 1; // 0 (1 == 1)
$x = 3 <=> 2; // 1 (3 > 2)
$x = 1 <=> 2; // -1 (1 < 2)

逻辑运算符

逻辑运算符通常与比较运算符一起使用。如果左右两边都为真,则逻辑与(&&)计算为true,如果左右两边都为真,则逻辑或(||)计算为true。对一个布尔结果求反,有一个逻辑非(!)运算符。请注意,对于“逻辑与”和“逻辑或”,如果左侧已经确定了结果,则不会计算运算符的右侧。

$x = (true && false); // logical and (false)
$x = (true || false); // logical or (true)
$x = !true;           // logical not (false)

按位运算符

按位运算符可以处理二进制数字。例如,xor 运算符(^)打开运算符一侧设置的位,而不是两侧设置的位。

$x = 5 & 4;  // 101&100=100 (4) // and
$x = 5 | 4;  // 101|100=101 (5) // or
$x = 5 ^ 4;  // 101¹⁰⁰=001 (1) // xor (exclusive or)
$x = 4 << 1; // 100<<1=1000 (8) // left shift
$x = 4 >> 1; // 100>>1=10   (2) // right shift
$x = ~4;     // ~00000100=11111011 (-5) // invert

这些位操作符有速记赋值操作符,就像算术操作符一样。

$x=5; $x &= 4;  // 101&100=100 (4) // and
$x=5; $x |= 4;  // 101|100=101 (5) // or
$x=5; $x ^= 4;  // 101¹⁰⁰=001 (1) // xor
$x=5; $x <<= 1; // 101<<1=1010 (10)// left shift
$x=5; $x >>= 1; // 101>>1=10   (2) // right shift

请注意,与按位运算符一起使用的十进制数自动计算为二进制数。二进制记数法也可用于为按位运算指定二进制数。

$x = 0b101 & 0b100; // 0b100 (4)

运算符优先级

当表达式包含多个运算符时,这些运算符的优先级决定了它们的求值顺序。优先顺序见表 3-1 。

表 3-1

运算符优先级的顺序

|

在…之前

|

操作员

|

在…之前

|

操作员

| | --- | --- | --- | --- | | one | ** | Ten | & | | Two | ++ - | Eleven | ^ | | three | ~ -(一元) | Twelve | | | | four | ! | Thirteen | && | | five | * / % | Fourteen | || | | six | + -(二进制) | Fifteen | = op= | | seven | << >> | Sixteen | 和 | | eight | < <= > >= <> | Seventeen | 异或运算 | | nine | == != === !== <=> | Eighteen | 或者 |

举个例子,乘法的优先级高于加法,因此在下面一行代码中首先计算乘法。

$x = 4 + 3 * 2; // 10

圆括号可用于强制优先级。括号中的表达式在该语句中的其他表达式之前进行计算。

$x = (4 + 3) * 2; // 14

附加逻辑运算符

在优先表中,特别注意最后三个运算符:andorxorandor运算符的工作方式与逻辑&&||运算符相同。唯一的区别是它们的优先级较低。

// Same as: $x = (true && false);
$x = true && false; // $x is false (0)

// Same as: ($x = true) and false;
$x = true and false; // $x is true (1)

xor运算符是位^运算符的布尔版本。如果只有一个操作数为真,则计算结果为true

$x = (true xor true); // false

四、字符串

字符串是可以存储在变量中的一系列字符。在 PHP 中,字符串通常由单引号分隔。

$a = 'Hello';

串并置

PHP 有两个字符串操作符。点符号被称为串联运算符 ( .)。它将两个字符串合并为一个。它还有一个伴随的赋值操作符(.=),将右边的字符串追加到左边的字符串变量。

$b = $a . ' World'; // Hello World
$a .= ' World';     // Hello World

分隔字符串

PHP 字符串可以用四种不同的方式分隔。有两种常见的符号:双引号(" ")和单引号(' ')。它们之间的区别在于变量不是在单引号字符串中解析的,而是在双引号字符串中解析的。

$c = 'World';
echo "Hello $c"; // "Hello World"
echo 'Hello $c'; // "Hello $c"

除非需要解析,否则倾向于使用单引号字符串,这突出表明不进行解析。然而,双引号字符串被认为更容易阅读,这使得选择更多的是一个偏好问题。重要的是要始终如一。

除了单引号和双引号字符串,还有两种符号: heredocnowdoc 。这些符号主要用于包含较大的文本块。

继承字符串

heredoc 语法由<<<操作符后跟一个标识符和一个新行组成。然后包含该字符串,后跟一个包含标识符的新行,以结束该字符串。

$s = <<<LABEL
Heredoc string (with parsing)
Goodbye
LABEL;

变量在 heredoc 字符串中解析,就像双引号字符串一样。

$name = 'John';
$s = <<<LABEL
Hello $name
LABEL;
echo $s; // "Hello John"

Nowdoc 字符串

nowdoc 字符串的语法与 heredoc 字符串的语法相同,只是初始标识符用单引号括起来。在 nowdoc 字符串中不解析变量。

$s = <<<'LABEL'
Nowdoc string (without parsing)
LABEL;

在定义延伸多行的长字符串时,Heredoc 和 nowdoc 字符串对于提高可读性很有用。这些字符串中出现的任何特殊字符(如换行符或引号)都将被包含在内,而无需使用转义符。

转义字符

转义字符用于书写特殊字符,如反斜杠、换行符和双引号。这些字符前面总是有一个反斜杠(\)。表 4-1 列出了 PHP 中可用的转义字符。

表 4-1

转义字符

|

性格;角色;字母

|

意义

|

性格;角色;字母

|

意义

| | --- | --- | --- | --- | | \n | 新行 | \f | 换页 | | \t | 横表 | $ | 美元符 | | \v | 垂直标签 | ' | 单引号 | | \e | 逃跑 | " | 双引号 | | \r | 回车 | \ | 反斜线符号 | | \u{} | Unicode 字符 |   |   |

例如,换行符在字符串中用转义字符(\n)表示。

$s = "Hello\nWorld";

注意,这个字符不同于在网页上创建换行符的<br> HTML 标签。

echo "Hello<br>World";

当使用单引号或 nowdoc 分隔符时,唯一有效的转义字符是反斜杠(\\)和单引号(\')字符。只有在单引号之前或字符串末尾才需要转义反斜杠。

$s = 'It\'s'; // "It's"

PHP 7 引入了 Unicode 转义字符,它提供了将 UTF 8 编码的字符嵌入字符串的能力。这样的字符被指定为花括号内的十六进制数。该数字最长可达六位数,前导零是可选的。

echo "\u{00C2A9}"; // © (copyright sign)
echo "\u{C2A9}";   // ©

字符引用

可以通过在字符串变量后的方括号中指定所需字符的索引来引用字符串中的字符,从零开始。这可用于访问和修改单个字符。

$s = 'Hello';
$s[0] = 'J';
echo $s; // "Jello"

strlen函数获取字符串参数的长度。例如,这可以用来改变字符串的最后一个字符。

$s[strlen($s)-1] = 'y';
echo $s; // "Jelly"

字符串比较

比较两个字符串的方法很简单,就是使用其中一个相等运算符。这不像在其他语言中那样比较内存地址。

$a = 'test';
$b = 'test';
$c = ($a === $b); // true

字符串函数

PHP 有许多处理和操作字符串的内置函数。这里可以看到一些更常用的字符串函数的例子。

$a = 'String';

// Search and replace characters in a string
$b = str_replace('i', 'o', $a); // Strong

// Insert text at specified position
$b = substr_replace($a, 'My ', 0, 0); // My String

// Get part of string specified by start and length
$b = substr($a, 0, 3); // Str

// Convert to uppercase
$b = strtoupper($a); // STRING

// Find position of the first occurrence of a substring
$i = strpos($a, 'i'); // 3 (starts at 0)

// Get string length
$i = strlen($a); // 6

五、数组

数组用于在单个变量中存储值的集合。PHP 中的数组由键值对组成。该键可以是整数(数值数组)、字符串(关联数组)或两者的组合(混合数组)。该值可以是任何数据类型。

数字数组

数字数组用数字索引存储数组中的每个元素。使用array构造函数创建一个数组。此构造函数接受一个值列表,这些值被分配给数组的元素。

$a = array(1,2,3);

从 PHP 5.4 开始,有了一个更短的语法,其中数组构造函数被方括号取代。

$a = [1,2,3];

一旦创建了数组,就可以通过将所需元素的索引放在方括号中来引用它的元素。请注意,索引从零开始。

$a[0] = 1;
$a[1] = 2;
$a[2] = 3;

数组中元素的数量是自动处理的。向数组中添加新元素就像给它赋值一样简单。

$a[3] = 4;

也可以省略索引,将值添加到数组的末尾。如果变量还没有数组,这个语法也会构造一个新的数组。

$a[] = 5; // $a[4]

要检索数组中某个元素的值,需要在方括号中指定该元素的索引。

echo "$a[0] $a[1] $a[2] $a[3]"; // "1 2 3 4"

关联数组

在关联数组中,键是一个字符串而不是一个数字索引,这给元素一个名称而不是一个数字。创建数组时,使用双箭头操作符(=>)来判断哪个键代表哪个值。

$b = array('one' => 'a', 'two' => 'b', 'three' => 'c');

使用元素名称引用关联数组中的元素。不能用数字索引引用它们。

$b['one'] = 'a';
$b['two'] = 'b';
$b['three'] = 'c';

echo $b['one'] . $b['two'] . $b['three']; // "abc"

双箭头运算符也可以与数字数组一起使用,以决定将值放在哪个元素中。

$c = array(0 => 0, 1 => 1, 2 => 2);

并非所有的键都需要指定。如果某个键未被指定,则该值被赋给先前使用的最大整数键后面的元素。

$e = array(5 => 5, 6);

混合数组

PHP 不区分关联数组和数值数组,所以两者的元素可以组合在同一个数组中。

$d = array(0 => 1, 'foo' => 'bar');

只是要确保用相同的键访问元素。

echo $d[0] . $d['foo']; // "1bar"

多维数组

多维数组是包含其他数组的数组。例如,二维数组可以通过以下方式构建。

$a = array(array('00', '01'), array('10', '11'));

创建后,可以使用两组方括号修改元素。

$a[0][0] = '00';
$a[0][1] = '01';
$a[1][0] = '10';
$a[1][1] = '11';

它们的访问方式也是一样的。

echo $a[0][0] . $a[0][1] . $a[1][0] . $a[1][1];

可以给这个键一个字符串名,使它成为一个多维关联数组,也称为哈希表

$b = array('one' => array('00', '01'));
echo $b['one'][0] . $b['one'][1]; // "0001"

通过添加额外的方括号组,多维数组可以有两个以上的维度。

$c[][][][] = "0000"; // four dimensions

六、条件

条件语句用于根据不同的条件执行不同的代码块。

如果语句

只有当括号内的条件被评估为true时,if语句才会执行。条件可以包括任何比较和逻辑运算符。

$x = 1;
// ...
if ($x == 1) {
  echo 'x is 1';
}

为了测试其他条件,if语句可以用任意数量的elseif子句来扩展。只有当所有先前的条件都为假时,才会测试每个附加条件。

elseif ($x == 2) {
  echo 'x is 2';
}

对于处理所有其他情况,可以在末尾有一个else子句,如果前面的所有条件都为假,则执行该子句。

else {
  echo 'x is something else';
}

如果只需要有条件地执行一条语句,可以省去花括号。但是,包含它们被认为是一种好的做法,因为它们可以提高代码的可读性。

if ($x == 1)
  echo 'x is 1';
elseif ($x == 2)
  echo 'x is 2';
else
  echo 'x is something else';

请记住,PHP 中变量的作用域不受本地代码块的限制,这与许多其他编程语言不同。因此,即使在代码块结束后,在条件语句中创建的变量仍然可以访问。

if ($x == 1) {
  $output = 'True';
}
else {
  $output = 'False';
}
echo $output; // ok

任何表达式都可以用作 if 语句的条件,不仅仅是布尔值。如下所示,任何类似零的表达式都将被计算为 false。所有其他值都被认为是真的。

if (0) {} // false
if (-0.0) {} // false
if ('') {} // false (empty string)
if (array()) {} // false (empty array)
if (null) {} // false (no value)
if (-1) {} // true (non-zero number)

交换语句

switch语句检查整数、浮点或字符串与一系列 case 标签之间的相等性。然后,它将执行传递给匹配的案例。该语句可以包含任意数量的 case 子句,并且可以以处理所有其他 case 的默认标签结束。

switch ($x)
{
  case 1: echo 'x is 1'; break;
  case 2: echo 'x is 2'; break;
  default: echo 'x is something else';
}

请注意,每个 case 标签后面的语句没有用花括号括起来。相反,语句以关键字break结束,以脱离开关。如果没有中断,执行将转到下一个案例。如果需要以相同的方式评估几个案例,这将非常有用。

替代语法

PHP 为条件语句提供了另一种语法。在这个语法中,if语句的左括号被替换为冒号,右括号被删除,最后一个右括号被替换为endif关键字。

if ($x == 1):     echo 'x is 1';
elseif ($x == 2): echo 'x is 2';
else:             echo 'x is something else';
endif;

类似地,switch 语句也有一个替代语法,它使用endswitch关键字来终止语句。

switch ($x):
  case 1:  echo 'x is 1'; break;
  case 2:  echo 'x is 2'; break;
  default: echo 'x is something else';
endswitch;

对于较长的条件语句,替代语法通常更可取,因为这样更容易看到这些语句的结束位置。

混合模式

在代码块中间切换回 HTML 模式是可能的。这提供了编写将文本输出到网页的条件语句的另一种方式。

<?php if ($x == 1) { ?>
  This will show if $x is 1.
<?php } else { ?>
  Otherwise this will show.
<?php } ?>

也可以以这种方式使用替代语法,以使代码更加清晰。

<?php if ($x == 1): ?>
  This will show if $x is 1.
<?php else: ?>
  Otherwise this will show.
<?php endif; ?>

当输出 HTML 和文本时,尤其是较大的块,这种编码风格通常是首选的,因为它更容易区分 PHP 代码和出现在网页上的 HTML 内容。

三元运算符

除了ifswitch语句,还有三元运算符(?:)。该操作符可以替换单个if/else子句。运算符有三个表达式。如果第一个求值为true,则返回第二个表达式,如果为false,则返回第三个。

// Ternary operator expression
$y = ($x == 1) ? 1 : 2;

在 PHP 中,这个操作符可以用作表达式和语句。

// Ternary operator statement
($x == 1) ? $y = 1 : $y = 2;

编程术语表达式指的是计算出一个值的代码,而语句是以分号或右花括号结束的代码段。

匹配表达式

PHP 8 增加了一个新的条件语句,叫做匹配表达式。它对 switch 语句进行了一些改进,包括更简洁的语法。考虑下面的 switch 语句。

switch ($x)
{
  case 1: $output = 'True' break;
  case 2: $output = 'False'; break;
  default: $output = 'Unknown';
}

该语句可以重写为如下所示的匹配表达式。请注意,没有使用 break 关键字,表达式以分号结束。

$output = match($x) {
    1 => 'True',
    2 => 'False',
    default => 'Unknown'
};
echo $output;

每种情况下只允许一个表达式。返回值可以忽略,因此前一个示例也可以用下面的方式键入。

match($x) {
  1 => $output = 'True',
  2 => $output = 'False',
  default => $output = 'Unknown'
};
echo $output;

switch cases 使用松散比较(==)进行匹配,而 match expression cases 使用严格比较(===)进行匹配。这有助于避免微妙的转换错误。

$x = '1'; // string type
switch ($x)
{
  case 1: $output = 'integer'; break;
  case '1': $output = 'string';
}
echo $output; // "integer"

$output = match($x) {
  1 => 'integer',
  '1' => 'string',
};
echo $output; // "string"

要以相同方式处理的条件可以组合在同一行上,用逗号分隔。请记住,匹配表达式必须有匹配条件或默认大小写。否则会发生错误,代码停止执行。

$x = 4;
match($x) {
  1, 2, 3 => echo 'case 1, 2, or 3'
};
// Fatal error: Uncaught UnhandledMatchError

七、循环

PHP 中有四种循环结构。这些用于多次执行特定的代码块。

While 循环

只有当条件为真时,while循环才会遍历代码块。只要条件保持为真,它就继续循环。注意,条件只在每次迭代(循环)开始时检查。

$i = 0;
while ($i < 10) { echo $i++; } // 0-9

正如条件 if 语句一样,如果代码块中只有一条语句,则可以省略循环的花括号。

$i = 0;
while ($i < 10) echo $i++; // 0-9

Do-while 循环

除了检查代码块之后的条件之外,do-while循环的工作方式与while循环相同。因此,它总是至少在代码块中运行一次。请记住,这个循环以分号结束。

$i = 0;
do { echo $i++; } while ($i < 10); // 0-9

For 循环

for循环用于遍历一个代码块特定的次数。它使用三个参数。第一个参数初始化一个计数器,并且总是在循环之前执行一次。第二个参数保存循环的条件,并在每次迭代之前进行检查。第三个参数包含计数器的增量,在每次迭代结束时执行。

for ($i = 0; $i < 10; $i++) { echo $i; } // 0-9

for循环有多种变化,因为任何一个参数都可以省略。例如,如果省略第一个和第三个参数,它的行为方式与while循环相同。

$i = 0;
for (;$i < 10;) { echo $i++; }

第一个和第三个参数也可以使用逗号运算符(,)拆分成几个语句。

for ($i = 0, $x = 9; $i < 10; $i++, $x--) {
  echo $x; // 9-0
}

函数的作用是:获取数组中元素的数量。与for循环一起,它可以用来遍历一个数值数组。

$a = array(1,2,3);

for($i = 0; $i < sizeof($a); $i++) {
  echo $a[$i]; // "123"
}

如果不需要跟踪迭代,foreach循环提供了更清晰的语法。这个循环对于遍历关联数组也是必要的。

Foreach 循环

foreach循环提供了一种简单的方法来遍历数组。在每次迭代中,数组中的下一个元素被赋给指定的变量(迭代器),循环继续执行,直到遍历完整个数组。

$a = array(1,2,3);

foreach ($a as $v) {
  echo $v; // "123"
}

有一个对foreach循环的扩展,通过在迭代器前添加一个键变量,后跟双箭头操作符(=>)来获得键的名称或索引。

$a = array('one' => 1, 'two' => 2);

foreach ($a as $k => $v) {
  echo "$k = $v "; // "one = 1 two = 2 "
}

替代语法

与条件语句一样,循环中的括号可以用冒号和endwhileendforendforeach关键字之一重写为替代语法。

$i = 0;
while ($i < 10): echo $i++; endwhile;

for ($i = 0; $i < 10; $i++): echo $i; endfor;

$a = array(1,2,3);
foreach ($a as $v): echo $v; endforeach;

这样做的主要好处是提高了可读性,尤其是对于较长的循环。

破裂

有两个特殊的关键字可以在循环中使用— breakcontinue。关键字break结束一个循环结构的执行。

for ($i = 0; $i < 5; $i++)
{
  if ($i == 3) break; // end loop
  echo $i; // "012"
}

可以给它一个数值参数,指定要跳出多少个嵌套循环结构。

$i = 0;
while ($i++ < 10)
{
  for (;;) { break 2; } // end for and while
}

继续

可以在任何循环语句中使用continue关键字来跳过当前循环的剩余部分,并在下一次迭代的开始处继续。

for ($i = 0; $i < 5; $i++)
{
  if ($i == 2) continue; // start next iteration
  echo $i; // "0134"
}

这个关键字可以接受一个参数,指定它应该跳到多少个封闭循环的末尾。

$i = 0;
while ($i++ < 10)
{
  for (;;) { continue 2; } // start next while iteration
}

与许多其他语言相比,continue语句也适用于开关,其行为与break相同。因此,要从开关内部跳过一次迭代,需要使用continue 2

$i = 0;
while ($i++ < 10)
{
  switch ($i)
  {
    case 1: continue 2; // start next while iteration
  }
}

转到

PHP 5.3 中引入的第三个跳转语句是goto,它执行到指定标签的跳转。标签是一个名称后跟一个冒号(:)。

goto myLabel; // jump to label
myLabel: // label declaration

目标标签必须在相同的脚本文件和范围内。因此,goto不能用来跳转到循环结构中,只能跳出它们。

loop:
while (!$finished)
{
  // ...
  if ($try_again) goto loop; // restart loop
}

一般来说,最好避免使用goto语句,因为它会使执行流程难以跟踪。

八、函数

函数是可重用的代码块,只在被调用时执行。它们允许代码被分成更小的部分,更容易理解和重用。

定义函数

要创建一个函数,需要使用function关键字,后跟一个名称、一组括号和一个代码块。函数的命名约定 1 与变量的相同——使用一个描述性的名称,除了第一个单词以外,每个单词的首字母都大写。

function myFunc()
{
  echo 'Hello World';
}

一个函数代码块可以包含任何有效的 PHP 代码,包括其他函数定义。

调用函数

一旦定义了一个函数,就可以在页面上的任何地方调用它,方法是在它的名字后面加上一组括号。

myFunc(); // "Hello World"

函数名不区分大小写,但是使用与它们定义中相同的大小写是一个好习惯。

myfunc(); // "Hello World"
MYFUNC(); // "Hello World"

即使函数定义出现在脚本文件的更下面,也可以调用函数。

foo(); // allowed
function foo() {}

一个例外是,只有在满足特定条件时才定义函数。该条件代码必须在调用函数之前执行。

bar(); // error
if (true) { function bar() {} }
bar(); // ok

函数参数

函数名后面的括号用于向函数传递参数。为此,必须首先在函数定义中以逗号分隔的变量列表的形式指定相应的参数。然后可以在函数中使用这些参数。

function myFunc($x, $y)
{
  echo $x . $y;
}

使用指定的参数,可以使用相同数量的参数调用该函数。

myFunc('Hello', ' World'); // "Hello World"

准确地说,参数出现在函数定义中,而参数出现在函数调用中。然而,这两个术语有时会被错误地互换使用。

可选参数

可以通过在参数列表中为参数赋值来指定默认值。然后,如果在调用函数时未指定该参数,则使用默认值。为使其按预期工作,有默认值的参数声明在没有默认值的参数的右边是很重要的。

function myFunc($x, $y = ' Earth')
{
  echo $x . $y;
}

myFunc('Hello'); // "Hello Earth"

命名参数

PHP 8 引入了命名参数,通过指定相应参数的名称,允许参数以任意顺序传递。该特性通过允许跳过默认值来补充可选参数。

function myNamed($a, $b = 2, $c = 4)
{
  echo "$a $b $c";
}

myNamed(c: 3, a: 1); // "1 2 3"

可选参数和必需参数都可以命名,但是任何命名的参数都必须放在未命名的参数之后。

myNamed(1, c: 3); // "1 2 3"

可变参数列表

调用函数时不能使用少于其声明中指定的参数,但可以使用更多的参数。这允许传递可变数量的参数,然后可以使用几个内置函数来访问这些参数。为了一次得到一个参数,有一个func_get_arg函数。此函数采用单个参数,即要返回的参数,从零开始。

function myArgs()
{
  $x = func_get_arg(0);
  $y = func_get_arg(1);
  $z = func_get_arg(2);
  echo $x . $y . $z;
}

myArgs('Fee', 'Fi', 'Fo'); // "FeeFiFo"

还有两个函数与参数列表相关。函数func_num_args获取传递的参数数量,函数func_get_args返回一个包含所有参数的数组。它们可以一起用于允许函数处理可变数量的参数。

function myArgs2()
{
  $num = func_num_args();
  $args = func_get_args();
  for ($i = 0; $i < $num; $i++)
    echo $args[$i];
}

myArgs2('Fee', 'Fi', 'Fo'); // "FeeFiFo"

PHP 5.6 简化了可变参数列表的使用。从这个版本开始,参数列表可能包含一个可变参数,由省略号(...)标记表示,它接受可变数量的参数。可变参数表现为数组,并且必须始终是列表中的最后一个参数。

function myArgs3(...$args)
{
  foreach($args as $v) {
    echo $v;
  }
}

myArgs3(1, 2, 3); // "123"

作为一个补充功能,省略号还可以用来将一组值解包到一个参数列表中。

$a = [1, 2, 3];
myArgs3(...$a); // "123"

返回语句

return是一个跳转语句,它使函数结束执行并返回到它被调用的位置。

function myFunc()
{
  return; // exit function
  echo 'Hi'; // never executes
}

可以选择给它一个要返回的值,在这种情况下,它让函数调用计算该值。

function myFunc()
{
  // Exit function and return value
  return 'Hello';
}

echo myFunc(); // "Hello"

没有返回值的函数会自动返回 null。

function myNull() {}

if (myNull() === null)
  echo 'true'; // "true"

范围和寿命

通常,PHP 变量的作用域从声明它的地方开始,一直持续到页面的末尾。然而,在函数中引入了局部函数作用域。默认情况下,函数中使用的任何变量都被限制在这个局部范围内。一旦函数的作用域结束,局部变量就被销毁。

$x = 'Hello'; // global variable

function myFunc()
{
  $y = ' World'; // local variable
}

在 PHP 中,试图从一个函数中访问一个全局变量不起作用,而是创建一个新的局部变量。为了使一个全局变量可访问,该变量的范围必须通过用关键字global声明来扩展到函数。

$x = 'Hello'; // global $x

function myFunc()
{
  global $x; // use global $x
  $x .= ' World'; // change global $x
}

myFunc();
echo $x; // "Hello World"

从全局范围访问变量的另一种方法是使用预定义的$GLOBALS数组。变量通过其名称引用,名称被指定为不带美元符号的字符串。

function myFunc()
{
  $GLOBALS['x'] .= ' World'; // change global $x
}

与许多其他语言相比,控制结构(如循环和条件语句)没有自己的变量范围。因此,在这样的代码块中定义的变量在代码块结束时不会被销毁。

if(true)
{
  $x = 10; // global $x
}

echo $x; // "10"

除了全局和局部变量,PHP 还有属性变量;这些将在下一章讨论。

匿名函数

PHP 5.3 引入了匿名 函数,允许函数作为参数传递并赋给变量。匿名函数的定义类似于常规函数,只是它没有指定的名称。可以使用正常的赋值语法(包括分号)将函数赋给变量。这个变量可以作为一个函数来调用。

$say = function($name)
{
  echo "Hello " . $name;
};

$say("World"); // "Hello World"

匿名函数主要用作回调函数。这是一个作为参数传递给另一个函数的函数,该函数应该在执行过程中调用它。

function myCaller($myCallback)
{
  echo $myCallback();
}

// "Hello"
myCaller( function() { echo "Hello"; } );

通过这种方式,可以将函数注入到现有函数中,从而增加其通用性。例如,内置的array_map函数将其回调应用于给定数组的每个元素。

$a = [1, 2, 3];

$squared = array_map(function($val)
{
  return $val * $val;
}, $a);

foreach ($squared as $v)
  echo $v; // "149"

使用匿名函数的一个好处是,它们提供了一种简洁的方法来定义那些只在使用位置使用一次的函数。这也防止了这种一次性函数搞乱全局范围。

关闭

闭包是一个匿名函数,它可以捕获创建它的作用域的局部变量。在 PHP 中,所有的匿名函数都是闭包,它们是使用 Closure 类实现的。他们可以在函数头中用一个use子句指定要捕获的变量。

$x = 1, $y = 2;

$myClosure = function($z) use ($x, $y)
{
  return $x + $y + $z;
};

echo $myClosure(3); // "6"

箭头函数

PHP 7.4 引入了一种更简洁的匿名函数语法,称为箭头函数。与匿名函数一样,arrow 函数也是使用 Closure 类实现的,并支持相同的功能,不同之处在于 arrow 函数通过值自动从父作用域中捕获变量。因此,前面的例子可以简化如下。

$x = 1, $y = 2;
$myClosure = fn($z) => $x + $y + $z;
echo $myClosure(3); // "6"

箭头函数的主要优点是简洁,所以在指定匿名函数的参数时,它们使用保留关键字fn而不是function。他们也只允许一个单一的,隐式返回的表达式,所以他们不使用return关键字。

发电机

发生器是用于产生一系列值的函数。每个值都用一个yield语句返回。与 return 不同,yield语句保存函数的状态,允许它在被再次调用时从停止的地方继续。

function getNum()
{
  for ($i = 0; $i < 5; $i++) {
    yield $i;
  }
}

生成器函数的行为类似于迭代器;因此,它可以与foreach循环一起使用。循环继续进行,直到生成器不再产生更多的值。

foreach(getNum() as $v)
  echo $v; // "01234"

PHP 5.5 中引入了生成器。它们的使用在 PHP 7 中通过yield from语句得到了扩展,它允许一个生成器从另一个生成器、迭代器或数组中产生值。

function countToFive()
{
  yield 1;
  yield from [2, 3, 4];
  yield 5;
}

foreach (countToFive() as $v)
  echo $v; // "12345"

由于生成器只在需要时一次产生一个值,所以它们不需要一次性计算整个序列并存储在内存中。在生成大量数据时,这会带来显著的性能优势。

内置函数

PHP 自带了大量的内置函数,这些函数总是可用的,比如字符串和数组处理函数。其他函数取决于 PHP 编译时使用的扩展,例如,用于与 MySQL 数据库通信的 MySQLi 扩展。有关内置 PHP 函数的完整参考,请参见 PHP 函数参考。 2

Footnotes 1

www.php-fig.org/psr/psr-12/

  2

www.php.net/manual/en/funcref.php

 

九、类

一个是一个用来创建对象的模板。要定义一个,关键字class后跟一个名称和一个代码块。类的命名约定是混合使用大小写,这意味着每个单词最初都应该大写。

class MyRectangle {}

类体可以包含属性和方法。属性是保存对象状态的变量,而方法是定义对象能做什么的函数。属性在其他语言中也被称为字段属性。在 PHP 中,它们需要明确指定访问级别。在下面的例子中,使用了public访问级别,它提供了对属性的无限制访问。

class MyRectangle
{
  public $x, $y;
  function newArea($a, $b) { return $a * $b; }
}

为了从类内部访问成员,$this伪变量与单箭头操作符(->)一起使用。$this变量是对类的当前实例的引用,只能在对象上下文中使用。没有它,$x$y只会被视为局部变量。

class MyRectangle
{
  public $x, $y;

  function newArea($a, $b) {
    return $a * $b;
  }

  function getArea() {
    return $this->newArea($this->x, $this->y);
  }
}

实例化对象

要从封闭类的外部使用类的成员,必须首先创建该类的对象。这是使用new关键字完成的,它创建一个新的对象或实例。

$r = new MyRectangle(); // object instantiated

对象包含自己的一组属性,这些属性可以保存与类的其他实例不同的值。与函数一样,即使类定义出现在脚本文件的更下面,也可以创建类的对象。

$r = new MyDummy(); // allowed
class MyDummy {};

访问对象成员

要访问属于某个对象的成员,需要使用单箭头运算符(->)。它可用于调用方法或为属性赋值。

$r->x = 5;
$r->y = 10;
$r->getArea(); // 50

另一种初始化属性的方法是使用初始属性值。

初始属性值

如果一个属性需要有一个初始值,一个简洁的方法是在声明它的同时给它赋值。然后在创建对象时设置这个初始值。这种类型的赋值必须是常量表达式。例如,它不能是变量或数学表达式。

class MyRectangle
{
  public $x = 5, $y = 10;
}

构造器

一个类可以有一个构造函数,这是一个用来初始化(构造)对象的特殊方法。此方法提供了一种初始化属性的方法,这种方法不限于常量表达式。在 PHP 中,构造函数以两个下划线开头,后跟单词construct。像这样的方法被称为魔术方法

class MyRectangle
{
  public $x, $y;

  function __construct()
  {
    $this->x = 5;
    $this->y = 10;
    echo "Constructed";
  }
}

当创建此类的新实例时,调用构造函数,在此示例中,该构造函数将属性设置为指定的值。请注意,任何初始属性值都是在运行构造函数之前设置的。

$r = new MyRectangle(); // "Constructed"

因为这个构造函数不带参数,所以可以选择省略括号。

$r = new MyRectangle; // "Constructed"

就像任何其他方法一样,构造函数可以有一个参数列表。它可用于将属性值设置为创建对象时传递的参数。

class MyRectangle
{
  public $x, $y;

  function __construct($x, $y)
  {
    $this->x = $x;
    $this->y = $y;
  }
}

$r = new MyRectangle(5,10);

PHP 8 引入了构造函数属性提升,允许在构造函数参数列表中声明属性。任何以访问级别(如 public)为前缀的构造函数参数都将提升为新属性。这使得前面的类可以用下面不太冗长的方式重写。

class MyRectangle
{
  function __construct(public $x, public $y)
  {
    $this->x = $x;
    $this->y = $y;
  }
}

默认参数可以以与其他函数和方法相同的方式用于构造函数。提升的和非提升的参数都可以有默认值。请记住,默认值被分配给参数,而不是提升的属性。提升的属性需要在构造函数中赋值,就像这里所做的一样。

class MyRectangle
{
  public $x;
  function __construct($x = 5, public $y = 10)
  {
    $this->x = $x;
    $this->y = $y;
  }

}
$r = new MyRectangle;
echo $r->x + $r->y; // "15"

破坏者

除了构造函数,类也可以有析构函数。这个魔术方法以两个下划线开始,后跟单词destruct。一旦不再有对该对象的引用,就在 PHP 垃圾收集器销毁该对象之前调用它。

class MyRectangle
{
  // ...
  function __destruct() { echo "Destructed"; }
}

为了测试析构函数,可以使用unset函数来手动移除对象的所有引用。

$r = new MyRectangle;
unset($r); // "Destructed"

请记住,PHP 5 完全重写了对象模型。因此,类的许多特性,如析构函数,在早期版本的语言中不起作用。

区分大小写

变量名区分大小写,而 PHP 中的类名不区分大小写——函数名、关键字和内置构造(如echo)也是如此。这意味着名为MyClass的类也可以被引用为myclassMYCLASS

class MyClass {}
$o1 = new myclass(); // ok
$o2 = new MYCLASS(); // ok

对象比较

当对对象使用等于运算符(==)时,如果对象是同一类的实例,并且它们的属性具有相同的值和类型,则这些对象被认为是相等的。相反,只有当变量引用同一个类的同一个实例时,严格等于运算符(===)才返回true

class Flag
{
  public $flag = true;
}

$a = new Flag();
$b = new Flag();

$c = ($a == $b);  // true (same values)
$d = ($a === $b); // false (different instances)

匿名类

PHP 7 引入了对匿名类的支持。当只需要一个一次性的对象时,这样的类可以代替命名类。

$obj = new class {};

匿名类的实现以及由此创建的对象与命名类没有什么不同。例如,他们可以像使用任何命名类一样使用构造函数。

$o = new class('Hi')
{
  public $x;
  public function __construct($a)
  {
    $this->x = $a;
  }
};

echo $o->x; // "Hi";

闭包对象

PHP 中的匿名函数也是闭包,因为它们能够从函数范围之外捕获上下文。除了变量,这个上下文也可以是一个对象的范围。这创建了一个所谓的闭包对象,它可以访问该对象的属性。使用bindTo方法创建一个对象闭包。该方法接受两个参数:闭包所绑定到的对象和与之关联的类范围。若要访问非公共成员(私有成员或受保护成员),必须将类或对象的名称指定为第二个参数。

class C { private $x = 'Hi'; }

$getC = function() { return $this->x; };
$getX = $getC->bindTo(new C, 'C');
echo $getX(); // "Hi"

这个例子使用了两个闭包。第一个闭包$getC定义了检索属性的方法。第二个闭包$getX$getC的副本,对象和类范围已经绑定到它。PHP 7 简化了这一点,它提供了一种更简洁、性能更好的临时绑定方法,然后在同一个操作中调用一个闭包。

// PHP 7
$getX = function() { return $this->x; };
echo $getX->call(new C); // "Hi"

十、继承

继承允许一个类获取另一个类的成员。在下面的例子中,Square类继承自Rectangle,由extends关键字指定。Rectangle然后成为Square的父类,后者又成为Rectangle的子类。除了自己的成员,Square还获得了Rectangle中所有可访问(非私有)的成员,包括任何构造函数。

// Parent class (base class)
class Rectangle
{
  public $x, $y;
  function __construct($a, $b)
  {
    $this->x = $a;
    $this->y = $b;
  }
}

// Child class (derived class)
class Square extends Rectangle {}

当创建Square的实例时,现在必须指定两个参数,因为Square已经继承了Rectangle的构造函数。

$s = new Square(5,10);

Rectangle继承的属性也可以从Square对象中访问。

$s->x = 5;
$s->y = 10;

PHP 中的类只能从一个父类继承,并且在脚本文件中父类必须在子类之前定义。

重写成员

子类中的成员可以重新定义其父类中的成员,从而为其提供新的实现。要覆盖继承的成员,只需要用相同的名称重新声明它。如下所示,Square构造函数覆盖了Rectangle中的构造函数。

class Square extends Rectangle
{
  function __construct($a)
  {
    $this->x = $a;
    $this->y = $a;
  }
}

使用这个新的构造函数,只使用一个参数来创建Square

$s = new Square(5);

因为Rectangle的继承构造函数被覆盖,所以在创建Square对象时不再调用Rectangle的构造函数。如有必要,由开发人员调用父构造函数。这是通过在调用前面加上parent关键字和一个双冒号来实现的。双冒号被称为范围解析操作符 ( ::)。

class Square extends Rectangle
{
  function __construct($a)
  {
    parent::__construct($a,$a);
  }
}

parent关键字是父类名的别名,也可以使用。在 PHP 中,使用这种表示法可以访问继承层次结构中任意级别的被覆盖成员。

class Square extends Rectangle
{
  function __construct($a)
  {
    Rectangle::__construct($a,$a);
  }
}

像构造函数一样,如果父析构函数被重写,它不会被隐式调用。它也必须从子析构函数中用parent::__destruct()显式调用。

最终关键字

为了阻止子类重写方法,可以将其定义为final。类本身也可以定义为 final,以防止任何类扩展它。

final class NotExtendable
{
  final function notOverridable() {}
}

运算符的实例

作为一项安全预防措施,您可以通过使用instanceof操作符来测试一个对象是否可以被转换为一个特定的类。如果左边的对象可以被投射到右边的type而不会导致错误,那么这个操作符返回true。当对象是右侧类的实例或从右侧类继承时,情况确实如此。

$s = new Square(5);
$s instanceof Square; // true
$s instanceof Rectangle; // true