PHP基础太薄弱了。最近几天做个补完,记点笔记。
PHP基础
PHP 脚本可以放在文档中的任何位置,以<?php
开始,以 ?>
结束,每个代码行都必须以分号结束。
//
是单行注释,/**/
是多行注释。
PHP变量
变量声明
变量以 $ 开始,大小写敏感。没有声明变量的命令,在声明变量在首次给变量赋值的同时完成。
作用域
变量的作用域是脚本中变量可被引用/使用的部分。
在所有函数外部定义的变量,拥有全局作用域。除了函数外,全局变量可以被脚本中的任何部分访问,要在一个函数中访问一个全局变量,需要使用 global 关键字。在 PHP 函数内部声明的变量是局部变量,仅能在函数内部访问。
PHP中函数的花括号构成新的作用域。if和for的花括号并不构成新的作用域。
PHP 有四种不同的变量作用域:
- local:局部变量
- global:全局变量
- static:静态变量
- parameter:函数参数
global
global 关键字用于函数内访问全局变量。要在函数内定义global变量需要先写global再声明。实例:
1 |
|
PHP 将所有全局变量存储在一个名为 $GLOBALS[index] 的数组中。 index 保存变量的名称。这个数组可以在函数内部访问,也可以直接用来更新全局变量。
上面的实例可以写成这样:
1 |
|
PHP的执行是以一个.php脚本为单位,在一个.php脚本的执行过程中,可以include和require其他PHP脚本进来执行。执行的.php脚本与include/require进来的脚本共享一个全局域(global scope)。global关键字无论在哪层,所引用的都是全局域的变量。
Static
每次调用该函数时,该变量将会保留着函数前一次被调用时的值。该变量仍然是函数的局部变量,但不会像普通的局部变量一样因为函数完成而被删除。
Parameter
参数是通过调用代码将值传递给函数的局部变量。参数是在参数列表中声明的,作为函数声明的一部分。实例:
1 |
|
预定义变量
PHP 提供了大量的预定义变量。由于许多变量依赖于运行的服务器的版本和设置,及其它因素,所以并没有详细的说明文档。一些预定义变量在 PHP 以命令行形式运行时并不生效。有关这些变量的详细列表,请参阅预定义变量一章。
如果有可用的 PHP 预定义变量那最好用,如超全局数组。
超级全局变量不能被用作函数或类方法中的可变变量。
- 超全局变量 — 超全局变量是在全部作用域中始终可用的内置变量
- $GLOBALS — 引用全局作用域中可用的全部变量
- $_SERVER — 服务器和执行环境信息
- $_GET — HTTP GET 变量
- $_POST — HTTP POST 变量
- $_FILES — HTTP 文件上传变量
- $_REQUEST — HTTP Request 变量
- $_SESSION — Session 变量
- $_ENV — 环境变量
- $_COOKIE — HTTP Cookies
- $php_errormsg — 前一个错误信息
- $HTTP_RAW_POST_DATA — 原生POST数据
- $http_response_header — HTTP 响应头
- $argc — 传递给脚本的参数数目
- $argv — 传递给脚本的参数数组
可变变量
在 PHP 的函数和类的方法中,超全局变量不能用作可变变量。$this 变量也是一个特殊变量,不能被动态引用。
要将可变变量用于数组,必须解决一个模棱两可的问题。这就是当写下 \[a[1] 时,解析器需要知道是想要 $a[1] 作为一个变量呢,还是想要 \]a 作为一个变量并取出该变量中索引为 [1] 的值。解决此问题的语法是,对第一种情况用 \({\)a[1]},对第二种情况用 \({\)a}[1]。
PHP之外的变量
通常,PHP 不会改变传递给脚本中的变量名。然而应该注意到点(句号)不是 PHP 变量名中的合法字符。至于原因,看看:
1 |
|
这时,解析器看到是一个名为 $varname 的变量,后面跟着一个字符串连接运算符,后面跟着一个裸字符串(即没有加引号的字符串,且不匹配任何已知的健名或保留字)'ext'。很明显这不是想要的结果。
出于此原因,要注意 PHP 将会自动将外部变量名中的点替换成下划线。
因为 PHP 会判断变量类型并在需要时进行转换(通常情况下),因此在某一时刻给定的变量是何种类型并不明显。PHP 包括几个函数可以判断变量的类型,例如:gettype(),is_array(),is_float(),is_int(),is_object() 和 is_string()。参见类型一章。
输出语句
echo和print
- echo - 可以输出一个或多个字符串
- print - 只允许输出一个字符串,返回值总为 1
提示:echo 输出的速度比 print 快, echo 没有返回值,print有返回值1。
echo
使用的时候可以不用加括号,也可以加上括号: echo 或 echo()。输出的字符串可以带有html标签,多个串用逗号隔开。
实例:
1 |
|
可以使用括号,也可以不使用括号: print 或 print()。使用与echo基本相同。
数据类型
String
放在单引号双引号中均可。
单引号除了单引号本身和反斜线以外其它均不转义。
当字符串用双引号或 heredoc 结构定义时,其中的变量将会被解析。
PHP 的字符串在内部是字节组成的数组。因此用花括号访问或修改字符串对多字节字符集很不安全。
heredoc
<<<xxx。
要注意的是结束标识符这行除了可能有一个分号(;)外,绝对不能包含其它字符。这意味着标识符不能缩进,分号的前后也不能有任何空白或制表符。更重要的是结束标识符的前面必须是个被本地操作系统认可的换行,比如在 UNIX 和 Mac OS X 系统中是 *,而结束定界符(可能其后有个分号)之后也必须紧跟一个换行。
1 |
|
nowdoc
<<<'xxx'。
与heredoc结构相似,但不对特殊字符转义。(>php5.3.0)
变量解析
简单语法
遇
$
尝试解析。复杂语法
花括号括起。里面可以写复杂的表达式。只有 $ 紧挨着 { 时才会被识别。
存取和修改字符
用方括号或花括号访问。
Int
不能包含逗号和空格,无小数点,可正可负,可以是十进制, 十六进制( 以 0x 为前缀)或八进制(前缀为 0)。
Float
带小数的或是指数形式的。例如: 1.2,2.4e3,8E-5。
Bool
true或false。
Array
有序映射。映射是一种把 values 关联到 keys 的类型。由于数组元素的值也可以是另一个数组,树形结构和多维数组也是允许的。
定义数组
可以用 array() 语言结构来新建一个数组。它接受任意数量用逗号分隔的 键(key) => 值(value)对。
自 5.4 起可以使用短数组定义语法,用 [] 替代 array()。
1 |
|
key 可以是 integer 或者 string。value 可以是任意类型。
- 包含有合法整型值的字符串会被转换为整型。例如键名 "8" 实际会被储存为 8。但是 "08" 则不会强制转换,因为其不是一个合法的十进制数值。
- 浮点数也会被转换为整型,意味着其小数部分会被舍去。例如键名 8.7 实际会被储存为 8。
- 布尔值也会被转换成整型。即键名 true 实际会被储存为 1 而键名 false 会被储存为 0。
- Null 会被转换为空字符串,即键名 null 实际会被储存为 ""。
- 数组和对象不能被用为键名。坚持这么做会导致警告:Illegal offset type。
如果在数组定义中多个单元都使用了同一个键名,则只使用了最后一个,之前的都被覆盖了。
PHP 数组可以同时含有 integer 和 string 类型的键名,因为 PHP 实际并不区分索引数组和关联数组。
如果未指定key,PHP 将自动使用之前用过的最大 integer 键名加上 1 作为新的键名,之前没用过integer键名的取key值为0(注意这里所使用的最大整数键名不一定当前就在数组中。它只要在上次数组重新生成索引后曾经存在过就行了。):
1 |
|
还可以只对某些单元指定键名而对其它的空置:
1 |
|
访问数组单元
1 |
|
Note:
方括号和花括号可以互换使用来访问数组单元(例如 $array[42] 和 $array{42} 在上例中效果相同)。
新建、修改、删除值
要修改某个值,通过其键名给该单元赋一个新值。要删除某键值对,对其调用 unset() 函数。
1 |
|
数组解引用
自 PHP 5.4 起可以用直接对函数或方法调用的结果进行数组解引用,在此之前只能通过一个临时变量。
自 PHP 5.5 起可以直接对一个数组原型进行数组解引用。
1 |
|
数组实用函数
unset() 函数允许删除数组中的某个键。但要注意数组将不会重建索引。如果需要删除后重建索引,可以用 array_values() 函数。
1 |
|
此外,foreach函数专门用于数组。(简单遍历)
注意
应该始终在用字符串表示的数组索引上加上引号。而键名为常量和变量的不需要加引号。
可用函数返回值做数组索引。也可用已知常量。
转换为数组
对于任意 integer,float,string,boolean 和 resource 类型,如果将一个值转换为数组,将得到一个仅有一个元素的数组,其下标为 0,该元素即为此标量的值。换句话说,(array)\(scalarValue* 与 *array(\)scalarValue) 完全一样。
如果一个 object 类型转换为 array,则结果为一个数组,其单元为该对象的属性。键名将为成员变量名,不过有几点例外:整数属性不可访问;私有变量前会加上类名作前缀;保护变量前会加上一个 '*' 做前缀。这些前缀的前后都各有一个 NULL 字符。这会导致一些不可预知的行为:
1 |
|
比较
array_diff(),数组运算符。
Object
初始化
new 语句。
转换为对象
如果其它任何类型的值被转换成对象,将会创建一个内置类 stdClass 的实例。如果该值为 NULL
,则新的实例为空。 array 转换成 object 将使键名成为属性名并具有相对应的值。注意:在这个例子里, 使用 PHP 7.2.0 之前的版本,数字键只能通过迭代访问。对于其他值,会包含进成员变量名 scalar。
1 |
|
Resource
get_resource_type
返回资源类型。例如:
1 |
|
Callback/Callable
自 PHP 5.4 起可用 callable 类型指定回调类型 callback。
NULL
NULL 值表示变量没有值。NULL 是数据类型为 NULL 的值。
伪类型与变量
mixed
一个参数可以接受多种不同的(但不一定是所有的)类型。
number
callback
在 PHP 5.4 引入 callable 类型之前使用 了 callback 伪类型。二者含义完全相同。
array|object
void
void 作为返回类型意味着函数的返回值是无用的。void 作为参数列表意味着函数不接受任何参数。
...
在函数原型中,$...
表示等等的意思。当一个函数可以接受任意个参数时使用此变量名。
类型转换
类型设置
settype设置变量类型。
settype ( mixed &$var
, string $type
) : bool
var是待转换变量。type是要转换的类型。返回值是成功与否。
实例:
1 |
|
类型强制转换
在要转换的变量之前加上用括号括起来的目标类型。
允许的强制转换有:
- (int), (integer) - 转换为整形 integer
- (bool), (boolean) - 转换为布尔类型 boolean
- (float), (double), (real) - 转换为浮点型 float
- (string) - 转换为字符串 string
- (array) - 转换为数组 array
- (object) - 转换为对象 object
- (unset) - 转换为 NULL (PHP 5)
- (binary) 转换和 b 前缀转换支持为 PHP 5.2.1 新增。转换为二进制字符串。
Note: 可以将变量放置在双引号中的方式来代替将变量转换成字符串。
运算符
运算符优先级
结合方向 | 运算符 | 附加信息 |
---|---|---|
无 | clone new | clone 和 new |
右 | **** | 算术运算符 |
右 | ++ -- ~ (int) (float) (string) (array) (object) (bool) *@* | 类型、递增/递减、错误控制 |
无 | instanceof | 类型 |
右 | ! | 逻辑运算符 |
左 | *** / % | 算术运算符 |
左 | + - . | 算术运算符 和 字符串运算符 |
左 | << >> | 位运算符 |
无 | < <= > >= | 比较运算符 |
无 | == != === !== <> <=> | 比较运算符 |
左 | & | 位运算符 和 引用 |
左 | ^ | 位运算符 |
左 | | | 位运算符 |
左 | && | 逻辑运算符 |
左 | || | 逻辑运算符 |
右 | ?? | null 合并运算符 |
左 | ? : | 三元运算符 |
右 | = += -= =* = /= .= %= &= |= ^= <<= >>= | 赋值运算符 |
右 | yield from | yield from |
右 | yield | yield |
左 | and | 逻辑运算符 |
左 | xor | 逻辑运算符 |
左 | or | 逻辑运算符 |
比较运算
注意 ==
和 ===
的区别。
特别注意的是,$a <> $b 与 $a != \(b 表示相同的意思。\)a <=> \(b是结合比较运算符,当\)a小于、等于、大于$b时分别返回一个小于、等于、大于0的integer 值。 PHP7开始提供。
多类型比较(按顺序)
运算数 1 类型 | 运算数 2 类型 | 结果 |
---|---|---|
null 或 string | string | 将 NULL 转换为 "",进行数字或词汇比较 |
bool 或 null | 任何其它类型 | 转换为 bool,FALSE < TRUE |
object | object | 内置类可以定义自己的比较,不同类不能比较,相同类和数组同样方式比较属性(PHP 4 中),PHP 5 有其自己的说明 |
string,resource 或 number | string,resource 或 number | 将字符串和资源转换成数字,按普通数学比较 |
array | array | 具有较少成员的数组较小,如果运算数 1 中的键不存在于运算数 2 中则数组无法比较,否则挨个值比较(见下例) |
object | 任何其它类型 | object 总是更大 |
array | 任何其它类型 | array 总是更大 |
注意!由于由于浮点数 float 的内部表达方式,不应比较两个浮点数float是否相等。更多信息参见 float。
三目运算
表达式 (expr1) ? (expr2) : (expr3) 在 expr1 求值为 TRUE
时的值为 expr2,在 expr1 求值为 FALSE
时的值为 expr3。
自 PHP 5.3 起,可以省略三元运算符中间那部分。表达式 expr1 ?: expr3 在 expr1 求值为 TRUE
时返回 expr1,否则返回 expr3。
注意三元运算符是个语句,因此其求值不是变量,而是语句的结果。在一个通过引用返回的函数中语句 return $var == 42 ? $a : $b; 将不起作用,以后的 PHP 版本会为此发出一条警告。
NULL合并运算
当 expr1 为 NULL
,表达式 (expr1) ?? (expr2) 等同于 expr2,否则为 expr1。
尤其要注意,当不存在左侧的值时,此运算符也和 isset() 一样不会产生警告。 对于 array 键尤其有用。
1 |
|
注意NULL 合并运算符是一个表达式,产生的也是表达式结果,而不是变量。在返回引用的函数里就无法使用这样的语句:return $foo ?? $bar;,还会提示警告。
NULL 合并运算符支持简单的嵌套:
1 |
|
算数运算
除了加减乘除取模外还有:取反:-$x
,并置:$a.$b
。
取反是取相反数,并置是连接两个字符串。
取反会先将待取反的变量转换为数值的形式。
赋值运算
没什么好说的。注意 $a .= $b 和 $a = $a . $b 表示的意思相同。
递增递减
x++, ++x, x--, --x。
逻辑运算
and与 / or或 /xor异或 / &&与 / ||或 / !非。
位运算
例子 | 名称 | 结果 |
---|---|---|
$a & $b |
And(按位与) | 将把 $a 和 \(b 中都为 1 的位设为 1。 | | **`\)a |
位移在 PHP 中是数学运算。向任何方向(左右)移出去的位都被丢弃。左移时右侧以零填充,符号位被移走意味着正负号不被保留。右移时左侧以符号位填充,意味着正负号被保留。
若&,/,^左右是字符串,则对ASCII做运算以后得到字符串结果,对于其它类型全部转成Integer,得到Integer结果。~操作同理。<<和>>左右两边都被当作 Integer 处理,String也不会转成ASCII。和>
PHP 的 ini 设定 error_reporting 使用了按位的值,提供了关闭某个位的实例。
1 | E_ALL ^ E_NOTICE |
错误控制运算
@符号。当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都被忽略掉。
如果用 set_error_handler() 设定了自定义的错误处理函数,仍然会被调用,但是此错误处理函数可以(并且也应该)调用 error_reporting(),而该函数在出错语句前有 @ 时将返回 0。
如果激活了 track_errors 特性,表达式所产生的任何错误信息都被存放在变量 $php_errormsg 中。此变量在每次出错时都会被覆盖,所以如果想用它的话就要尽早检查。
执行运算
反引号(``)。PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回(即,可以赋给一个变量而不是简单地丢弃到标准输出)。使用反引号运算符的效果与函数 shell_exec() 相同。
注:exec命令,如果指定了command,它就会取代当前的shell而不是创建新的进程。
1 |
|
反引号运算符在激活了安全模式或者关闭了 shell_exec() 时是无效的。
与其它某些语言不同,反引号不能在双引号字符串中使用。但是,由变量搭载则可以使用,如上例。
数组运算
例子 | 名称 | 结果 |
---|---|---|
$a + $b | 联合 | $a 和 $b 的联合。 |
$a == $b | 相等 | 如果 $a 和 $b 具有相同的键/值对则为 TRUE 。 |
$a === $b | 全等 | 如果 $a 和 $b 具有相同的键/值对并且顺序和类型都相同则为 TRUE 。 |
$a != $b | 不等 | 如果 $a 不等于 $b 则为 TRUE 。 |
$a <> $b | 不等 | 如果 $a 不等于 $b 则为 TRUE 。 |
$a !== $b | 不全等 | 如果 $a 不全等于 $b 则为 TRUE 。 |
+ 运算符把右边的数组元素附加到左边的数组后面,两个数组中都有的键名,则只用左边数组中的,右边的被忽略。
类型运算
instanceof 用于确定一个 PHP 变量是否属于某一类的实例:
1 |
|
也可以用来确定一个变量是不是继承自某一父类的子类的实例:
1 |
|
可以用 !
检查对象不是某个类的实例。
也可用于确定一个变量是不是实现了某个接口的对象的实例。
虽然 instanceof 通常直接与类名一起使用,但也可以使用对象或字符串变量。
instanceof 的使用还有一些陷阱必须了解。在 PHP 5.1.0 之前,如果要检查的类名称不存在,instanceof 会调用 __autoload()。另外,如果该类没有被装载则会产生一个致命错误。可以通过使用动态类引用或用一个包含类名的字符串变量来避开这种问题。
instanceof 运算符是 PHP 5 引进的。在此之前用 is_a(),但是后来 is_a() 被废弃而用 instanceof 替代了。注意自 PHP 5.3.0 起,又恢复使用 is_a() 了。参见 get_class() 和 is_a()。
流程控制
不必多说的
if,else,while, do while, for
elseif/else if
必须要注意的是 elseif 与 else if 只有在使用花括号的情况下才认为是完全相同。如果用冒号来定义 if/elseif 条件,那就不能用两个单词的 else if,否则 PHP 会产生解析错误。
1 |
|
流程控制的替代语法
PHP 提供了一些流程控制的替代语法,包括 if,while,for,foreach 和 switch。替代语法的基本形式是把左花括号({)换成冒号(:),把右花括号(})分别换成 endif;
,endwhile;
,endfor;
,endforeach;
以及 endswitch;
。
1 | if ($a == 5): |
在上面的例子中,HTML 内容“A is equal to 5”用替代语法嵌套在 if 语句中。该 HTML 的内容仅在 $a 等于 5 时显示。
不支持在同一个控制块内混合使用两种语法。
switch 和第一个 case 之间的任何输出(含空格)将导致语法错误。例如,这样是无效的:
1 | switch ($foo): |
而这样是有效的,因为 switch 之后的换行符被认为是结束标记 ?> 的一部分,所以在 switch 和 case 之间不能有任何输出:
1 | switch ($foo): |
foreach
foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。有两种语法:
1 | foreach (array_expression as $value) |
第一种格式遍历给定的 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。
第二种格式做同样的事,只除了当前单元的键名也会在每次循环中被赋给变量 $key。
当 foreach 开始执行时,数组内部的指针会自动指向第一个单元。这意味着不需要在 foreach 循环之前调用 reset()。
可以很容易地通过在 $value 之前加上 & 来修改数组的元素。此方法将以引用赋值而不是拷贝一个值。
1 |
|
$value 的引用仅在被遍历的数组可以被引用时才可用(例如是个变量)。以下代码则无法运行:
1 |
|
foreach* 不支持用“@”来抑制错误信息的能力。
以下代码功能完全相同:
1 |
|
1 |
|
用 list() 给嵌套的数组解包¶
(PHP 5 >= 5.5.0, PHP 7)
PHP 5.5 增添了遍历一个数组的数组的功能并且把嵌套的数组解包到循环变量中,只需将 list() 作为值提供。
例如:
1 |
|
break/continue
PHP中 break/continue 可以接受一个可选的数字参数来决定跳出几重循环。
*break* 的更新记录
版本 | 说明 |
---|---|
5.4.0 | break 0; 不再合法。这在之前的版本被解析为 break 1;。 |
5.4.0 | 取消变量作为参数传递(例如 $num = 2; break $num;)。 |
continue同break。
switch
注意和其它语言不同,continue 语句作用到 switch 上的作用类似于 break。如果在循环中有一个 switch 并希望 continue 到外层循环中的下一轮循环,用 continue 2。
注意 switch/case 作的是松散比较。
switch 语句一行接一行地执行(实际上是语句接语句)。开始时没有代码被执行。仅当一个 case 语句中的值和 switch 表达式的值匹配时 PHP 才开始执行语句,直到 switch 的程序段结束或者遇到第一个 break 语句为止。如果不在 case 的语句段最后写上 break 的话,PHP 将继续执行下一个 case 中的语句段。
在一个 case 中的语句也可以为空,这样只不过将控制转移到了下一个 case 中的语句。
1 |
|
一个 case 的特例是 default。它匹配了任何和其它 case 都不匹配的情况。例如:
1 |
|
允许使用分号代替 case 语句后的冒号。
declare
declare 结构用来设定一段代码的执行指令。declare 的语法和其它流程控制结构相似:
1 | declare (directive) |
directive 部分允许设定 declare 代码段的行为。目前只认识两个指令:ticks(更多信息见下面 ticks 指令)以及 encoding(更多信息见下面 encoding 指令)。
Note: encoding 是 PHP 5.3.0 新增指令。
declare 结构也可用于全局范围,影响到其后的所有代码(但如果有 declare 结构的文件被其它文件包含,则对包含它的父文件不起作用)。
1 |
|
Tick
Tick(时钟周期)是一个在 declare 代码段中解释器每执行 N 条可计时的低级语句就会发生的事件。N 的值是在 declare 中的 directive 部分用 ticks=N
来指定的。
不是所有语句都可计时。通常条件表达式和参数表达式都不可计时。
在每个 tick 中出现的事件是由 register_tick_function() 来指定的。更多细节见下面的例子。注意每个 tick 中可以出现多个事件。
1 |
|
Encoding
可以用 encoding 指令来对每段脚本指定其编码方式。
1 |
|
在 PHP 5.3 中除非在编译时指定了 --enable-zend-multibyte,否则 declare 中的 encoding 值会被忽略。注意除非用 phpinfo(),否则 PHP 不会显示出是否在编译时指定了 --enable-zend-multibyte。参见 zend.script_encoding。
return
如果在一个函数中调用 return 语句,将立即结束此函数的执行并将它的参数作为函数的值返回。return 也会终止 eval() 语句或者脚本文件的执行。
如果在全局范围中调用,则当前脚本文件中止运行。如果当前脚本文件是被 include 的或者 require 的,则控制交回调用文件。此外,如果当前脚本是被 include 的,则 return 的值会被当作 include 调用的返回值。如果在主脚本文件中调用 return,则脚本中止运行。如果当前脚本文件是在 php.ini 中的配置选项 auto_prepend_file 或者 auto_append_file 所指定的,则此脚本文件中止运行。
Note: 当用引用返回值时永远不要使用括号,这样行不通。只能通过引用返回变量,而不是语句的结果。如果使用 return (\(a);* 时其实**不是返回一个变量,而是表达式 *(\)a) 的值**(当然,此时该值也正是 $a 的值)。
include/require
包含文件用。区别是include遇错产警告,require遇错终止。
如果没有给出目录(只有文件名)时则按照 include_path 指定的目录寻找。如果在 include_path 下没找到该文件则 include 最后才在调用脚本文件所在的目录和当前工作目录下寻找。如果定义了路径(绝对和相对)include_path 会被完全忽略。
当一个文件被包含时,其中所包含的代码继承了 include 所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用。不过所有在包含文件中定义的函数和类都具有全局作用域(?)。
如果 include 出现于调用文件中的一个函数里,则被调用的文件中所包含的所有代码将表现得如同它们是在该函数内部定义的一样。所以它将遵循该函数的变量范围。此规则的一个例外是魔术常量,它们是在发生包含之前就已被解析器处理的。
1 |
|
当一个文件被包含时,语法解析器在目标文件的开头脱离 PHP 模式并进入 HTML 模式,到文件结尾处恢复。由于此原因,目标文件中需要作为 PHP 代码执行的任何代码都必须被包括在有效的 PHP 起始和结束标记之中。(文件中的html内容会被直接显示出来。)
如果“URL include wrappers”在 PHP 中被激活,可以用 URL(通过 HTTP 或者其它支持的封装协议——见支持的协议和封装协议)而不是本地文件来指定要被包含的文件(远程包含)。如果目标服务器将目标文件作为 PHP 代码解释,则可以用适用于 HTTP GET 的 URL 请求字符串来向被包括的文件传递变量。严格的说这和包含一个文件并继承父文件的变量空间并不是一回事;该脚本文件实际上已经在远程服务器上运行了,而本地脚本则包括了其结果。
1 |
|
因为 include 是一个特殊的语言结构,其参数不需要括号。在比较其返回值时要注意。
1 |
|
include和return:
1 | return.php |
如果在包含文件中定义有函数,这些函数不管是在 return 之前还是之后定义的,都可以独立在主文件中使用。如果文件被包含两次,PHP 5 发出致命错误因为函数已经被定义,但是 PHP 4 不会对在 return 之后定义的函数报错。推荐使用 include_once 而不是检查文件是否已包含并在包含文件中有条件返回。
将 PHP 文件“包含”到一个变量中的方法是用输出控制函数结合 include 来捕获其输出,例如:
1 |
|
要在脚本中自动包含文件,参见 php.ini 中的 auto_prepend_file 和 auto_append_file 配置选项。
Note: 因为是一个语言构造器而不是一个函数,不能被 可变函数 调用。
include_once/require_once
只包含一次。
Note:
在 PHP 4中,_once 的行为在不区分大小写字母的操作系统(例如 Windows)中有所不同,例如:
Example #1 include_once 在 PHP 4 运行于不区分大小写的操作系统中
1
2
3
4
include_once "a.php"; // 这将包含 a.php
include_once "A.php"; // 这将再次包含 a.php!(仅 PHP 4)此行为在 PHP 5 中改了,例如在 Windows 中路径先被规格化,因此 C:~1.php 和 C:Files.php 的实现一样,文件只会被包含一次。
goto
PHP 中的 goto 有一定限制,目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中。可以跳出循环或者 switch,通常的用法是用 goto 代替多层的 break。
1 |
|
Note:
goto 操作符仅在 PHP 5.3及以上版本有效。
函数
自定义函数
任何有效的 PHP 代码都有可能出现在函数内部,甚至包括其它函数和类定义。
函数无需在调用之前被定义,除非是下面两个例子中函数是有条件被定义时。当一个函数是有条件被定义时,必须在调用函数之前定义。
1 |
|
递归函数
1 |
|
Note: 但是要避免递归函数/方法调用超过 100-200 层,因为可能会使堆栈崩溃从而使当前脚本终止。 无限递归可视为编程错误。
函数参数
注意当使用默认参数时,任何默认参数必须放在任何非默认参数的右侧;否则,函数将不会按照预期的情况工作。考虑下面的代码片断:
函数默认参数的不正确用法
1 |
|
以上例程会输出:
1 | Warning: Missing argument 2 in call to makeyogurt() in |
正确用法:
1 |
|
参数类型
type | Description | Minimum PHP version |
---|---|---|
Class/interface name | The parameter must be an instanceof the given class or interface name. | PHP 5.0.0 |
self | The parameter must be an instanceof the same class as the one the method is defined on. This can only be used on class and instance methods. | PHP 5.0.0 |
array | The parameter must be an array. | PHP 5.1.0 |
callable | The parameter must be a valid callable. | PHP 5.4.0 |
bool | The parameter must be a boolean value. | PHP 7.0.0 |
float | The parameter must be a floating point number. | PHP 7.0.0 |
int | The parameter must be an integer. | PHP 7.0.0 |
string | The parameter must be a string. | PHP 7.0.0 |
iterable | The parameter must be either an array or an instanceof Traversable. | PHP 7.1.0 |
object | The parameter must be an object. | PHP 7.2.0 |
Nullable type declaration
1 |
|
严格类型
默认情况下,如果能做到的话,PHP将会强迫错误类型的值转为函数期望的标量类型。 例如,一个函数的一个参数期望是string,但传入的是integer,最终函数得到的将会是一个string类型的值。
可以基于每一个文件开启严格模式。在严格模式中,只有一个与类型声明完全相符的变量才会被接受,否则将会抛出一个TypeError。 唯一的一个例外是可以将integer传给一个期望float的函数。
实例:
1 |
|
可变数量的参数列表
PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 ... 语法实现;在 PHP 5.5 及更早版本中,使用函数 func_num_args(),func_get_arg(),和 func_get_args() 。
... in PHP 5.6+,例如:
1 |
|
也可以用...把list解压到参数列表中:
1 |
|
在老版本(低于5.5)时,不需要特殊语法标注可变,但是需要使用函数,例如: func_num_args(), func_get_arg() 和 func_get_args().
实例:
1 |
|
返回值
函数不能返回多个值,但可以通过返回一个数组来得到类似的效果。
从函数返回一个引用,必须在函数声明和指派返回值给一个变量时都使用引用运算符 &。例如:
1 |
|
PHP 7 增加了对返回值类型声明的支持。在默认的弱模式中,如果返回值与返回值的类型不一致,则会被强制转换为返回值声明的类型。在强模式中,返回值的类型必须正确,否则将会抛出一个TypeError异常。
1 |
|
返回对象:
1 |
|
返回null(PHP 7.1.0+):
1 |
|
可变函数
一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。
实例:
1 |
|
当调用静态方法时,函数调用要比静态属性优先。
调用数组和类中的方法,例:
1 |
|
版本 | 说明 |
---|---|
7.0.0 | 'ClassName::methodName' is allowed as variable function. |
5.4.0 | Arrays, which are valid callables, are allowed as variable functions. |
内置函数
PHP手册中每一个函数的页面中都有关于函数参数、行为改变、成功与否的返回值以及使用条件等信息。了解这些重要的(常常是细微的)差别是编写正确的 PHP 代码的关键。
有时需要加载模块(和扩展模块一起编译)以使用模块中的函数。
匿名函数
也叫闭包函数。允许临时创建一个没有指定名称的函数。
Arrow函数
PHP 7.4之后新增的使匿名函数语法更简洁的函数。
类与对象
基本概念
简单的类定义
1 |
|
当一个方法在类定义内部被调用时,有一个可用的伪变量 \(this。\)this 是一个到主叫对象的引用(通常是该方法所从属的对象,但如果是从第二个对象静态调用时也可能是另一个对象)。
示例:
1 |
|
Output of the above example in PHP 5:
1 | $this is defined (A) |
Output of the above example in PHP 7:
1 | $this is defined (A) |
new
1 |
|
在类定义内部,可以用 new self 和 new parent 创建新对象。
当把一个对象已经创建的实例赋给一个新变量时,新变量会访问同一个实例,就和用该对象赋值一样。此行为和给函数传递入实例时一样。可以用克隆给一个已创建的对象建立一个新实例。
对象赋值
1 |
|
以上例程会输出:
1 | NULL |
PHP 5.4.0 起,可以通过一个表达式来访问新创建对象的成员:
1 |
|
以上例程的输出类似于:
1 | 2016 |
属性
类的变量成员叫做“属性”。
属性声明是由关键字 public,protected 或者 private 开头,然后跟一个普通的变量声明来组成。属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值。
为了向后兼容 PHP 4,PHP 5 声明属性依然可以直接使用关键字 var 来替代(或者附加于)public,protected 或 private。但是已不再需要 var 了。如果直接使用 var 声明属性,而没有用 public,protected 或 private 之一,PHP 5 会将其视为 public。
在类的成员方法里面,可以用 ->(对象运算符):\(this->property(其中 *property* 是该属性名)这种方式来访问非静态属性。静态属性则是用 *::*(双冒号):self::\)property 来访问。更多静态属性与非静态属性的区别参见 Static 关键字。
实例:
1 |
|
类常量
可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。
常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。
实例:
1 |
|
类的自动加载
在编写面向对象(OOP) 程序时,很多开发者为每个类新建一个 PHP 文件。 这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件)。
在 PHP 5 中,已经不再需要这样了。 spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
自动加载不可用于 PHP 的 CLI 交互模式。
实例:
1 |
|
构造函数和析构函数
__construct()
创建新对象时先调用此方法。初始化。
Note: 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 **parent::__construct()**。如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)。
__destruct()
析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 **parent::__destruct()**。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。
析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。
Note: 试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。
访问控制(可见性)
对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。被定义为公有的类成员可以在任何地方被访问。被定义为受保护的类成员则可以被其自身以及其子类和父类访问。被定义为私有的类成员则只能被其定义所在的类访问。
属性
类属性必须定义为公有,受保护,私有之一。如果用 var 定义,则被视为公有。
实例:
1 |
|
方法
类中的方法可以被定义为公有,私有或受保护。如果没有设置这些关键字,则该方法默认为公有。
实例:
1 |
|
继承
当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。
1 | class bar extends foo |
范围解析操作符 (::)
可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法。
实例:
在类定义外部使用:
1 |
|
self,parent 和 static 这三个特殊的关键字是用于在类定义的内部对其属性或方法进行访问的。
在类定义内部使用:
1 |
|
调用父类方法:
1 |
|
Static
声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。
由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。
静态属性不可以由对象通过 -> 操作符来访问。
抽象类
定义为抽象的类不能被实例化。如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。
重载
动态地创建类属性和方法。通过魔术方法(magic methods)来实现。
当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。
所有的重载方法都必须被声明为 public。
属性重载
在给不可访问属性赋值时,__set() 会被调用。
读取不可访问属性的值时,__get() 会被调用。
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
当对不可访问属性调用 unset() 时,__unset() 会被调用。
方法重载
在对象中调用一个不可访问方法时,__call() 会被调用。
在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
魔术方法
__sleep()和__wakeup()
序列化时先调用__sleep()方法。反序列话是wakeup方法。