[toc]
##基础 ###基本语法 ####PHP 标记
<?php ?>
<script language="php"> </script>
<? ?>
php已经不支持段标签 要修改php.ini short_open_tag = on<% %>
asp_tags = on
如果文件内容是纯 PHP 代码,最好在文件末尾删除 PHP 结束标记. 这可以避免在 PHP 结束标记之后万一意外加入了空格或者换行符,会导致 PHP 开始输出这些空白, 而脚本中此时并无输出的意图
####从 HTML 中分离 凡是在一对开始和结束标记之外的内容都会被 PHP 解析器忽略, 这使得 PHP 文件可以具备混合内容. 可以使 PHP 嵌入到 HTML 文档中去
<p>This is going to be ignored by PHP and displayed by the browser.</p>
<?php echo 'While this is going to be parsed.'; ?>
<p>This will also be ignored by PHP and displayed by the browser.</p>
这将如预期中的运行,因为当 PHP 解析器碰到?>
结束标记时就简单地将其后内容原样输出 (除非马上紧指令分隔符) 知道碰到一下个开始标记
<?php if ($expression == true): ?>
This will show if the expression is true.
<?php else: ?>
Otherwise this will show.
<?php endif; ?>
要输出打断文本时, 跳出 PHP 解析模式通常比将文本通过 echo 或 print 输出更有效率 自 PHP 5.4 起, 短格式的 echo 标记 <?= 总会被识别并且合法, 而不管 short_open_tag 的设置是什么
####指令分隔符 同 C 或 Perl 一样, PHP 需要在每个语句后用分号结束指令. 一段 PHP 代码中的结束标记隐含表示了一个分号; 在一个 PHP 代码段中的最后一行可以不用分号结束. 如果后面还有新行, 则代码段的结束标记包含了行结束
<?php
echo "This is a test";
?>
<?php echo "This is a test" ?>
<?php echo 'We omitted the last closing tag';
文件末尾的 PHP 代码段结束标记可以不要, 有些情况下当使用 include 或则 require 时省略掉会更好些, 这样不期望的空白符就不会出现文件末尾, 之后仍然可以输出响应标头. 在使用输出缓冲也很便利, 就不会看到由文件生成的不期望的空白符
####注释 PHP 支持 C, C++ 和 Unix Shell 风格 (Perl风格) 的注释
//
行注释#
行注释/**/
块注释 单行注释仅仅注释到行末或则当前的 PHP 代码块, 视乎那个首先出现. 这意味着在// ...?> 或则 # ... ?> 之后的 HTML 代码将被显示出来 ?>跳出了 PHP 模式并返回了 HTML 模式, // 或 # 并不能影响到这一点
/**/
注释在碰到第一个*/时结束. 确保不要嵌套注释块,试图嵌套注释块很容易出该错误
<?php
/*
echo "This is a test"; /* This comment will cause a problem */
*/
?>
###类型 ####简介 PHP 支持 8 种原始数据类型
四中标量类型
- boolean (布尔型)
- integer (整型)
- float (浮点型, 也称作double)
- string (字符串)
两种复合类型
- array (数组)
- object (对象)
最后两种特殊类型
- resource (资源)
- NULL (无类型)
可能会有 双精度 (double) 类型的参考. 实际上 double 和 float 是相同的, 由于一些历史的原因, 这两个名称同时存在
- 如果想查看某个表达式的值和类型, 用 var_dump() 函数
- 如果只是想得到一个易读懂的类型的表达式用于调试, 用 gettype() 函数. 要查看某个类型, 不要用 gettype() , 而用 is_type 函数
- 如果要将一个变量强制转换为某类型, 可以对其使用强制转换或者settype()函数
- 变量会根据当时的烈性在特定场合下会表现出不同的值
####Boolean 布尔类型 boolean 表达了真值, 可以为 TRUE 和 FALSE
要明确地讲一个值转换成 boolean , 用 (bool) 或则 (boolean) 来强制转换. 但是很多情况下不需要用强制转换, 因为当运算符, 函数或者流程控制结构需要一个 boolean 参数时, 该值会被自动转换
当转换为 boolean 时, 以下值被认为是 FALSE
- 布尔值 FALSE 本身
- 整型值 0 (零)
- 浮点型值 0.0 (零)
- 空字符串, 以及字符串 "0"
- 不包括任何元素的数组
- 不包括任何成员变量的对象 (仅 PHP 4.0 适用)
- 特殊类型 NULL (包含尚未赋值的变量)
- 从控标记生成的 SimpleXML 对象
所有其它值都被认为是 TRUE (包括任何资源)
Warning -1 和其它非零值 (不论正负) 一样, 被认为是 TRUE var_dump(0 == 'all'); // TRUE
####Integer 整型 整型值可以使用十进制, 十六进制, 八进制或二进制表示, 前面可以加上可选的符号 (- 或者 +)
二进制表达的 integer 自 PHP 5.4.0 起可用
要使用八进制表达, 数字前必须加上 0 (零). 要使用十六进制表达, 数字前必须加上 0X. 要使用二进制表达, 数字前必须加上 0b
整型数的字长和平台有关, 尽管通常最大值大约二十亿 (32位有符号) 64 位平台下的最大值通常是大约 9E18. PHP 不支持无符号整数. Integer 值的字长可以用常量 PHP_INT_SIZE 来表示, 自 PHP 4.4.0 和 PHP 5.0.5 后, 最大值可以用常量 PHP_INT_MAX 来表示
Warning 如果向八进制传递了一个非法数字 (即 8 或 9), 则后面其余数字会被忽略
整数溢出 如果给定的一个数超出了 integer 的范围, 将会被解释为 float. 同样如果执行的运算结果超出了 integer范围, 也会返回 float
转换类型 要明确地将一个值转换为 integer ,用 (int) 或者 (integer) 强制转换. 不过大多数情况下都不需要强制转换,因为当运算符, 函数或流程控制符需要一个 integer 参数时,会自动转换. 还可以用过函数 intval() 来将一个值转换成整型
==从布尔值转换==
FALSE 将产生出 0 (零) , TRUE 将产生出 1 (壹)
==从浮点型转换==
当从浮点数转换成整数时,将向下取证
决不要将未知的分数强制转换为 integer , 这样有时会导致不可预料的结果
==从字符串转换==
当一个字符串被当作一个数值来取值,
如果该字符串没有包含'.', 'e'或'E' 并且其数字值在整型的范围之内, 该字符串将被当成 integer 来取值. 其它情况下都被作为float来取值
该字符串的开始部分决定了它的值. 如果该字符串以合法的数值开始, 则使用该数值. 否则其值为 0 (零). 合法数值由可选的正负号, 后面跟着一个或多个数字 (可能有小数点) , 再跟着可选的指数部分. 指数部分由 'e' 或 'E' 后面跟着一个或多个数字构成.
<?php
$foo = 1 + "10.5"; // $foo is float (11.5)
$foo = 1 + "-1.3e3"; // $foo is float (-1299)
$foo = 1 + "bob-1.3e3"; // $foo is integer (1)
$foo = 1 + "bob3"; // $foo is integer (1)
$foo = 1 + "10 Small Pigs"; // $foo is integer (11)
$foo = 4 + "10.2 Little Piggies"; // $foo is float (14.2)
$foo = "10.0 pigs " + 1; // $foo is float (11)
$foo = "10.0 pigs " + 1.0; // $foo is float (11)
?>
==从其它类型转换==
Caution 没有定义从其他烈性转换为整型的行为. 不要依赖任何现有的行为, 因为它会未加通知地改变
####Float 浮点型 浮点型 (也叫浮点数 float, 双精度数 double 或实数 real)
浮点数的字长和平台相关, 尽管通常最大值是 1.8e308 并具有 14 位十进制数字的精度 (64 位 IEEE 格式)
比较浮点数 由于内部表达式的原因, 比较两个浮点数是否相等室友问题的. 不过还是有与会的方法比较浮点数值的
要测试浮点数是否相等, 要使用一个仅比该数值大一丁点的最小误差值. 该值也被称为机器极小值 (epsilon) 或最小单元取整数, 是计算中所能接受的最小的差别值
<?php
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;
if(abs($a-$b) < $epsilon) {
echo "true";
}
?>
浮点数会丢失精度,不推荐用浮点数来比较大小
Nan 某些数学运算会产生一个有常量 NAN 所代表的结果. 此结果代表着一个在浮点数运算中未定义或不可表述的值. 不应拿 NAN 去和其它值进行比较, 包括其自身, 应该用 is_nan() 来检查
####String 字符串 一个字符串 string 就是由一系列的字符组成, 其中每个字符等同于一个字节. 这意味着 PHP 只能支持 256 的字符集, 因此不支持 Unicode.
Note: string 最大可以达到 2GB PHP 中的 string 的实现方式是一个由字节组成的数组再加上一个整数指明缓冲区长度
一个字符串可以用4中方式表达
- 单引号
- 双引号
- heredoc 语法结构
- nowdoc 语法结构 (自 PHP 5.3.0 起)
单引号 定义一个字符串的最简单的方法是用单引号把它包围起来 (字符 ')
要表达一个单引号自身, 需在它的前面加个反斜杠 () 来转义. 要表达一个反斜线自身, 则用两个反斜线 (\) . 其他任何方式的反斜线都会被当成反斜线本身: 也就是说如果想使用其他转义序列 例如 \r 或者 \n, 并不代表任何特殊含义, 就单纯是这两个字符本身
Note: 不像双引号和 heredoc 语法结构, 在单引号字符串中的变量和特殊字符的转义序列将不会被替换
双引号 如果字符串是包围在双引号 (") 中, PHP 将对一些特殊的字符进行解析
转义字符
序列 | 含义 |
---|---|
\n | 换行(ASCII 字符集中的 LF 或 0x0A (10)) |
\r | 回车(ASCII 字符集中的 CR 或 0x0D (13)) |
\t | 水平制表符(ASCII 字符集中的 HT 或 0x09 (9)) |
\v | 垂直制表符(ASCII 字符集中的 VT 或 0x0B (11))(自 PHP 5.2.5 起) |
\e | Escape(ASCII 字符集中的 ESC 或 0x1B (27))(自 PHP 5.4.0 起) |
\f | 换页(ASCII 字符集中的 FF 或 0x0C (12))(自 PHP 5.2.5 起 |
\ | 反斜线 |
$ | 美元标记 |
" | 双引号 |
[0-7]{1,3} | 符合该正则表达式序列的是一个以八进制方式来表达的字符 |
\x[0-9A-Fa-f]{1,2} | 符合该正则表达式序列的是一个以十六进制方式来表达的字符 |
和单引号字符串一样, 转义任何其他字符都会导致反斜线被显示出来. PHP 5.1.1 以前, {$var} 中的反斜线还不会被显示出来 |
用双引号定义的字符串最重要的特征是变量会被解析
Heredoc 结构
第三种表达字符串的方法使用 heredoc 语法结构: <<<
在该运算符之后要提供一个标识符, 然后换行. 接下来是字符串 string 本身, 最后要用前面定义的标识符作为结束标志
结束时所引用的标识符必须在该行的第一列,而且 标识符的命名也要像其它标签一样遵守 PHP 的规则: 只能包含字母 数字 和 下划线, 并且必须以字母和下划线作为开头
Warning 要注意的是结束标识符这行除了可能有一个分号(;)外,绝对不能包含其它字符。这意味着标识符不能缩进,分号的前后也不能有任何空白或制表符。更重要的是结束标识符的前面必须是个被本地操作系统认可的换行,比如在 UNIX 和 Mac OS X 系统中是 \n,而结束定界符(可能其后有个分号)之后也必须紧跟一个换行。
如果不遵守该规则导致结束标识不“干净”,PHP 将认为它不是结束标识符而继续寻找。如果在文件结束前也没有找到一个正确的结束标识符,PHP 将会在最后一行产生一个解析错误。
Heredocs 结构不能用来初始化类的属性。自 PHP 5.3 起,此限制仅对 heredoc 包含变量时有效。
Heredoc 结构就像是没有使用双引号的双引号字符串, 这就是说在 heredoc 结构中单引号不用被转义
Nowdoc 结构 就像 heredoc 结构类似于双引号字符串, Nowdoc 结构是类似于单引号字符串的. Nowdoc 结构很像 heredoc 结构, 但是 nowdoc 中不进行解析操作. 这种结构很适合用于 PHP 代码或其它打断文本而无需对其中的特殊字符进行转义
一个 nowdoc 结构也用和 heredoc 结构一样的标记 <<<
但是跟在后面的标识符要用单引号括起来, 即 <<<'EOT'. Heredoc 结构的所有规则也同样适用于 nowdoc 结构, 尤其是结束标识符的规则
不像 heredoc 结构, nowdoc 结构可以用在任一的金泰数据环境中, 最典型的实例是用来初始化类的属性或常量
<?php
class foo {
public $bar = <<<'EOT'
bar
EOT;
}
?>
变量解析 当字符串用双引号或 heredoc 结构定义时, 其中的变量将会被解析
这里共有两种语法规则: 一种简单规则, 一种复杂规则. 简单的语法则是最常用的最方便的, 它可以用最少的代码在一个 string 中嵌入一个变量, 一个 array 的值, 或一个 object 的属性
复杂规则语法的显著标记使用花括号包围的表达式
==简单语法== 当 PHP 解析器遇到一个美元符号 ($) 时, 它会和其他很多解析器一样, 去组合尽量多的标识以形成一个合法的变量名. 可以用花括号来明确变量名的界线.
==复杂 (花括号) 语法== 复杂语法不是因为其语法复杂而得名, 而是因为它可以使用复杂的表达式
任何局右 string 表达的标量变量, 数组单元或对象属性都可使用此语法. 只需要简单地像在 string 以外的地方那样写出表达式, 然后用花括号 {
和 }
把它括起来即可. 由于 {
无法被转义, 只有 $
紧挨着 {
时才会被识别. 可以用 {\$
来表达 {$
Note: 函数 方法 静态类变量和类常量只有在 PHP 5 以后才可在
{$}
中使用. 然而 只有在该字符串被定义的命名空间中才可以将其值作为变量名来访问. 只单一使用花括号 ({}
) 无法处理从函数或方法的返回值或者类常量以及类静态变量的值.
存取和修改字符串中的字符 string 中的字符可以通过一个从 0 开始的下标, 用类似 array 结构中的方括号包含对应的数字来访问修改
Note: string 也可用花括号访问 $str{0} Warning 用超出字符串长度的下标写入将会拉长该字符串并以空格填充. 非整数类型下标会被转换成整数. 非法下标类型会产生一个 E_NOTICE 级别错误. 用负数下标写入字符串时会产生一个 E_NOTICE 级别错误, 用负数下标读取字符串时返回空字符串. 写入时只用到了赋值字符串的第一个字符. 用空字符串赋值则覆给的值是 NULL 字符 Warning PHP 的字符串在内部是字节组成的数组. 因此用花括号访问或修改字符串对多字节字符集很不安全. 自 PHP 5.4 起字符串下标必须为整数或可转换为整数的字符串, 否则会发出警告. Note: 用 [] 或 {} 访问任何其它类型 (不包括数组或具有相应接口的对象实现) 的变量只会无声地返回 NULL Note: PHP 5.5 增加了直接在字符串原型中用 [] 或 {} 访问字符的支持
转换成字符串 一个值可以通过在前面加上 (string) 或用 strval() 函数来转变成字符串. 在一个需要字符串的表达式中, 会自动转换为 string . 比如在使用函数 echo 或 print 时, 或在一个变量和一个 string 进行比较时, 就会发生这种转换
一个布尔值 boolean 的 TRUE 被转换成 string 的 "1". Boolean 的 FALSE 被转换成 "" (空字符串).这种转换可以再 boolean 和 string 之间互相进行
一个整数 integer 或浮点数 float 被转换为数字的字面样式的 string (包括 float 中的指数部分)
数组 array 总是转换成字符串 "Array", 因此 echo 和 print 无法显示出该数组的内容
在 PHP 4 中对象 object 总是被转换成字符串 "Object"
资源 resource 总会被转变成 "Resource id #1" 这种结构的字符串, 其中的 1是 PHP 在运行时分配给该 resource 的唯一值.
NULL 总是被转变成空字符串
####Array 数组 PHP 中的数组实际上是一个有序映射. 映射是一种把 values 关联到 keys 的类型. 此类型在很多方面做了优化, 因此可以把它当成真正的数组, 或列表 (向量) , 散列表 (是映射的一种实现) , 字典, 集合, 栈, 队列以及更多可能性. 由于数组元素的值也可以是另一个数组, 树形结构和多维数组也是允许的
可以用 array() 语言结构来新建一个数组. 它接受任意数量用逗号分隔的 键 (key) => 值 (value) 对
自 5.4 起可以使用短数组定义语法, 用 [] 替代 array()
最后一个数组单元之后的逗号可以省略. 通常用于单行数组定义中. 对多行数组定义通常保留最后一个逗号, 这样要添加一个新单元时更方便
键 (key) 可是是一个整数 integer 或字符串 string 值 (value) 可以是任意类型的值
key 会有如下的的强制转换
- 包含有合法整型值的字符串会被转换为整型. 例如键名 "8" 实际会被储存为 8. 但是"08" 则不会强制转换, 因为其不是一个合法的十进制数值
- 浮点数也会被转换为整型, 意味着其小数部分会被舍去. 例如键名 8.7 实际会被储存为 8
- 布尔值也会被转换成整型. 即键名 true 实际会被存储为 1 而键名 false 会被存储为 0
- Null 会被转换为空字符串, 即键名 null 实际会被存储为 ""
- 数组和对象不能被用为键名. 坚持这么做会导致警告: Illegal offset type
如果在数组定义中多个单元都使用了同一个键名, 则只使用了最后一个, 之前的都被覆盖了
PHP 数组可以同时含有 integer 和 string 类型的键名, 因为 PHP 实际并不区分索引数组和关联数组
如果对给出的值没有制定键名, 则取当前最大的整数索引值, 而新的键名将是该值加一. 如果指定的键名已经有了值, 则该值会被覆盖.
注意这里所使用的最大整数键名不一定当前就在数组中. 它只要在上次数组重新生成索引后曾经存在过就行了
方括号和花括号可以互换使用来访问数组单元 效果相同
自 PHP 5.4 起可以用数组间接引用函数或方法调用的结果. 之前只能通过一个临时变量
自 PHP 5.5 起可以用数组间接引用一个数组原型
Note: 视图访问一个未定义的数组键名与访问任何未定义变量一样: 会导致 E_NOTICE 级别错误信息, 其结果为 NULL
转换为数组 对于任意 integer, float, string, boolean 和 resourcee 类型, 如果将一个值转换为数组,将得到一个仅有一个元素的数组, 其下标为 0, 该元素即为此标量的值. 换句话说, (array)$scalarValue 与 array($scalarValue) 完全一样
如果一个 object 类型转换为 array , 则结果为一个数组, 其单元为该对象的属性. 键名蒋为成员变量名, 不过有几点例外: 整数属性不可访问; 私有变量前回家上类名做前缀; 保护变量前会加上一个 '*' 做前缀. 这些前缀前后都各有一个 NULL 字符
####Object 对象 要创建一个行的对象 object , 使用 new 语句实例化一个类
转换为对象 如果讲一个对象转换成对象, 它将不会有任何变化. 如果其他任何类型的值被转换成对象, 将会创建一个内置类 stdClass 的实例. 如果该值为 NULL , 则新的实例为空. 数组转换成对象将使键名成为属性名并具有相对应的值. 对于任何其他的值, 名为 scalar 的成员变量将包含该值
创建一个空类
$genericObject = new stdClass()
在PHP 7中有几个方法可以创建一个空对象
- $obj1 = new \stdClass; // Instantiate stdClass object
- $obj2 = new class{}; // Instantiate anonymous class
- $obj3 = (object)[]; // Cast empty array to object
####Resource 资源类型 资源 resource 是一种特殊变量, 保存了到外部资源的一个引用. 资源是通过专门的函数来建立和使用的
转换为资源 由于资源类型变量保存有为打开文件 数据库链接 图形画布区域等的特殊句柄, 因此将其他类型的值转换为资源没有意义
释放资源 由于 PHP 4 Zend 引擎引进了引用计数系统, 可以自动检测到一个资源不在被引用了 (和 java 一样). 这种情况下此资源使用的所有外部资源都会被垃圾回收系统释放. 因此, 很少需要手工释放内存
Note: 持久数据库链接比较特殊, 它们不会被垃圾回收系统销毁
####NULL 特殊的 NULL 值表示一个变量没有值. NULL 类型唯一可能的值就是 NULL
在下列情况下一个变量被认为是 NULL:
- 被赋值为 NULL
- 尚未被赋值
- 被 unset()
NULL 类型只有一个值, 就是不区分大小写的常量 NULL
**转换到 NULL ** 使用 (unset)$var 将一个变量转换为 null 将不会删除该变量或 unset 其值. 仅是返回 NULL 值而已
注意:空数组转换为零的非严格等于“= =”比较。使用is_null()或“= = = '如果有可能得到空数组。
$a = array();
$a == null <== return true
$a === null < == return false
is_null($a) <== return false
get/post接收方式 简短风格 $tireqty 需要设置register_globals配置选项设置为on 不推荐使用 中等风格 $_POST['tireqty'] 推荐使用 冗长风格 $HTTP_POST_VARS['tireqty'] 弃用 reister_long_arrays配置指令禁用它,这样能改进性能
####Callback 回调类型 自 PHP 5.4 起可用 callable 类型制定回调类型 callback
传递 一个 PHP 的函数以 string 类型传递其名称. 可以使用任何内置或用户自定义函数, 但除了语言结构 : array(), echo, empty(), eval(), exit(), isset(), print 或 unset()
一个已实例化的对象的方法被作为数组传递, 下标 0 包含该对象, 下标 1 包含方法名
静态类方法也可不经实例化该类的对象而传递, 只要在下标 0 中包含类名而不是对象. 自 PHP 5.2.3 起, 也可以传递 'ClassName::MethodName'
除了普通的用户自定义函数外, create_function() 可以用来创建一个匿名回调函数. 自 PHP 5.3.0 起也可传递 closure 给回调参数
####类型转换的判别 PHP 在变量定义中不需要 (或不支持) 明确的类型定义; 变量类型是根据使用该变量的上下文所决定的. 也就是说, 如果把一个 string 赋值给变量 $var , $var 就成了一个 string. 如果又把一个 integer 赋给 $var , 那么它就成了一个 integer
PHP 的自动类型转换的一个例子是加法运算符"+". 如果任何一个操作数是 float, 则所有的操作数都被当成 float, 结果也是 float. 否则操作数会被解释为 integer, 结果也是 integer. 注意这并没有改变这些操作数本身的类型; 改变的仅是这些操作数如何被求值以及表达式本身的类型
如果要改变一个变量的类型可以使用 settype()
Note: 自动转换为数组的行为目前没有定义
####类型强制转换 PHP 中的类型强制转换和 C 中的非常像: 在要转换的变量之前加上用括号括起来的目标类型
允许的强制转换有:
- (int),(integer) - 转换为整型 integer
- (bool),(boolean) - 转换为布尔类型 boolean
- (float),(double),(real) - 转换为浮点型 float
- (string) - 转换为字符串 string
- (array) - 转换为数组 array
- (object) - 转换为对象 object
- (unset) - 转换为 NULL (PHP 5)
- (binary) - 将字符串文字和变量转换为二进制字符串 PHP 5.2.1 新增
Note: 可以将变量放置在双引号中的方式来代替将变量转换成字符串:
<?php
$foo = 10; // $foo 是一个整数
$str = "$foo"; // $str 是一个字符串
$fst = (string) $foo; // $fst 也是一个字符串
// 输出 "they are the same"
if ($fst === $str) {
echo "they are the same";
}
?>
###变量 ####基础 PHP 中的变量用一个美元符号后面跟变量名俩表示. 变量名是区分大小写的
变量名与 PHP 中其他的标签一样遵循相同的规则. 一个有效的变量名由字母或者下划线开头, 后面跟上任意数量的字母, 数字, 或者下划线. 按照正常的正则表达式, 它将被表述为: '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
Note: 在此所说的字母是 a-z, A-Z, 以及 ASCII 字符从127 到255 (0x7f-0xff)
Note: $this 是一个特殊的变量, 它不能被赋值
变量默认总是传值赋值. 那么就是说, 当将一个表达式的值赋予一个变量时, 整个原始表达式的值被赋值到目标变量. 但一个变量的值赋予另一个变量时, 改变其中一个变量的值, 将不会影响到另外一个变量
PHP 也提供了另外一种方式给变量赋值: 引用赋值. 这意味着新的变量简单的引用 (换言之, "成为其别名" 或者 "指向") 了原始变量. 改动新的变量将影响到原始变量, 反之亦然
使用引用赋值, 简单地讲一个 & 符号加到要赋值的变量前 (源变量)
有一点重要事项必须指出, 那就是只有有名字的变量才可以引用赋值
虽然子啊 PHP 中并不需要初始化变量, 但对变量进行初始化是个好习惯. 未初始化的变量局右其类型的默认值 - 布尔类型的变量默认值是 FALSE, 整型和浮点型变量默认值是零, 字符串型变量 (例如用于 echo 中) 默认值是空字符创以及数组变量的默认值是空数组
依赖未初始化变量的默认值在某些情况下会有问题, 例如把一个文件包含到另一个之中时碰上相同的变量名. 另外把 register_globals 打开是一个主要的安全隐患. 使用未初始化的变量会发出 E_NOTICE 错误, 但是在向一个未初始化的数组附加单元时不会. isset() 语言结构可以用来检测一个变量是否已被初始化
####预定义变量 对于全部脚本而言, PHP 提供了大量的预定义变量. 这些变量将所有的外部变量表示成内建环境变量, 并且将错误信息表示成返回头. 一些预定义变量在 PHP 以命令形式运行时并不生效
名称 | 作用 |
---|---|
超全局变量 | 超全局变量是在全部作用域中始终可用的内置变量 |
$GLOBALS | 引用全局作用域中可用的全部变量 |
$_SERVER | 服务器和执行环境信息 |
$_GET | 接收用户表单get方式提交的所有数据 |
$_POST | 接收用户表单post方式提交的所有数据 |
$_FILES | 接收用户上传的文件信息 |
$_COOKIE | 存放cookie数据(存放在浏览器) |
$_SESSION | 存放session数据(存放在服务器) |
$_REQUEST | 接收用户get 或 post方式提交的数据,有害cookie数据 |
$_ENV | 保存系统环境信息 |
$php_errormsg | 前一个错误信息 |
$HTTP_RAW_POST_DATA | 原生POST数据 |
$http_response_header | HTTP响应头 |
$argc | 传递给脚本的参数数目 |
$argv | 传递给脚本的参数数组 |
Note: 尽管超全局变量和 HTTP_*_VARS 同时存在, 但是它们并不是同一个变量, 所以改变其中一个的值并不会对另一个产生影响
如果某些 variables_order
中的变量没有设定, 它们的对应的 PHP 预定义数组也是空的
PHP将自动替换任何点在传入的变量名称(“。”)和下划线(“_”)。如果你点的输入变量,例如:
example.com/page.php?chuck.norris=nevercries
你不能引用它们的名称中使用URI:
//错误的
echo $_GET['chuck.norris'];
相反,您必须使用:
//正确的
echo $_GET['chuck_norris'];
#####超全局变量
PHP 中的许多预定义变量都是 "超全局的" , 这意味这它们在一个脚本的全部作用域中都可用. 在函数或方法中无需执行 global $variable;
就可以访问它们
这些全局变量是
-
$GLOBALS
-
$_SERVER
-
$_GET
-
$_POST
-
$_FILES
-
$_COOKIE
-
$_REQUEST
-
$_ENV
Note: 处理 register_globals
如果已经弃用的 register_globals 指令被设置为 on 那么局部变量也将在脚本的全局作用域中可用. 例如, $_POST['foo'] 也将以 $foo 形式存在
Note: 在函数或类方法中, 超全局变量不能被用作可变变量
#####$GLOBALS 一个包含了全部变量的全局组合数组. 变量的名字就是数组的键
Note: "Superglobal" 也称为自动化的全局变量. 这就表示其在脚本的所有作用域中都是用的. 不需要在函数或方法中用 global $variable; 来访问它
Note: 与所有其他超全局变量不同, $GLOBALS 在 PHP 中总是可用的
#####$_SERVER
$_SERVER
是一个包含了诸如头信息 (header) 路径(path) 以及脚本位置(script locations)等等信息的数组. 这个数组中的项目由 Web 服务器创建. 不能保证每个服务器都提供全部项目; 服务器可能会忽略一些, 或者踢动一些灭有在这里列举出来的项目.
在 $_SERVER
中, 你也许能够, 也许不能够找到下面的这些元素.
参数名 | 信息 | 警告 |
---|---|---|
'PHP_SELF' | 当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/test.php/foo.bar 的脚本中使用 $_SERVER['PHP_SELF'] 将得到 /test.php/foo.bar。FILE 常量包含当前(例如包含)文件的完整路径和文件名。 从 PHP 4.3.0 版本开始,如果 PHP 以命令行模式运行,这个变量将包含脚本名。之前的版本该变量不可用。 | |
'argv' | 传递给该脚本的参数的数组。当脚本以命令行方式运行时,argv 变量传递给程序 C 语言样式的命令行参数。当通过 GET 方式调用时,该变量包含query string。 | |
'argc' | 包含命令行模式下传递给该脚本的参数的数目(如果运行在命令行模式下)。 | |
'GATEWAY_INTERFACE' | 服务器使用的 CGI 规范的版本;例如,“CGI/1.1”。 | |
'SERVER_ADDR' | 当前运行脚本所在的服务器的 IP 地址。 | |
'SERVER_NAME' | 当前运行脚本所在的服务器的主机名。如果脚本运行于虚拟主机中,该名称是由那个虚拟主机所设置的值决定。 | |
'SERVER_SOFTWARE' | 服务器标识字符串,在响应请求时的头信息中给出。 | |
'SERVER_PROTOCOL' | 请求页面时通信协议的名称和版本。例如,“HTTP/1.0”。 | |
'REQUEST_METHOD' | 访问页面使用的请求方法;例如,“GET”, “HEAD”,“POST”,“PUT”。 | 如果请求方法为 HEAD,PHP 脚本将在发送 Header 头信息之后终止(这意味着在产生任何输出后,不再有输出缓冲)。 |
'REQUEST_TIME' | 请求开始时的时间戳。从 PHP 5.1.0 起可用。 | |
'REQUEST_TIME_FLOAT' | 请求开始时的时间戳,微秒级别的精准度。 自 PHP 5.4.0 开始生效。 | |
'QUERY_STRING' | query string(查询字符串),如果有的话,通过它进行页面访问。 | |
'DOCUMENT_ROOT' | 当前运行脚本所在的文档根目录。在服务器配置文件中定义。 | |
'HTTP_ACCEPT' | 当前请求头中 Accept: 项的内容,如果存在的话。 | |
'HTTP_ACCEPT_CHARSET' | 当前请求头中 Accept-Charset: 项的内容,如果存在的话。例如:“iso-8859-1,*,utf-8”。 | |
HTTP_ACCEPT_ENCODING' | 当前请求头中 Accept-Encoding: 项的内容,如果存在的话。例如:“gzip”。 | |
'HTTP_ACCEPT_LANGUAGE' | 当前请求头中 Accept-Language: 项的内容,如果存在的话。例如:“en”。 | |
'HTTP_CONNECTION' | 当前请求头中 Connection: 项的内容,如果存在的话。例如:“Keep-Alive”。 | |
'HTTP_HOST' | 当前请求头中 Host: 项的内容,如果存在的话。 | |
'HTTP_REFERER' | 引导用户代理到当前页的前一页的地址(如果存在)。由 user agent 设置决定。并不是所有的用户代理都会设置该项,有的还提供了修改 HTTP_REFERER 的功能。简言之,该值并不可信。 | |
'HTTP_USER_AGENT' | 当前请求头中 User-Agent: 项的内容,如果存在的话。该字符串表明了访问该页面的用户代理的信息。一个典型的例子是:Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586)。除此之外,你可以通过 get_browser() 来使用该值,从而定制页面输出以便适应用户代理的性能。 | |
'HTTPS' | 如果脚本是通过 HTTPS 协议被访问,则被设为一个非空的值。 | 注意当使用 IIS 上的 ISAPI 方式时,如果不是通过 HTTPS 协议被访问,这个值将为 off。 |
'REMOTE_ADDR' | 浏览当前页面的用户的 IP 地址。 | |
'REMOTE_HOST' | 浏览当前页面的用户的主机名。DNS 反向解析不依赖于用户的 REMOTE_ADDR | 你的服务器必须被配置以便产生这个变量。例如在 Apache 中,你需要在 httpd.conf 中设置 HostnameLookups On 来产生它。参见 gethostbyaddr()。 |
'REMOTE_PORT' | 用户机器上连接到 Web 服务器所使用的端口号。 | |
'REMOTE_USER' | 经验证的用户 | |
'REDIRECT_REMOTE_USER' | 验证的用户,如果请求已在内部重定向。 | |
'SCRIPT_FILENAME' | 当前执行脚本的绝对路径。 | 如果在命令行界面(Command Line Interface, CLI)使用相对路径执行脚本,例如 file.php 或 ../file.php,那么 $_SERVER['SCRIPT_FILENAME'] 将包含用户指定的相对路径。 |
'SERVER_ADMIN' | 该值指明了 Apache 服务器配置文件中的 SERVER_ADMIN 参数。如果脚本运行在一个虚拟主机上,则该值是那个虚拟主机的值。 | |
'SERVER_PORT' | Web 服务器使用的端口。默认值为 “80”。如果使用 SSL 安全连接,则这个值为用户设置的 HTTP 端口。 | |
'SERVER_SIGNATURE' | 包含了服务器版本和虚拟主机名的字符串。 | |
'PATH_TRANSLATED' | 当前脚本所在文件系统(非文档根目录)的基本路径。这是在服务器进行虚拟到真实路径的映像后的结果。 | 自 PHP 4.3.2 起,PATH_TRANSLATED 在 Apache 2 SAPI 模式下不再和 Apache 1 一样隐含赋值,而是若 Apache 不生成此值,PHP 便自己生成并将其值放入 SCRIPT_FILENAME 服务器常量中。这个修改遵守了 CGI 规范,PATH_TRANSLATED 仅在 PATH_INFO 被定义的条件下才存在。 Apache 2 用户可以在 httpd.conf 中设置 AcceptPathInfo = On 来定义 PATH_INFO。 |
'SCRIPT_NAME' | 包含当前脚本的路径。这在页面需要指向自己时非常有用。FILE 常量包含当前脚本(例如包含文件)的完整路径和文件名。 | |
'REQUEST_URI' | URI 用来指定要访问的页面。例如 “/index.html”。 | |
'PHP_AUTH_DIGEST' | 当作为 Apache 模块运行时,进行 HTTP Digest 认证的过程中,此变量被设置成客户端发送的“Authorization” HTTP 头内容(以便作进一步的认证操作)。 | |
'PHP_AUTH_USER' | 当 PHP 运行在 Apache 或 IIS(PHP 5 是 ISAPI)模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的用户名。 | |
'PHP_AUTH_PW' | 当 PHP 运行在 Apache 或 IIS(PHP 5 是 ISAPI)模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的密码。 | |
'AUTH_TYPE' | 当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是认证的类型。 | |
'PATH_INFO' | 包含由客户端提供的、跟在真实脚本名称之后并且在查询语句(query string)之前的路径信息,如果存在的话。例如,如果当前脚本是通过 URL http://www.example.com/php/path_info.php/some/stuff?foo=bar 被访问,那么 $_SERVER['PATH_INFO'] 将包含 /some/stuff。 | |
'ORIG_PATH_INFO' | 在被 PHP 处理之前,“PATH_INFO” 的原始版本。 |
#####$_GET 通过 URL 参数传递给当前脚本的变量数组
Note: GET是通过 urldecode() 传递的
#####$_POST 通过 HTTP POST 方法传递给当前脚本的变量数组
#####$_FILES 通过 HTTP POST 方式上传到当前脚本的项目的数组
#####$_REQUEST
默认情况下包含了 $_GET
, $_POST
和 $_COOKIE
的数组
Note: "由于 $_REQUEST 中的变量通过 GET , POST 和 COOKIE 输入机制传递给脚本文件, 因此可以被远程用户篡改而并不可信. 这个数组的项目及其顺序依赖与 PHP 的 variables_order 指令的配置
别忘了,因为$ _REQUEST是不同的变量$ _GET $ _POST,这被视为在PHP中——在运行时修改$ _GET或$ _POST元素不会影响$ _REQUEST 的值
#####$_SESSION 当前脚本可用 SESSION 变量的数组
#####$_ENV 通过环境方式传递给当前脚本的变量的数组
这些变量被从 PHP 解析器的运行环境导入到 PHP 的全局命名空间. 很多是由支持 PHP 运行的 Shell 提供的, 并且不同的系统很可能运行着不同种类的 Shell, 所以不可能有一份确定的列表. 请查看你的Shell 文档来获取定义环境变量列表
如果你的$ _ENV数组是神秘的空,但你还看到变量在调用getenv()或在phpinfo() 检查 php.ini variables_order 是否有 E
#####$_COOKIE 通过 HTTP Cookies 方式传递给当前脚本的变量的数组
####$php_errormsg
$php_errormsg
变量包含由 PHP 生成的最新错误信息. 这个变量只在错误发生的作用域内可用, 并且要求 track_errors 配置项是开启的 (默认是关闭的)
Warning 如果用户定义错误处理句柄 (set_error_handler()) 并且返回 FALSE 的时候, $php_errormsg 就会被设置
#####$HTTP_RAW_POST_DATA
Warning PHP 5.6.0 弃用
$HTTP_RAW_POST_DATA
包含 POST 提交的原始数据
一般而言, 使用php://input
代替 $HTTP_RAW_POST_DATA
. 因为php://input
不用依赖于 php.ini
php://input 不能用于 enctype="multipart/form-data"
#####$http_response_header
$http_reponse_header
数组与 get_headers() 函数类似. 当使用 HTTP 包装器时, $http_response_header
将会被 HTTP 响应头信息填充. $http_response_header
将被创建与局部作用域中
file_get_contents("http://example.com");
Note: HTTP包装器的硬限制为1024个字符的标题行。任何HTTP头收到超过这将被忽略,并且不会出现在$http_response_header。
cURL扩展没有这个限制
http_fopen_wrapper.c: #define HTTP_HEADER_BLOCK_SIZE 1024
#####$argc 包含当运行与命令行下时传递给当前脚本的参数的数目
Note: 脚本的文件名总是作为参数传递给当前脚本, 因此 $argc 的最小值为 1
Note: 这个变量仅在 register_Argentina_argv 打开时可用
检测你在CLI与否,这是更好的在我看来:
<?php
if (PHP_SAPI != "cli") {
exit;
}
?>
#####$argv 包含当运行与命令行下时传递给当前脚本的参数的数组
Note: 第一个参数总是当前脚本的文件名, 因此 $argv[0] 就是脚本文件名
Note: 这个变量仅在 register_argc_argv 打开时可用
Note: $argv和$argc 在静态类中使用时,需要声明全局
####变量范围 变量的范围即它定义的上下文背景 (uejiushi它的生效范围) . 大部分的 PHP 变量只有一个单独的范围. 这个单独的范围跨度同样包含了 include 和require 引入的文件.
在用户定义函数中, 一个局部函数范围将被引入. 任何用于函数内部的变量按缺省情况将被限制在局部函数范围内
PHP 中全局变量在函数使用时必须声明为 global
$GLOBALS
是一个关联数组, 每一个变量为一个元素, 键名对应变量名, 值对应变量的内容. $GLOBALS
之所以在全局范围内存在, 是因为 $GLOBALS 是一个超全局变量
#####使用静态变量 变量范围的另一个重要特性是静态变量 (static variable) . 静态变量尽在局部函数域中存在, 担当程序执行离开此作用域时, 其值并不丢失
Note: 如果在声明中用表达式的结果对静态变量赋值会导致解析错误.
Note: 在函数之外使用 blobal 关键字不算错. 可以用于在一个函数之内包含文件时
#####全局和静态变量的引用 在 Zend 引擎1代, 它驱动了 PHP4, 对于变量的 staric 和 blobal 定义是以引用的方式实现的. 例如,在一个函数域内部用 global 语句导入的一个真正的全局变量实际上是建立了一个到全局变量的引用
####可变变量 有时候使用可变变量名是很方便的. 就是说, 一个变量的变量名可以动态的设置和使用
一个可变变量获取了一个普通变量的值作为这个变量的变量名
要将可变变量用于数组, 必须解决一个模棱两可的问题. 这就是当写下 $$a[1] 时, 解析器需要知道是想要
类的属性也可以通过可变属性名来访问. 可变属性名将在该调用所处的范围内被解析. 例如, 对于 $foo->$bar 表达式, 则会在本地范围来解析 $bar 并且其值将被用于 $foo 的属性名. 对于 $bar 是数组单元时也是一样
也可使用花括号来给属性名清晰定界. 最有用实在属性位于数组中, 或者属性名包含有多个部分或者属性名包含有非法字符时 (例如来自 json_decode() 或 SimpleXML )
Warning 在 PHP 的函数和类的方法中, 超全局变量不能用作可变变量. $this 变量也是一个特殊变量, 不能被动态引用
####来自 PHP 之外的变量 #####表单 (GET 和 POST) 点一个表单提交给 PHP 脚本时, 表单中的信息会自动在脚本中可用
Note: 变量名中的点和空格被转换成下划线
Note: magic_quotes_gpc 配置指令影响到 Get, Post 和 Cookie 的值. 如果打开, 值(It's "PHP!") 会自动转成 (IT\'s \"PHP!\"). 十多年前对数据库的插入需要如此转义, 如今已经过时了, 应该关闭. 参见 addslashes(), stripslashes() 和 magic_quotes_sybase
#####IMAGE SUBMIT 变量名 当提交表单时, 可以用一副图像代替标准的提交按钮, 用类似这样的标记
<input type='image' src='image.fig' name='sub' />
当用户点击到图像中的某处时, 相应的表单会被传送来服务器, 并加上两个变量 sub_x 和 sub_y . 它们包含了用户点击图像的坐标
#####HTTP Cookies PHP 透明地支持 >> RFC 6265 定义中的 HTTP cookies. Cookies 是一种在远端浏览器端存储数据并能追踪或识别的用户机制. 可以用 setcookie() 函数谁定 cookies. Cookie 是 HTTP 信息头中的一部分, 因此 SetCookie 函数必须在向浏览器发送任何输出之前调用. 对于 header() 函数也有同样的限制
想在仅仅一个 cookie 中设定多个值, 考虑现在值上使用 serialize() 或 explode()
#####变量名中的点 通常, PHP 不会改变传递给脚本中的变量名. 然而应该注意到点 (.) 不是 PHP 变量中的合法字符. 这是, 解析器看到是一个名为 $varname 的变量, 后面跟着一个字符串链接运算符, 后面跟着一个裸字符串 (既没有加引号的字符串, 且不匹配任何已知的键名或保留字) . 很明显这不是想要的结果. 出于此原因, 要注意 PHP 将会自动将变量名中的点替换成下划线
#####确定变量类型 因为 PHP 会判断变量类型并在需要时进行转换 (通常情况下) , 因此在某一时刻给定的变量是何种类型并不明显. PHP 包括几个函数可以判断变量的类型
函数名 | 作用 |
---|---|
gettype() | 获取变量的类型 |
is_bool() | is_bool() |
is_string() | 字符 |
is_int() | 整数 |
is_float() | 浮点 |
is_array() | |
is_object() | |
is_null() | |
is_resource() | |
is_numeric() | 检测变量是否为数字或数字字符串 |
is_scalar() | 判断一个变量的值是否是标量类型 |
is_cllable() | 检查该变量是否是有效的函数名称 |
###常量 常量是一个简单值的标识符 (名字) . 如同其名称所暗示的, 在脚本执行期间该值不能改变 (除了所谓的 魔术常量, 它们其实不是常量) . 常量默认为大小写敏感. 传统上常量标识符总是大写的
和 superglobals 一样, 常量的范围是全局的. 不用管作用区域就可以在脚本的任何地方访问常量
####语法 可以用 define() 函数来定义常量, 在 PHP 5.3.0 以后, 可以使用 const 关键字在类定义之外定义常量. 一个常量一旦被定义, 就不能在改变或者取消定义
常量只能包含数据 (boolean, integer, float和string) . 可以定义 resource 常量, 但尽量避免, 因为会赵成不可预料的结果
可以简单的通过其名字来去等常量的值, 与变量不同, 不应该在常量前面加上 $ 符号. 如果常量名是动态的, 也可以用函数 constant() 来获取常量的值. 用个体_defined_constants() 可以获得所有已定义的常量列表.
Note: 常量和 (全局) 变量在不同的名字空间中. 这意味着列入 true 和 $true 是不同的.
如果使用了一个未定义的常量, PHP 假定想要的是该常量本身的名字, 如同用字符串调用它一样. 此时将发出一个 E_NOTICE 级的错误.如果只想检查是否定义了某常量, 用 fefined() 函数
常量和变量有如下不同
-
常量前面没有美元符号 ($)
-
常量只能用 define() 函数定义, 而不能通过赋值语句
-
常量可以不用例会变量的作用域而在任何地方定义和访问
-
常量一旦定义就不能被重新定义或者取消定义
-
常量的值只能是标量
Note: 和使用 define() 来定义常量相反的是, 使用 const 关键字定义常量必须处于最顶端的作用区域, 因为用此方法是在编译时定义的. 这就意味着不能在函数内, 循环内以及 if 语句之内用 const 来定定义常量
####魔术常量 PHP 向他和运行的任何脚本提供了大量的预定义常量. 不过很多常量都是由不同的扩展库定义的, 只有在加载了这些扩展时才会出现, 或者动态加载后, 后者在编译时已经包括进去了
有八个魔术常量它们的值随着它们在代码中的位置改变而改变
名称 | 说明 |
---|---|
LINE | 文件中的当前行号 |
FILE | 文件的完整路径和文件名. 如果在被包含文件中, 则返回被包含的文件名. 自 PHP 4.0.2 起, FILE 总是包含一个绝对路径 (如果是符号链接, 则是解析后的绝对路径) , 而在此之前的版本有时会包含一个相对路径 |
DIR | 文件所在的目录. 如果用在被包括文件中, 则返回被包括的文件所在的目录. 它等价于 dirname (FILE) . 除非是根目录, 否则目录中名不包括末尾的斜杠. (PHP 5.3.0 中新增) |
FUNCTION | 函数名称 (PHP 4.3.0 新加) . 自 PHP 5 起本常量返回该函数被定义时的名字 (区分大小写) . 在 PHP 4 中该值总是小写字母的 |
CLASS | 类的名称 (PHP 4.3.0 新加) . 自 PHP 5 起本常量返回该类被定义时的名字 (区分大小写) . 在 PHP 4 中该值总是小写字母的. 类名包括其被声明的作用区域 . 注意自 PHP 5.4 起 CLASS 对trait 也起作用. 当用在 trait 方法中时, CLASS 是调用 trait 方法的类的名字 |
TRAIT | Trait 的名字 (PHP 5.4.0 新加) . 自 PHP 5.4起此常量返回 trait被定义时的名字 (区分大小写) . Trait 名包括其被声明的作用区域 |
METHOD | 类的方法名(PHP 5.0.0 新加) . 返回该方法被定义时的名字 (区分大小写) |
NAMESPACE | 当前命名空间的名称 (区分大小写) . 此常量是在编译时定义的 (PHP 5.3.0 新增) |
__FUNCTION__ 和 __METHOD__ 的区别在于 : __FUNCTION__ 输出functionName 而 __METHOD__ 输出 className::functionName
###表达式 表达式是 PHP 最重要的基石. 在 PHP 中, 几乎缩写的任何东西都是一个表达式. 简单但却最精确的定义一个表达式的方式就是"任何有值的东西"
最基本的表达式形式是常量和变量
###运算符 运算符是可以通过给出的一或多个值(用编程行话来说, 表达式) 来产生另一个值 (因而整个结构成为一个表达式) 的东西
运算符可按照其能接受几个值来分组. 一元运算符只能接受一个值, 例如 ~ ! ++ . 二元运算符可接受两个值, 例如 + - , 大多数 PHP 运算符都是这种. 最后是唯一的三元运算符 ?:, 可接受三个值, 通常 就简单称之为 "三元运算符" (尽管称之为条件运算符可能更合适)
####运算符优先级 运算符优先级制定了两个表达式绑定得有多"紧密", 如果运算符优先级相同, 那运算符的结合方向决定了该如何运算
没有结合的相同优先级的运算符不能连在一起使用, 例如 1<2>1
在 PHP 是不合法的. 但另外一方面表达式 1<=1==1
是合法的, 因为 == 的优先级低于 <=
括号的使用, 哪怕在不是必要的场合下, 通过括号的配对来明确表名运算顺序, 而非靠运算符优先级和结合性来决定, 通常能够增加代码么的可读性
#####运算优先级
结合方向 | 运算符 | 附加信息 |
---|---|---|
无 | clone new | clone 和 new |
左 | [ | array() |
右 | ** |
算术运算符 |
右 | ++ -- ~ (int)(float)(string)(array)(object)(bool)@ | 类型和递增/递减 |
无 | instanceof | 类型 |
右 | ! | 逻辑运算符 |
左 | * / % | 算术运算符 |
左 | + - . | 算术运算符和字符串运算符 |
左 | << >> | 位运算符 |
无 | < <= > >= | 比较运算符 |
左 | & | 位运算符和引用 |
左 | ^ | 位运算符 |
左 | | | 位运算符 |
左 | && | 逻辑运算符 |
左 | || | 逻辑运算符 |
左 | ?? | 比较运算符 |
左 | ?: | ternary |
右 | = += -= *= **= /= .= %= |= ^= <<= >>= | 赋值运算符 |
左 | and | 逻辑运算符 |
左 | xor | 逻辑运算符 |
左 | or | 逻辑运算符 |
Note: 尽管 = 比其它大多数的运算符的优先级低, PHP 仍旧允许类似如下的表达式: if(!$a = foo()) , 在此例中 foo() 的返回值被赋给了 $a
####算术运算符
例子 | 名称 | 结果 |
---|---|---|
-$a | 取反 | $a 的负值 |
$a + $b | 加法 | $a 和 $b 的和 |
$a - $b | 减法 | $a 和 $b 的差 |
$a * $b | 乘法 | $a 和 $b 的积 |
$a / $b | 除法 | $a 除以 $b 的商 |
$a % $b | 取模 | $a 除以 $b 的余数 |
$a ** $b |
乘方 | $a 的 $b 次方(PHP 5.6) |
除法运算符总是返回浮点数. 只有在 两个操作数都是整数 (或字符串转换成的整数) 并且正好能整除, 这时它返回一个整数
取模运算符的操作数在运算之前都会转换成整数 (除去小数部分)
取模运算符 % 的结果和被除数的符号 (正负号) 相同. 即 $a % $b 的结果和 $a 的符号相同
####赋值运算符 基本的赋值运算符是 "=". 一开始可能会以为他是 "等于", 其实不是的. 它实际商意味着把右边表达式的值赋给左边的运算数
赋值运算表达式的值也就是所附的值. 也就是说, "$a = 3" 的值是3.
注意赋值运算将原变量的值拷贝到新变量中 (传值赋值) , 所以改变其中一个并不影响另一个. 这也适合与在密集循环中拷贝一些值 例如大数组
在 PHP 中普通的传值赋值行为有个例外就是碰到对象 object 时, 在 PHP 5中是以引用赋值的, 除非明确使用了 clone 关键字来拷贝
#####引用赋值
PHP 支持引用赋值, 使用 $var = &$othervar;
语法. 引用赋值意味着两个变量指向了同一个数据, 没有拷贝任何东西
自 PHP 5起, new 运算符自动返回一个引用, 因此在对 new 的结果进行引用赋值在 PHP 5.3 以及以后版本中会发出一条 E_DEPRECATED 错误信息, 在之前版本会发出一条 E_STRICT 错误信息
####位运算符
例子 | 名称 | 结果 |
---|---|---|
$a & $b | And (按位与) | 将把 $a 和 $b 中都为1 的位设为1 |
$a | $b | Or (按位或) | 将把 $a 和 $b 中任何一个为1 的位设为1 |
$a ^ $b | Xor (按位异或) | 将把 $a 和 $b 中一个为1 另一个为0 的位设为1 |
~ $a | Not (按位取反) | 将 $a 中为 0 的位设为1, 反之亦然 |
$a << $b | 左移 | 将 $a 中的位向左移动 $b 次 (每一次移动都表示"乘以2") |
$a >> $b | 右移 | 将 $a 中的位向右移动 $b 次 (每一次移动都表示"除以2") |
位运算符允许对整型数中制定的位进行求值和操作
位移在 PHP 中是数学运算. 向任何方向移出去的位都被丢弃. 左移时右侧以零填充, 符号位被移走意味这正负号不被保留. 右移时左侧以符号位填充, 意味着正负号被保留
要用括号确保想要的优先级. 例如 $a & $b == true 先进行比较在进行按位与; 而 ($a & $b) == true 则先进行按位与在进行比较
>>
或 <<
两边操作数总被当作整数
####比较运算符
例子 | 名称 | 结果 |
---|---|---|
$a == $b | 等于 | TRUE, 如果类型转化后 $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, 如果 $a 严格小于 $b |
$a > $b | 大于 | TRUE, 如果 $a 严格大于 $b |
$a <= $b | 小于等于 | TRUE, 如果 $a 小于或者等于 $b |
$a >= $b | 大于等于 | TRUE, 如果 $a 大于或者等于 $b |
$a <=> $b | 结合比较运算符 | 当 $a 小于 等于 大于 than $b 时, 分别返回一个小于 等于 大于0 的 integer 值. PHP 7开始提供 |
$a ?? $b ?? $c | NULL 合并符 | 从左往右第一个存在且不为 NULL 的操作数. 如果都没有定义且不为 NULL, 则返回 NULL. PHP 7开始提供 |
如果比较一个数字和字符串或者比较涉及到数字内容的字符串, 则字符串会被转换为值并且比较按照数值进行. 此规则也用于 switch 语句. 当用 === 或 !== 进行比较时则不进行类型转换, 因为此时类型和数值都要比对
#####比较多种类型
运算数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 总是更大 |
PHP 5 中的对象比较要比 PHP 4 中复杂, 所期望的结果更复合一个面向对象语言
当使用比较运算符 (==) 比较两个对象变量时, 比较的原则是: 如果两个对象的属性和属性值都相等, 而且两个对象是同一个类的实例, 那么这两个对象变量相等.
而如果使用全等运算符 (===) , 这两个对象变量一定要指向某个类的同一个实例 (即同一个对象).
#####三元运算符 另一个条件运算符是 ?: (三元) 运算符
表达式 (expr1)?(expr2):(expr3) 在 expr1 求值为 TRUE 时的值为 expr2, 在 expr1 求值为 FALSE 时的值为 expr3.
自 PHP 5.3 起, 可以省略三元运算符中间那部分. 表达式 expr1 ?: expr3 在 expr1 求值为 TRUE 时返回expr1, 否则返回 expr3
Note: 注意三元运算符是个语句, 因此其求值不是变量, 而是语句的结果, 如果想通过引用返回一个变量这点就很重要. 在一个通过引用返回的函数中语句 return $var == 42?$a:$b ; 将不起作用, 以后的 PHP 版本会为此发出一条警告
Note: 建议避免将三元运算符堆积在一起使用. 当在一条语句中使用多个三元运算符时会造成 PHP 运算结果不清晰.
####错误控制运算符 PHP 支持一个错误控制运算符: @. 当将其放置在一个 PHP 表达式之前, 该表达式可能产生的任何错误信息都被忽略掉
如果用 set_error_handler() 设定了自定以的错误处理函数, 仍然会被调用, 但是此错误处理函数可以 (并且也应该) 调用 error_reporting(), 而该函数在出错语句前有 @ 时将返回 0
如果激活了 track_errors 特性, 表达式所产生的任何错误信息都被存放在变量 $php_errormsg 中. 此变量在每次出错时都会被覆盖, 所以如果想用它的话就要迟早检查
Note: @ 运算符支队表达式有效. 对新手来说一个简单的规则就是: 如果能从某处得到值, 就能在它前面加上 @ 运算符. 例如, 可以把它放在变量, 函数 和 include 调用, 常量, 等等之前. 不能把它放在函数或类定义之前, 也不能用于条件结构例如if和foreach等
Warning 目前的"@"错误控制运算符前缀甚至使导致脚本终止的严重错误的错误报告也失效. 这意味着如果在某个不存在或者敲错了字母的函数调用了"@"来抑制错误信息, 那脚本会没有任何迹象显示原因而死在那里
####执行运算符 PHP 支持一个执行运算符: 反引号 (``). 注意这不是单引号! PHP 将尝试将反引号中的内容作为 shell 命令来执行, 并将其输出信息返回 (即, 可以赋给一个变量而不是简单地丢弃到标准输出) . 使用反引号运算符 "`"的效果与函数 shell_exec() 相同.
Note: 反引号运算符在激活了安全模式或者关闭了 shell_exec() 时是无效的
Note: 与其它某些语言不同, 反引号不能在双引号字符串中使用
####递增/递减运算符 PHP 支持 C 风格的前/后递增与递减运算符
Note: 递增/递减运算符不影响布尔值. 递减 NULL值也没有效果, 但是递增 NULL的结果是 1
例子 | 名称 | 效果 |
---|---|---|
++$a | 前加 | $a 的值加一, 然后返回 $a |
$a++ | 后加 | 返回 $a, 然后将 $a 的值加一 |
--$a | 前减 | $a 的值减一, 然后返回 $a |
$a-- | 后减 | 返回 $a, 然后将 $a 的值减一 |
在处理字符变量的算数运算时,PHP 沿袭了 Perl 的习惯,而非 C 的。例如,在 Perl 中 $a = 'Z'; $a++; 将把 $a 变成'AA',而在 C 中,a = 'Z'; a++; 将把 a 变成 '['('Z' 的 ASCII 值是 90,'[' 的 ASCII 值是 91)。注意字符变量只能递增,不能递减,并且只支持纯字母(a-z 和 A-Z)。递增/递减其他字符变量则无效,原字符串没有变化。
####逻辑运算符
例子 | 名称 | 结果 |
---|---|---|
$a and $b | And (逻辑与) | TRUE, 如果 $a 和 $b 都为 TRUE |
$a or $b | OR (逻辑或) | TRUE, 如果 $a 或 $b 任一为 TRUE |
$a xor $b | Xor (逻辑异或) | TRUE, 如果 $a 或 $b 任一为 TRUE, 但不同时是 |
!$a | Not (逻辑非) | TRUE, 如果 $a 不为 TRUE |
$a && $b | And (逻辑与) | TRUE, 如果 $a 和 $b 都为 TRUE |
$a || $b | Or (逻辑或) | TRUE, 如果 $a 或 $b 任一为 TRUE |
逻辑操作符总是返回一个 boolean
####字符串运算符 有两个字符串 (string) 运算符. 第一个是连接运算符 (".") , 它返回其左右参数链接后的字符串. 第二个是链接赋值运算符 (".=") , 它将右边参数附加到左边的参数之后
Note: 点操作符和 + 优先级一样 根据从左到右运算
####数组运算符
例子 | 名称 | 结果 |
---|---|---|
$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 |
+
运算符把右边的数组元素附加到左边的数组后面, 两个数组都有键名, 则只用左边数组中的,右边的被忽略
array_merge()
先保留第一个数组的所有元素,如果后面数组的元素与前面数组的元素关联下标相同则用后面数组的元素覆盖前面的;如果是索引下标,直接添加到第一个数组后面,重置索引
数组中的单元如果具有相同的键名和值则比较时相等
####类型运算符 instanceof 用于确定一个 PHP 变量是否属于某一种 class 的实例
instanceof 也可用来确定一个变量是不是继承自某一父类的子类的实例
检查一个对象是否不是某个类的实例, 可以使用逻辑运算符 not
instanceof 也可用于确定一个变量是不是实现了某个接口的对象实例
虽然 instanceof 通常直接与类名一起使用, 但也可以使用对象或字符串变量
如果被检测的变量不是对象, instanceof 并不发出任何错误信息而是返回 FALSE. 不允许用来检测常量
然而 instanceof 的使用还有一些陷阱必须了解. 在 PHP 5.1.0 之前, 如果要检查的类名称不存在, instanceof 会调用 __autoload()
. 另外, 如果该类没有被装载则会产生一个致命错误. 可以通过使用动态类引用或用一个包含类名的字符串变量来避开这种问题
instanceof 运算符是 PHP 5 引进的. 在此之前用 is_a(), 但是后来 is_a() 被废弃而用 instanceof 替代了. 注意自 PHP 5.3.0 起, 又恢复使用 is_a()了
Note: instanceof 可以用来比较两个实例是否属于同一个类
###流程控制 ####简介 任何 PHP 脚本都是由一系列语句构成的. 一条语句可以是一个赋值语句, 一个函数调用, 一个循环, 一个条件语句或者甚至是一个什么也不做的语句 (空语句) . 语句通常以分号结束. 此外, 还可以用花括号将一组语句封装成一个语句组. 语句组本身可以当作是一行语句
####if if结构是很多语言包括 PHP 在内最重要的特性之一, 它允许按照条件执行代码片段
经常需要按照条件执行不止一条语句, 当然并不需要给每条语句都加上一个 if 子句. 可以将这些语句放入语句组中.
if 语句可以无限层地嵌套在其它 if 语句中, 这给程序的不同部分的条件执行提供了充分的弹性.
####else 经常需要在满足某个条件时执行一条语句, 而在不满足该条件时执行其它语句, 这正是 else 的功能. else 延伸了 if 语句, 可以在 if 语句中的表达式的值为 FALSE 时执行语句
else 语句尽在 if 以及 elseif (如果有的话) 语句中的表达式的值为 FALSE 时执行
####elseif/else if elseif , 和此名称暗示的一样, 是 if 和 else 的组合. 和 else 一样, 它延伸了 if 语句, 可以在原来的 if 表达式值为 FALSE 时执行不同语句. 但是和 else 不一样的是, 它尽在 elseif 的条件表达式值为 TRUE 时执行语句
在同一个 if 语句中可以有多个 elseif 部分, 其中第一个表达式值为 TRUE (如果有的话) 的 elseif 部分将会执行. 在 PHP 中, 也可以写成 else if (两个单词) , 它和 elseif (一个单词) 的行为完全一样
elseif 的语句仅在之前的 if 和所有之前 elseif 的表达式值为 FALSE, 并且当前的 elseif 表达式值为 TRUE 时执行.
Note: 必须要注意的是 elseif 与 else if 只有在使用花括号的情况下才认为是完全相同. 如果用冒号来定义 if/elseif 条件, 那就不能用两个单词的 else if , 否则 PHP 会产生解析错误
####流程控制的替代语法 PHP 提供了一些流程控制的替代语法, 包括 if, while, for, foreach 和 switch. 替代语法的基本形式是把左花括号 ({) 换成冒号 (:) , 把右花括号 (}) 分别换成 endif; , endwhile; , endfor; , endforeach; 以及 endswitch; .
Note: 不支持在同一个控制块内混合使用两种语法
Note: 替代语法只使代码更清晰,容易阅读 效率并没有原生的高
####while While 循环是 PHP 中最简单的循环类型
while 语句的含义很简单, 它告诉 PHP 只要 while 表达式的值为 TRUE 就重复执行嵌套中的循环语句. 表达式的值在每次开始循环时检查, 所以即使这个值在循环语句中改变了, 语句也不会停止执行, 直到本次循环结束. 有时候如果 while 表达式的值一开始就是 FALSE , 则循环语句一次都不会执行
####do-while do-while 循环和 while 循环非常相似, 区别在于表达式的值是在每次循环结束时检查而不是开始时. 和一般的 while 循环主要的区别是 do-while 的循环语句保证会执行一次 (表达式的真值在每次循环结束后检查) , 然而在一般的 while 循环中就不一定了 (表达式真值在循环开始时检查,如果一开始就为 FALSE 则整个循环立即终止)
####for for 循环是 PHP 中最复杂的循环结构
for (expr1; expr2; expr3)
statement
第一表达式 (expr1) 在循环开始前无条件求值 (并执行) 一次
expr2 在每次循环开始前求值. 如果值为 TRUE, 则继续循环, 执行嵌套的循环语句. 如果值为 FALSE , 则终止循环
expr3 在每次循环之后被求值 (并执行)
每个表达式都可以为空或包括逗号分隔的多个表达式. 表达式 expr2 中, 所有用逗号分隔的表达式都会计算, 但只取得最后一个结果. expr2 为空意味这将无限循环下去 (和 C 一样, PHP 暗中认为其值为 TRUE) . 这可能不像想象中那样没有用, 因为经常会希望用有条件的 break 语句来结束循环而不是用 for 的表达式真值判断
####foreach foreach 语法结构提供了遍历数组的简单方式. foreach 仅能够应用与数组和对象, 如果尝试应用于其它数据类型的变量, 或者未初始化的变量将发出错误信息.
Note: 当 foreach 开始执行时, 数组内部的指针会自动指向第一个单元. 这意味着不需要在 foreach 循环之前调用 reset().
由于 foreach 依赖内部数组指针, 在循环中修改其值将可能导致意外的行为
Warning 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留. 建议使用 unset() 来将其销毁.
Note: foreach 不支持用"@"来抑制错误信息的能力
#####用 list() 给嵌套的数组解包 PHP 5.5 增添了遍历一个数组的数组的功能并且把嵌套的数组解包到循环变量中, 只需将 list() 作为提供
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a, $b)) {
// $a contains the first element of the nested array,
// and $b contains the second element.
echo "A: $a; B: $b\n";
}
list() 中的单元可以少于嵌套数组的, 此时多出来的数组单元将被忽略
如果 list() 中列出的单元多余嵌套数组则会发出一条信息级别的错误信息
####break break 结束当前 for, foreach, while, do-while 或者 switch 结构执行
break 可以接受一个可选的数字参数来决定跳出几重循环
PHP 5.4.0 break 0; 不再合法. 这在之前的版本被解析为 break 1; 取消变量作为参数传递 (例如 $num = 2; break $num;)
当 break 在脚本上 而不是流程控制里 执行时 将会终止脚本的执行
####continue continue 在循环结构里用来跳过本次循环中剩余的代码并在条件求值为真时开始执行一下次循环
Note: 注意在 PHP 中 switch 语句被认为是可以使用 continue 的一种循环结构
continue 接受一个可选的数字参数来决定跳过几重循环到循环结尾. 默认值是 1, 即跳到当前循环末尾
PHP 5.4.0 break 0; 不再合法. 这在之前的版本被解析为 break 1; 取消变量作为参数传递 (例如 $num = 2; break $num;)
####switch switch 语句类似与具有同一个表达式的一系列 if 语句. 很多场合下需要把同一个变量 (或表达式) 与很多不同的值比较, 并根据它等于哪个值来执行不同的代码. 这正是 switch 语句的用途
Note: 注意和其他语言不同, continue 语句作用到 switch 上的作用类似于 break. 如果在循环中有一个 switch 并希望 continue 到外层循环中的下一轮循环, 用 continue 2
Note: 注意 switch/case 做的是松散比较
为避免错误, 理解 switch 是怎样执行的非常重要. switch 语句一行接一行地执行 (实际上是语句接语句). 开始没有代码被执行. 仅当一个 case 语句中的值和 switch 表达式的值匹配时 PHP 才开始执行语句, 知道 switch 的程序段结束或者遇到第一个 break 语句为止. 如果不在 case 的语句段最后协商 break 的话, PHP 将据需执行一下个 case 中的语句段.
在 switch 语句中条件只求值一次并用来和每个 case 语句比较. 在elseif 语句中条件会在此求值. 如果条件比一个简单的比较要复杂得多或者在一个很多次的循环中, 那么用 switch 语句可能会快一些
在一个 case 中的语句也可以为空, 这样只不过将控制转移到了下一个 case 中的语句
一个 case 的特例是 default . 它匹配了人格和其它 case 都不匹配的情况
case 表达式可以是任何值为简单类型的表达式, 即整型或浮点数以及字符串. 不能用数组或对象, 除非它们被解除引用成为简单类型
####declare declare结构用来设定一段代码的执行指令
declare (directive)
statement
directive 部分允许设定 declare 代码段的行为. 目前只认识两个指令: ticks 以及 encoding
Note: encoding 是 PHP 5.3.0 新增指令
declare 代码段中的 statement 部分将被执行--怎样执行以及执行中有什么副作用出现取决于 directive 中设定的指令
declare 结构也可用于全局范围, 影响到其后的所有代码 (但如果 declare 结构的文件被其它文件包含, 则对包含它的父文件不起作用)
#####Ticks Tick (时钟周期) 是一个在 declare 代码段中解释器没执行 N 可计时的低级语句就会发生的时间. N 的值实在 declare 中的 directive 部分用 ticks = N 来指定的.
不是所有语句都可计时. 通常条件表达式和参数表达式都不可计时
在每个 tick 中出现的事件是由 register_tick_function() 来指定的
function tick_handler()
{
static $a=0;
echo ++$a,'<br>';
}
register_tick_function('tick_handler');
declare(ticks=1){
echo ' a '; //a 1
echo ' b '; //b 2
}
#####Encoding 可以用 encoding 指令来对每段脚本制定其编码方式
declare(encoding = 'ISO-8859-1');
// code here
Caution 当和命名空间结合起来时 declare 的唯一合法语法是 declare(encoding='...'); , 其中 ...是编码的值. 而 declare(encoding='...'){} 将在与命名空间结合时产生解析错误
在 PHP 5.3 中除非在编译时制定了 --enable-zend-multibyte, 否则 declare 中的 encoding 值会被忽略.
注意除非用 phpinfo() , 否则 PHP 不会显示出是否在编译时指定了 --enable-zend-multibyte
这个功能应该可用与 项目的代码分析
####return 如果在一个函数中调用 return 语句, 将立即结束此函数的执行并将它的参数作为函数的值返回. return 也会终止 eval() 语句或者脚本文件的执行
如果在全局范围中调用, 则当前脚本文件中止运行. 如果当前脚本文件是被 include 的或者 require 的, 则控制交回调用文件. 此外, 如果当前脚本是被 include 的, 则return 的值会被当作 include 调用的返回值. 如果在主脚本文件中调用 return, 则脚本中止运行. 如果当前脚本文件是在 php.ini 中的配置选项 auto_prepend_file 或者 auto_append_file 所指定的, 则此脚本文件中止运行
Note: 注意既然 return 是语言结构而不是函数, 因此其参数没有必要用括号将其括起来. 通常都不用括号, 实际上也应该不会, 这样可以降低 PHP 负担
Note: 如果没有提供参数, 则一定不能用括号, 此时返回 NULL. 如果调用 return 时加上了括号却没有参数会导致解析错误
Note: 当引用返回值时永远不要使用括号, 这样行不通. 只能通过引用返回变量, 而不是语句的结果. 如果使用 return ($a); 时其实不是返回一个变量, 而是表达式 ($a) 的值 (当然, 此时该值也正是 $a 的值)
####include include 语句包含并运行指定文件
被包含文件先按参数给出的路径寻找, 如果灭有给出目录 (只要文件名) 时则按照 include_path 制定的目录寻找. 如果在 include_path 下没有找到该文件则 include 最后才在调用脚本文件所在的目录和当前工作目录下寻找. 如果最后仍未找到文件则 include 结构会发出一条警告; 这一点和 require 不同, 后者会发出一个致命错误
如果定义了路径--不管是绝对路径 (在Windows 下以盘符或者 \ 开头, 在 Unix/Linux 下以 / 开头) 还是当前目录的相对路径 (以 . 或者 .. 开头) -- include-path 都会被完全忽略
当一个文件被包含时, 其中所包含的代码继承了 include 所在行的变量范围. 从该处开始, 调用文件在该行处可用的任何变量在被调用的文件中也都可用. 不过所有在包含文件中定义的函数和类都具有全局作用域
如果 include 出现与调用文件中的一个函数里, 则被调用的文件中所包含的所有代码将表现得如同它们实在该函数内部定义的一样. 所以它将遵循该函数的变量范围. 此规则的一个例外是魔术常量, 它们是在发生包含之前就已经被解析器处理的
当一个文件被包含时, 语法解析器在目标文件的开头脱离 PHP 模式并进入 HTML 模式, 到文件结尾处恢复. 由于此原因, 目标文件中需要作为 PHP 代码执行的任何代码都必须被包括在有效的 PHP 起始和结束标记之中
要在脚本中自动包含文件,参见 php.ini 中的 auto_prepend_file 和 auto_append_file 配置选项。
####require require 和 includ 几乎完全一样, 除了处理失败的方式不同之外. require 在出错时产生 E_COMPILE_ERROR 级别的错误. 换句话说将导致脚本中止而 include 只产生警告 (E_WARNING) , 脚本会继续运行
####include_once include_once 语句在脚本执行期间包含并运行指定文件. 此行为和 include 语句类似, 唯一区别是如果该文件中已经被包含过, 则不会再次包含. 如同此语句名字暗示的那样, 只会包含一次
include_once 可以用于在脚本执行期间用一个文件有可能被包含超过一次的情况下, 想确保它只被包含一次以避免函数重定义, 变量重新赋值等问题
####require_once require_once 语句和 require 语句完全相同, 为一个区别是 PHP 会检查该文件是否已经被包含过, 如果是则不会再次包含
####goto goto 操作符可以用来跳转到程序中的另一个位置. 该目标位置可以用目标名称加上冒号来标记, 而跳转指令是 goto 之后接上目标位置的标记. PHP 中的 goto 有一定限制, 目标位置只能位于同一个文件和作用域, 也就是说无法跳出一个函数或类方法, 也无法跳入到另一个函数. 也无法跳入到任何循环或者 switch 结构中. 可以跳出循环或者 switch, 通常的用法是用 goto 代替多层的 break
Note: goto 操作符尽在 PHP 5.3 及以上版本有效
Note: goto 不能使用 字符串作为要跳转的目标
###函数 ####用户自定义函数 任何有效的 PHP 代码都有可能出现在函数内部, 甚至包括其它函数和类定义
函数无需调用之前被定义. 除非函数是有条件被定义时, 必须在调用函数之前定义
PHP 中的所有函数和类都具有全局作用域, 可以定义在一个函数之内而在之外调用, 反之亦然
PHP 不支持函数重载, 也不可能取消定义或者重定义已声明的函数
Note: 函数名是大小写无关的, 不过在调用函数的时候, 使用其在定义时相同的形式是个好习惯
在 PHP 中可以调用递归函数
Note: 但是要避免递归函数/方法调用超过 100-200 层, 因为可能会使堆栈崩溃从而使当前脚本终止. 无限递归可视为编程错误
####函数的参数 通过参数列表可以传递信息到函数, 即以逗号作为分隔符的表达式列表. 参数是从左向右求值的.
#####通过引用传递参数 默认情况下, 函数参数通过值传递 (因而即使在函数内部改变参数的值, 它并不会改变函数外部的值) . 如果希望允许函数修改它的参数值, 必须通过引用传递参数
如果想要函数的一个参数总是通过引用传递, 可以在函数定义中该参数的前面加上符号 &
#####默认参数的值 函数可以定义 C++ 风格的标量参数默认值
PHP 还允许使用数组 array 和特殊类型 NULL 作为默认参数
默认值必须是常量表达式, 不能是诸如变量, 类成员, 或者函数调用等
注意当使用默认参数时, 任何默认参数必须放在任何非默认参数的右侧; 否则, 函数将不会按照预期的情况工作
Note: 自 PHP 5起, 传引用的参数也可以有默认值
#####类型声明 Note: 类型声明也称为烈性提示在 PHP 5
类型声明允许函数需要某种类型的参数调用. 如果给定的值是不正确的类型的, 然后生成一个错误 在 PHP 5中,这将算是一个可恢复的致命错误, PHP 7将抛出一个 TypeError异常
指定一个类型声明, 类型名称应该添加在参数名称之前. 声明可以接受 NULL 值如果参数的默认值设置为 NULL
有效的验证
类型 | 描述 | PHP最低版本 |
---|---|---|
Class/interface name | 参数必须是给定的类或接口的实例 | PHP 5.0.0 |
array | 参数必须是数组 | PHP 5.1.0 |
callable | 参数必须是回调函数 | PHP 5.4.0 |
bool | 参数必须是布尔值 | PHP 7.0.0 |
float | 参数必须是浮点数 | PHP 7.0.0 |
int | 参数必须是整型 | PHP 7.0.0 |
string | 参数必须是字符串 | PHP 7.0.0 |
严格类型 默认情况下, PHP会将错误类型的值强制转换为期望的标量类型
可以在每个文件的基础上启用严格模式. 在严格模式下, 只接受类型声明的精确类型的变量, 否则将抛出TypeError. 该规则的唯一例外是可以给予期望浮点的函数一个整数
要启用严格模式, declare 语句与 strict_types 声明一起使用:
Caution 启用严格模式也会影响返回类型声明
Note: 严格输入适用与函数调用由内部文件启用了严格的类型, 而不是该文件中声明的函数. 如果没有严格的文件类型启用调用一个函数, 定义在一个文件中有严格的类型, 将遵守调用的优先级 (弱类型) , 并且将该值强制
Note: 严格的类型只是为标量类型声明, 定义, 因此, 需要 PHP 7.0.0 或之后, 标量类型声明添加版本
#####可变数量的参数列表
PHHP 在用户自定义函数中支持可变数量的参数列表. 在 PHP 5.6 级以上的版本中, 由...
语法实现;在 PHP 5.5 即更早版本中, 使用函数 func_num_args(), func_get_arg() 和 func_get_args()
** ...in PHP 5.6+ ** 在 PHP 5.6 和以后, 参数列表可能包括 ...令牌表示该函数接受可变数量的参数. 参数将作为数组传递给给定的变量
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4); //10
你还可以使用 ...当调用函数将一个数组或可反驳的变量或文字到参数列表
function add($a, $b) {
return $a + $b;
}
echo add(...[1, 2])."\n"; //'3 \n'
$a = [1, 2];
echo add(...$a); //3
####返回值 只通过使用可选的返回语句返回. 可以返回包括数组和队形的任意类型. 返回语句会立即中止函数的运行, 并且将控制权交回调用该函数的代码行
Note: 如果省略了 return , 则返回值为 NULL
#####return的使用 函数不能返回多个值, 但可以通过返回一个数组来得到类似结果
从函数返回一个引用, 必须在函数声明和指派返回值给一个变量时都使用引用运算符 &
PHP 7增加对返回类型声明的支持
function sum($a, $b): float {
return $a + $b;
}
// Note that a float will be returned.
var_dump(sum(1, 2));
####可变函数 PHP 支持可变函数的概念. 这意味这如果一个变量名后又圆括号, PHP 将寻找与变量的值同名的函数, 并且尝试执行它. 可变函数可以用来实现包括回调函数, 函数表在内的一些用途
也可以用可变函数的语法来调用一个对象的方法
当调用静态方法时, 函数调用要比静态属性优先
####内部 (内置) 函数 PHP 有很多标准的函数和结构. 还有一些函数需要和特定地 PHP 扩展模块一起变异, 否则在使用它们的时候就会得到一个致命的"未定义函数"错误. 例如, 要是用 image 函数中的 imagecreatetruecolor(), 需要在编译 PHP 的时候加上 GD 的支持. 或者, 要使用 mysql_connect() 函数, 就需要在编译 PHP 的时候加上 MySQL 支持. 有很多核心函数已包含在每个版本的 PHP 中. 如字符串和变量函数. 调用 phpinfo() 或者 get_loaded_extensions() 可以得知 PHP 加载了那些扩展库. 同时还应该注意, 很多扩展库默认就是有效的. PHP 手册按照不同的扩展库组织了它们的文档.
Note: 如果传递给函数的参数类型与实际的类型不一致, 例如将一个 array 传递给一个 string 类型的变量, 那么函数的返回值是不确定的. 在这种情况下, 通常函数会返回 NULL. 但这仅仅是一个惯例, 并不一定如此
####匿名函数 匿名函数 (Anoymous functions) , 也叫闭包函数 (closures), 允许临时创建一个没有指定名称的函数. 最经常用作回调函数 (callback) 参数的值
echo preg_replace_callback('~-([a-z])~', function ($match) {
return strtoupper($match[1]);
}, 'hello-world');// 输出 helloWorld
闭包函数也可以作为变量的值来使用. PHP 会自动把此种表达式转换成内置类 Closure 的对象实例. 把一个 closure 对象赋值给一个变量的方式与普通变量赋值的语法是一样的, 最后也要加上分号
闭包可以从父作用域中继承变量. 任何此类变量都应该用 use 语言结构传递进去
$message = 'hello';
$example = function () use ($message) {
var_dump($message);
};
echo $example();
这些变量的必须在函数或类的头部声明. 从父作用域中继承变量与使用全局变量是不同的. 全局变量存在于一个全局的范围, 无论当前在执行的是哪个函数. 而闭包的父作用域是定义该闭包的函数 (不一定是调用它的函数)
匿名函数目前是通过 Closure 类来实现的 PHP 5.3 可以使用匿名函数 PHP 5.4 $this 可用于匿名函数
Note: 可以在闭包中使用 func_num_args() , func_get_arg() 和 func_get_args()
###类与对象 ####简介 自 PHP 5起完全重写了对象模型以得到更佳性能和更多特性. 这是自 PHP 4 依赖的最大变化. PHP 5具有完整的对象模型
PHP 5中的新特性包括访问控制, 抽象类和 final 类与方法, 附加的魔术方法, 接口, 对象复制和类型约束
PHP 对待对象的方式与引用和句柄相同, 即每个变量都持有对象的引用, 而不是整个对象的拷贝
####基本概念 #####class 每个类的定义都以关键字 class 开头, 后面跟着类名, 后面跟着一对花括号, 里面包含有类的属性与方法的定义
一个类可以包含有属于自己的常量, 变量 (称为"属性") 以及函数 (称为"方法")
当一个方法在类定义内部被调用时, 有一个可用的伪变量$this
.$this
是一个到主叫对象的引用 (通常是该方法所从属的对象, 但如果是从第二个对象静态调用时也可能是另一个对象) ==PHP 5 和 PHP 7 有点差异==
#####new 要创建一个类的实例, 必须使用 new 关键字. 当创建新对象时该对象总是被赋值, 除非该对象定义了构造函数并且在出错时抛出了一个异常. 类应在被实例化之前定义 (某些情况下则必须这样)
如果在 new 之后跟着的是一个包含有类名的字符串, 则该类的一个实例被创建. 如果该类属于一个名字空间, 则必须使用其完整名称
在类定义内部, 可以用 new self , new parent 和 new static 创建新对象
当把一个对象已经创建的实例赋给一个新变量时, 新变量会访问同一个实例, 就和用该对象赋值一样. 此行为和给函数传递入实例时一样. 可以用克隆给一个已创建的对象建立一个新实例
#####extends 一个类可以在声明中用 extends 关键字继承另一个类的方法和属性. PHP 不支持多重继承, 一个类只能继承一个基类
被继承的方法和属性可以通过用同样的名字重新声明被覆盖. 但是如果父类定义方法时用了 final, 则该方法不可被覆盖. 可以通过 parent:: 来访问被覆盖的方法或属性
当覆盖方法时, 参数必须保持一致否则 PHP 将发出 E_STRICT 级别的错误信息. 但结构函数例外, 结构函数可在被覆盖时使用不同的参数
#####::class 自 PHP 5.5起, 关键词 class 也可用用于类名的解析. 使用 ClassName::class 你可以获取一个字符串, 包含了类 ClassName 的完全限定名称. 这对使用了 命名空间的类尤其有用
####属性 类的变量成员叫做"属性", 或者叫"字段" "特征". 属性声明是由关键字 public, protected 或者 private 开头, 然后跟一个普通的变量声明来组成. 属性中的变量可以初始化, 但是初始化的值必须是常数, 这里的常数是指 PHP 脚本在编译阶段时可以得到其值, 而不依赖于运行时的信息才能求值
Note: 为了向后兼容 PHP 4, PHP 5 声明属性依然可以直接使用关键字 var 来替代 (或者附加于) public, protected 或 private. 但是已不在需要 var 了. 在 PHP 5.0 到 5.1.3 , var 会被认为是废弃的, 而且抛出 E_STRICT 警告, 但是 5.1.3 之后就不再任务是废弃, 也不会抛出警告
如果直接使用 var 声明属性, 而没有用 public, protected 或 private 之一, PHP 5 会将视为 public
在类的成员方法里面, 可以用 -> (对象运算符) : $this->property (其中 property 是该属性名) 这种方式来访问非静态属性. 静态属性则是用 ::(双冒号) self::$property 来访问
更 heredocs 不同, nowdocs 可在任何金泰数据上下文中使用, 包括属性声明 (PHP 5.3.0)
####类常量
可以把在类中始终保持不变的值定义为常量. 在定义和使用常量的时候不需要使用 $
符号
常量的值必须是一个定值, 不能是变量, 类属性, 数学运算的结果或函数调用.
接口(interface) 中也可以定义常量
自 PHP 5.3.0 起, 可以用一个变量来动态调用类. 但该变量的值不能为关键字
const constant = 'constant value';
####自动加载类 很多开发者写面向对象的应用程序时对每个类的定义建立一个 PHP 源文件. 一个很大的烦恼是不得不在每个脚本开头写一个长长的包含文件列表 (每个类一个文件)
在 PHP 5中, 不在需要这样了. 可以定义一个 __autoload()
函数, 它会在视图使用尚未被定义的类时自动调用. 通过调用此函数, 脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类
Tip sql_autoload_register() 提供了一种更加灵活的方式来实现类的自动加载. 因此, 不在建议使用 __autoload() 函数, 在以后的版本中它可能被弃用
Note: 在 5.3.0 版之前, __autoload 函数抛出的异常不能被 catch 语句块捕获并会导致一个致命错误. 从 5.3.0+ 之后, __autoload 函数抛出的异常可以被 catch 语句块捕获, 但需要遵循一个条件. 如果抛出的是一个自定义异常, 那么必须存在相应的自定义异常类. __autoload 函数可以递归的自动加载自定义异常类
Note: 自动加载不可用于 PHP 的 CLI 交互模式
Note: 如果类名比如被用于 call_user_func(), 则它可能包含一些危险的字符, 比如 ../ 建议你在这样的函数中不要使用用户的输入,起码需要在 __autoload() 时验证输入
####构造函数和析构函数 #####构造函数
void __construct([ mixed $args [, $...]])
PHP 5 允许开发者在于一个类中定义一个方法作为构造函数. 具有构造函数的类会在每次创建新对象时先调用此方法, 所以非常适合在使用对象之前做一些初始化工作
Note: 如果子类中定义了构造函数则不会隐式调用其父类的构造函数.要执行父类的构造函数, 需要在子类的构造函数中调用 parent::__construct() . 如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承 (假如没有被定义为 private 的话)
为了实现向后兼容性, 如果 PHP 5在类中找不到 __construct()
函数并且也没有从父类继承一个的话, 它就会尝试寻找旧式的构造函数, 也就是和类同名的函数. 因此唯一会产生兼容性问题的情况是: 类中已有一个名为 __construct()
的方法却被用于其它用途时.
与其它方法不同, 当__construct()
被与父类 __construct()
具有不同参数的方法覆盖时, PHP 不会产生一个 E_STRICT 错误信息
自 PHP 5.3.3 起, 在命名空间中, 与类名同名的方法不再作为构造函数. 这一改变不影响不再命名空间中的类
#####析构函数
void __destruct ( void )
PHP 5引入了析构函数的概念, 这类似与其它面向对象的语言, 如 C++. 析构函数会在到某个对象的所有引用都被删除或者当对象被显示销毁时执行
和构造函数一样, 父类的析构函数不会被引擎暗中调用. 要执行父类的析构函数, 必须在子类的析构函数体中显式调用 parent::__destruct()
此外也和构造函数一样, 子类如果自己没有定义析构函数则会继承父类的
析构函数及时在使用 exit() 终止脚本运行时也会被调用. 在析构函数中调用 exit() 将会终止其余关闭操作的运行
Note: 析构函数在脚本关闭时调用, 此时所有的 HTTP 头信息已经发出. 脚本关闭时的工作目录有可能和在 SAPI (如 apache) 中时不同
Note: 试图在析构函数 (在脚本终止时被调用) 中抛出一个异常会导致致命错误
####访问控制 (可见性) 对属性或方法的访问控制, 是通过在前面添加关键字 public (公有) , protected (受保护) 或 private (私有) 来实现的. 被定义为公有的类成员可以在任何地方被访问. 被定义为受保护的类成员则可以被其自身以及其子类和父类访问. 被定义为私有的类成员则只能被其定义所在的类访问
#####属性的访问控制 类属性必须定义为公有, 受保护, 私有之一. 如果用 var定义, 则被视为公有
Note: 为了兼容性考虑, 在 PHP 4中使用 var 关键字对变量进行定义方法在 PHP 5中仍然有效 (只是作为 public 关键字的一个别名) . 在 PHP 5.1.3 之前的版本, 该语法会产生一个 E_STRICT 警告
#####方法的访问控制 类中的方法可以被定义为公有, 私有或受保护. 如果没有设置这些关键字, 则该方法默认为公有
#####其它对象的访问控制 同一个类的对象即使不是同一个实例也可以互相访问对方的私有与受保护成员. 这是由于这些对象的内部具体实现的细节都是已知的
####对象继承 继承将会影响到类与类, 对象与对象之间的关系
比如, 当扩展一个类, 子类就会继承父类所有公有的和受保护的方法. 除非子类覆盖了父类的方法, 被继承的方法都会保留其原有功能
继承对于功能的设计和抽象是非常有用的, 而且对于类似的对象增加新功能就无须重新写这些公用的功能
Note: 除非使用了自动加载, 否则一个类必须在使用之前被定义. 如果一个类扩展了另一个, 则父类必须在子类之前被声明. 此规则适用于类继承其它类与接口
####范围解析操作符 (::) 范围解析操作符 (也可称作 Paamayim Nekudotayim) 或者更简单地说是一对冒号, 可用于访问静态成员, 类常量, 还可以用用于覆盖类中的属性和方法
当在类定义之外引用到这些项目时,要使用类名
自 PHP 5.3.0 起, 可以用过变量来引用类, 该变量的值不能是关键字 (如 self, paret 和 static)
把 Paamayim Nekudotayim 选作双冒号操作符的名字似乎有些奇怪. 然而, 这是 Zend 开发小组在写 Zend Engine 0.5 (被用于 PHP 3中) 时所作出的决定. 事实上这个词在希伯莱文就是双冒号的意思
self, parent 和 static 这三个特殊的关键字适用于在类定义的内部对其它属性或方法进行访问的
当一个子类覆盖其父类中的方法时, PHP 不会调用父类中已被覆盖的方法. 是否调用父类的方法取决于子类. 这种机制也作用于构造函数和析构函数, 重载以及魔术方法
类常量, 静态类属性 和 静态类函数 都可以共享相同的名称和使用双冒号访问
####Static (静态) 关键字 声明类属性或方法为静态, 就可以不实例化类而直接访问. 静态属性不能通过一个类已实例化的对象来访问 (但静态方法可以)
为了兼容 PHP 4, 如果没有指定访问控制, 属性和方法默认为公有
由于静态方法不需要通过对象即可调用, 所以伪变量$this
在静态方法中不可用
静态属性不可以有对象通过 -> 操作符来访问
静态方式调用一个非静态方法会到一个 E_STRICT 级别的错误
就像其它所有的 PHP 静态变量一样, 静态属性只能被初始化为文字或常量, 不能使用表达式. 所以可以把静态属性初始化为整数或数组, 但不能初始化为另一个变量多函数返回值, 也不能指向一个对象
自 PHP 5.3.0起, 可以用一个变量来动态调用类. 但该变量的值不嗯给你为关键字 self, parent 或 static
####抽象类 PHP 5 支持抽象类和抽象方法. 定义为抽象的类不能被实例化. 任何一个类, 如果它里面至少有一个方法是被声明为抽象的, 那么这个类就必须被声明为抽象的. 被定义为抽象的方法只是声明了其调用方式 (参数) , 不能定义其具体的功能实现
继承一个抽象类的时候, 子类必须定义父类中的所有抽象方法; 另外, 这些方法的访问控制必须和父类中一样 (或者更为宽松) . 例如露个抽象方法被声明为受保护的, 阿姆子类中实现的方法就应该声明为受保护的或者公有的, 而不能定义为私有的. 此外方法的调用方式必须匹配, 即类型和所需参数数量必须一致. 例如, 子类定义了一个可选参数u, 而父类抽象方法的声明里没有, 则两者的声明并无冲突. 这也适用于 PHP 5.4 起的构造函数. 在 PHP 5.4 之前的构造函数声明可以不一样的
abstract class AbstractClass
{
// 强制要求子类定义这些方法
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// 普通方法(非抽象方法)
public function printOut() {
print $this->getValue() . "\n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
老代码中如果自定义类或函数被命名为 "abstract", 则应该能不加修改地正常运行
####对象接口 使用接口 (interface) , 可以指定某个类必须实现那些方法, 但不需要定义这些方法的具体内容
接口是通过 interface 关键字来定义的, 就像定义一个标准的类一样, 但其中定义所有的方法都是空的
接口中定义的所有方法都必须是公有, 这是接口的特性
#####实现 (implements) 要实现一个接口, 使用 implements 操作符. 类中必须实现接口中定义的所有方法, 否则会博爱一个致命错误. 类可以实现多个接口, 用逗号来分隔多个接口的名称
Note: 实现多个接口时, 接口中的方法不能有重名
Note: 接扩也可以继承, 通过使用 extends 操作符
Note: 类要实现接口, 必须使用和接口中所定义的方法完全一致的方式. 否则会导致致命错误
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
class Template implements iTemplate
{
public function setVariable($name, $var){}
public function getHtml($template){}
}
#####常量 接口中也可以定义常量. 接口常量和类常量的使用完全相同, 但是不能被子类或子接口所覆盖
接口加上类型约束, 提供了一种很号的方式来确保某个对象包含有某些方法
####Trait 自 PHP 5.4.0 起, PHP 实现了一种代码复用的方法, 称为 trait
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制. Trait 为了减少单继承语言的限制, 使开发人员能够自由地在不同层次结构内独立的复用 method. Trait 和 Class 组合的语言定义了一种减少复杂性的方式, 避免传统多继承和 Mixin 类相关典型问题
Trait 和 Class 相似, 但仅仅指在用细粒度和一致的方式来组合功能. 无法通过 trait 自身来实例化. 它为传统继承增加了水平特性的组合; 也就是说, 应用的几个 Class 之间不需要继承
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
#####优先级 从基类继承的成员会被 trait 插入的成员所覆盖. 优先顺序是来自当前类的成员覆盖了 trait 的方法, 而 trait 则覆盖了被继承的方法
#####多个trait 通过逗号分隔, 在 use 声明列出多个 trait , 可以都插入到一个类中
#####冲突的解决 如果两个trait都插入一个同名的方法, 如果没有明确解决冲突将会产生一个致命错误
为了解决多个 trait 在用一个类中的命名冲突, 需要使用 instradof 操作符来明确使用冲突方法中的哪一个
以上方式仅允许排除掉其它方法, as 操作符可以将其中一个冲突的方法以另一个名称来引入
A::bigTalk insteadof B;
B::bigTalk as talk;
#####修改方法的访问控制 使用 as 语法还可以用来调整方法的访问控制
use HelloWorld { sayHello as protected; }
use HelloWorld { sayHello as private myPrivateHello; }
#####从trait 来组成 trait 正如class 能够使用 trait 一样, 其它 trait 也能够使用 trait. 在 trait 定义时通过使用一个或多个 trait, 能够组合其它 trait 中的部分或全部成员
#####Trait 的抽象成员 为了对使用的类施加强制要求, trait 支持抽象方法的使用
trait Hello {
abstract public function getWorld();
}
#####Trait 的静态成员 Traits 可以被静态成员静态方法定义
#####属性 Trait 同样可以定义属性
如果 trait 定义了一个属性, 那类将不能定义同样名称的出行, 否则会产生一个错误. 如果该属性在类中的定义与在 trait 中的定义兼容 (同样的可见性和初始值) 则错误的级别是 E_STRICT, 否则是一个致命错误
####匿名类 PHP 7 开始支持匿名类. 匿名类很有用, 可以创建一次性的简单对象
$util->setLogger(new class {
public function log($msg)
{
echo $msg;
}
});
可以传递参数到匿名类的构造器, 也可以扩展 (extend) 其它类, 实现接口 (implement interface) , 以及像其它普通的类一样使用 trait
匿名类被嵌套进普通 Class 后, 不能访问这个外部类的 priveate , protected 方法或者属性. 为了访问外部类 protected 属性或方法, 匿名类可以 extend 此外部类. 为了使用外部类的 private 属性, 必须通过构造器传进来
####重载 PHP 所提供的"重载" (overloading) 是指动态地"创建"类属性和方法. 我们是通过魔术方法 (magicmethods) 来实现的
当调用当前环境下未定义或不可见的类属性方法时, 重载方法会被调用
所有的重载方法都必须被声明为 public
Note: 这些魔术方法的参数都不能通过引用传递
Note: PHP中的"重载"与其它绝大多数面向对象语言不同. 传统的"重载"适用于提供多个同名的类方法, 但各方法的参数类型和个数不同
PHP 5.3.0 新增 __callStatic() 魔术方法. 可见性未设置为 public 或未声明为 static 的时候会产生一个警告
PHP 5.1.0 新增 __isset() 和 __unset() 两个魔术方法
#####属性重载
//在给不可访问属性赋值时 调用
public void __set ( string $name , mixed $value )
//读取不可访问属性的值时 调用
public mixed __get ( string $name )
//当对不可访问属性调用 isset() 或 empty() 时 调用
public bool __isset ( string $name )
//当对不可访问属性调用 unset() 时 调用
public void __unset ( string $name )
//参数 $name 是指要操作的变量名称. __set() 方法的 $value 参数指定了参数指定了 $name 变量的值
属性重载只能在对象中进行. 在静态方法中, 这些魔术方法将不会被调用. 所以这些方法都不能被声明为 static. 从PHP 5.3.0 起, 将这些魔术方法定义为 static 会产生一个警告
Note: 因为 PHP 处理赋值运算的方式, __set() 的返回值将会被忽略. 类似的, 在这样的链式赋值中, __get() 不会被调用 $a = $obj -> b = 8
Note: 在除 isset() 外的其它语言结构中无法使用重载的属性, 这意味这当对一个重载的属性使用 empty() 时, 重载魔术方法将不会被调用
为避开此限制, 必须将重载属性赋值到本地变量在使用 empty()
#####方法重载
//在对象中调用一个不可访问方法时 调用
public mixed __call ( string $name , array $arguments )
//在静态上下文中调用一个不可访问方法时 调用
public static mixed __callStatic ( string $name , array $arguments )
// $name 参数要是调用的方法名称, $arguments 参数是一个枚举数组, 包含这要传递给方法 $name 的参数
==这里的技术应该叫做解释器钩子==
####遍历对象 PHP 5提供了一种定义对象的方法使其可以通过单元列表来遍历, 例如用 foreach 语句. 所有课件属性都将被用于遍历
更进一步, 可以实现 Iterator 接口. 可以让对象自行决定如何遍历以及每次遍历时那些值可用
Note: PHP 5.5 及以后版本的用户也可参考生成器, 其踢动了另一方法来定义 Iterators
####魔术方法
__construct()
, __destruct()
, __call()
, __callStatic()
, __get()
, __set()
, __isset()
, __unset()
, __sleep()
, __wakeup()
, __toString()
, __invoke()
, __set_state()
, __clone()
和 __debugInfo()
等方法在 PHP 中被称为"魔术方法"(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。
Caution PHP 将所有以__ (两个下划线) 开头的类方法保留为魔术方法. 所以在定义类方法时, 除了上述魔术方法, 建议不要以 __ 为前缀
#####__sleep()
和 __wakeup()
public array __sleep (void)
void __wakeup(void)
serialize()
函数 会检查类中是否存在一个魔术方法 __sleep()
. 如果存在, 该方法会先被调用, 然后才执行序列化操作. 此功能可以用于清理对象, 并返回一个包含对象中所有应被序列化的变量名称数组. 如果该方法未返回任何内容, 则 NULL 被序列化, 并产生一个 E_NOTICE 级别的错误.
Note: `__sleep()` 不能返回父类的私有成员的名字. 这样做会产生一个 E_NOTICE 级别的错误. 可以用 Serializable 接口来替代
__sleep()
方法常用于提交未提交的数据, 或类似的清理操作. 同时, 如果有一些很大的对象, 但不需要全部保存, 这个功能就很好用
与之相反, unserialize()
会检查是否存在一个 __wakeup()
方法, 如果存在, 则会先调用 wakeup()
方法, 预先准备对象需要的资源.
__wakeup()
经常用在反序列化操作中, 例如重新建立数据库链接, 或执行其它初始化操作
#####__toString
public string __toString (void)
__toString()
方法用于一个类被当成字符串时应怎样回应. 此方法必须返回一个字符串, 否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误
Warning 不能在 __toString() 方法中抛出异常. 这么做会导致致命错误
需要支出的是在 PHP 5.2.0 之前, __toString()
方法只有在直接适用于 echo
或 print
时才能生效. PHP 5.2.0 之后, 则可以自任何字符串环境生效 (例如通过 print()
, 使用 %s 修饰符) , 但不能用于非字符串环境 (如使用 %d 修饰符) . 自 PHP 5.2.0 起, 如果将一个未定义 __toString()
方法的对象转换为字符串, 会产生 E_RECOVERABLE_ERROR 级别的错误
#####__invoke()
mixed __invoke([$...])
当尝试以调用函数的方式调用一个对象时, __invoke()
方法会被自动调用
Note: 本特性只在 PHP 5.3.0 级以上版本有效
#####__set_state()
static object __set_state ( array $properties )
class A
{
public $var1;
public $var2;
public static function __set_state($an_array) // As of PHP 5.1.0
{
$obj = new A;
$obj->var1 = $an_array['var1'];
$obj->var2 = $an_array['var2'];
return $obj;
}
}
eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
// 'var1' => 5,
// 'var2' => 'foo',
// ));
自 PHP 5.1.0 起当调用 var_export()
导出类时, 此静态方法会被调用
被方法的唯一参数是一个数组, 其中包含 array('property'=>value,...) 格式排列的类属性
#####__debugInfo()
array __debugInfo ( void )
用var_dump()一个对象的时候 尝试调用此方法
class C {
private $prop;
public function __construct($val) {
$this->prop = $val;
}
public function __debugInfo() {
return [
'propSquared' => $this->prop ** 2,
];
}
}
var_dump(new C(42));
####Final 关键字 PHP 5 新增一个 final 关键字. 如果分类中的方法被声明 final, 则子类无法覆盖该方法. 如果一个类被声明为 final, 则不能被继承
Note: 属性不能被定义为 final, 只有类和方法才能被定义为 final
####对象复制 在多数情况下, 我们并不需要完全复制一个对象来获取中属性. 但有一个情况下确实需要: 如果你有一个 GTK 窗口对象, 该对象持有窗口相关资源. 你可能会想赋值一个新的窗口, 保持所有属性与原来的窗口相同, 但必须是一个新的对象 (因为如果不是新的对象, 那么一个窗口中的改变就会影响到另一个窗口) . 还有一种情况: 如果对象 A 中保存这对象 B 的引用, 当你赋值对象 A 时, 你想其中使用的对象不再是对象 B 而是 B 的一个副本, 那么你必须得到对象 A 的一个副本
对象赋值可以通过 clone 关键字来完成 (如果可能, 这将调用对象的 __clone()
方法) . 对象中的 __clone()
方法不能被直接调用
当对象被复制后, PHP 5 会对对象的所有属性执行一个浅复制 (shallow copy) . 所有的引用属性仍然会是一个指向想原来的变量的引用
void __clone ( void )
当复制完成时, 如果定义了 __clone()
方法, 则新创建的对象 (复制生成的对象) 中 __clone()
方法会被调用, 可用修改属性的值 (如果有必要的话)
####对象比较 PHP 5 中的对象比较要比 PHP 4 中复杂, 所期望的结果更复合一个对象对语言
当使用比较运算符 (==) 比较两个对象变量时, 比较多原则是: 如果两个对象的属性和属性值都同等, 而且两个对象是用一个类的实例, 那么这两个对象变量相等
而如果使用全等运算符 (===) , 这两个对象变量一定要指向某个类同一个实例 (即同一个对象)
Note: PHP 扩展可以自行定义对象比较的原则
####类型约束 PHP 5 可以使用类型约束. 函数的参数可以指定必须对为对象 (在函数原型里面指定类的名字) , 接口, 数组 (PHP 5.1起) 或者 callable (PHP 5.4起) . 不过如果使用 NULL 作为参数的默认值, 那么在调用函数的使用依然可以使用 NULL 作为实参
如果一个类或接口指定了类型约束, 则其所有的子类或实现也都如此
类型约束不能用于标量类型如 int 或 string. traits 也不允许
函数调用的参数与定义的参数类型不一致时, 会抛出一个可捕获的致命错误
类型约束不只是用在类的成员函数里, 也能使用在函数里
####后期静态绑定 自 PHP 5.3.0 起, PHP 增加了一个叫做后期静态绑定的功能, 用于在继承范围内引用静态调用的类
准确说, 后期静态绑定工作原理是存储了在一个"非转发调用" (non-forwarding call) 的类名. 当进行静态方法调用时, 该类名即为明确指定的那个 (通常在 :: 运算符左侧部分) ; 当进行非静态方法调用时, 即为该对象所属的类. 所谓的"转发调用" (forwarding call) 指的是通过一下几种方式进行的静态调用 self::, parent:: ,static:: 以及 forward_static_call(). 可用 get_called_class() 函数来得到被调用的方法所在的类名, static:: 则指出了其范围
该功能从语言内部角度考虑被命名为"后期静态绑定"."后期绑定"的意思是说, static:: 不在被解析为定义当前方法所在的类, 而是在实际运行时计算的. 也可以称之为"静态绑定", 因为它可以用于 (但不限于) 静态方法的调用
#####self::的限制
使用 self:: 或者 __CLASS__
对当前类的静态引用, 取决于定义当前方法所在的类
#####后期静态绑定的用法
后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制. 简单地说, 这个关键字能够让你在上述例子中调用 test()
时引用的类是 B 而不是 A. 最终决定不引入关键字, 而是使用已经预留的 static 关键字
Note: 在非静态环境下, 所调用的类即为该对象实例所属的类. 由于 $this-> 会在同一作用范围内尝试调用私有方法, 而 static:: 则可能给出不同结果. 另一个区别是 static:: 只能用于静态属性
Note: 后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止. 另一方面, 如果静态调用使用 parent:: 或者 self:: 将妆发调用信息
####对象和引用 在php5 的对象编程经常提到的一个关键点是"默认情况下队形是通过引用传递的". 但其实这不是完全正确的
php的引用是别名, 就是两个不同的变量名字指向相同的内容. 在php5, 一个对象变量已经不在保存整个对象的值. 只是保存一个标识来访问真正的对象内容. 当对象作为参数传递, 作为结果返回, 或者赋值给另外一个变量, 另外一个变量跟原来的不是引用的关系, 只是它们都保存着用一个标识符的拷贝, 这个标识符指向同一个对象的真正内容.
####对象序列化
所有php里面的值都可以使用函数 serialize()
来返回一个包含字节流的字符串来表示. unserialize()
函数能够重新把字符串变回php原来的值. 序列化一个对象将会保存对象的所有变量, 但是不会保存对象的方法, 只会保存类的名字.
为了能够 unserialize()
一个对象, 这个对象的类必须已经定义过. 如果序列化类A的一个对象, 将会返回一个跟类A相关, 而且包含了对象所有变量值的字符串. 如果要想在另一个文件中解序列化一个对象, 这个对象的列必须在解序列化之前定义, 可以通过包含一个定义类的文件或使用函数spl_autoload_register()
来实现
当一个应用程序使用函数 session_register()
来保存对象到回话中时, 在每个页面结束的时候这些对象都会自动序列化, 而在每个页面开始的时候又自动解序列化. 所以一旦对象被保存在会话中, 整个应用程序的页面都能使用这些对象. 但是, session_register()
在php5.4.0 之后被移除了
在应用程序中序列化对象以便在之后使用, 强烈推荐在整个应用程序都包含对象的类的定义. 不然有可能出现在解序列化对象的时候, 没有找到该对象的类的定义, 从而把没有方法的类 __PHP_Incomplete_Class_Name
作为该对象的类, 导致返回一个没有用的对象
Note: 不序列化对象的静态成员
###命名空间 ####命名空间概述 在PHP中, 命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:
- 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突
- 为了很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名 (或简短) 的名称, 提高源代码的可读性
PHP命名空间提供了一种将相关的类, 函数和常量组合到一起的途径.
Note: 名为PHP或php的命名空间, 以及这些名字开头的命名空间 (例如PHP\Classes) 被保留用作语言内核使用, 而不应该在用户空间的代码中使用
####定义命名空间 虽然合一合法的PHP代码都可以包含在命名空间中, 但只有以下类型的代码受命名空间的影响, 它们是: 类 (包括抽象类和traits) 接口 函数和常量
命名空间通过关键字 namespace 来声明. 如果一个文件中包含命名空间, 它必须在其它所有代码之前声明命名空间, 除了一个以外: declare关键字 ####定义子命名空间 ####在同一个文件中定义多个命名空间 也可以在同一个文件中定义多个命名空间. 在同一个文件中定义多个命名空间有两种语法形式
在实际的编程实践中, 非常不提倡在同一个文件中定义多个命名空间. 这种方式的主要用于将多个 PHP 脚本合并在同一个文件中
将全局的非命名孔家那种的代码与命名空间中的代码组合在一起, 只能使用大括号形式的语法. 全局代码必须用一个不带明晨搞得 namespace 语句加上大括号括起来
除了开始identitydeclare 语句外, 命名空间的括号外不得有任何 PHP 代码 ####使用命名空间: 基础 在讨论如何使用命名空间之前, 必须了解 PHP 是如何知道要使用哪一个命名孔家那种的元素的. 可以将PHP 命名空间与文件系统作一个简单的类比. 在文件系统中访问一个文件有三种方式:
1. 相对文件名形式如 Foo.txt 它会被解析为 currentdirectory/foo.txt 其中 currentdirectory 表示当前目录. 因此如果当前目录是 /home/foo, 则文件名被解析为 /home/foo/foo.txt
PHP命名空间中的元素使用同样的原理. 例如,类名可以用通过三种方式引用: 1.非限定名称, 或不包含前缀的类名称, 例如 $a = new foo(); 或Foo::staticmethod();. 如果当前命名空间是 currentnamespace, foo将被解析为currentnamespace\foo. 如果使用foo 的大妈是全局的, 不包含在任何命名空间中的代码, 则foo 会被解析为 foo. 警告: 如果命名空间中的函数或常量未定义, 则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称. 2.限定名称, 或包含前缀的名称, 例如 $a = new subnamespace\foo(); 或 subnamespace\foostaticmethod();. 如果当前的命名空间是 currentnamespace, 则foo 会被解析为 currentnamespace\subnamespace\forr. 如果使用 foo 的代码是全局的, 不包含在任何命名空间中的代码, foo 会被解析为 subnamespace\foo 3.完全限定名称, 或包含了全局前缀操作符的名称, 例如, $a = new \currentnamespace\foo(); 或\currentnamespace\foo::staticmethod();. 在这种情况下, foo 总是被解析为代码中的文字名( literalname) currentnamespace\foo
注意访问任意全局类, 函数或常量, 都可以使用完全限定名称, 例如 \strlen() 或 \Exception 或 \INI_ALL
####命名空间和动态语言特征 PHP 命名空间的实现受到其语言自身的动态特征的影响
必须使用完全限定名称 (包括命名空间前缀的类名称) 注意因为在动态的类名称,函数名称或常量名称中, 限定名称和完全限定名称没有区别, 因此其前导的反斜杠是不必要的
####namespace 关键字和 NAMESPACE 常量 PHP支持两种抽象的访问当前命名空间内部元素的方法, NAMESPACE 魔术常量和 namespace 关键字
常量 NAMESPACE 的值是包含当前命名空间名称的字符串. 在全局的, 不包括在任何命名空间中的代码, 它包含一个空的字符串
关键字 namespace 可用来显示访问当前命名空间或子命名空间中的元素. 他等价于类中的 SELF 操作符
####使用命名空间: 别名/常量 允许通过别名引用或导入外部的完全限定名称, 是命名空间的一个重要特征. 这有点类似与在 unix 文件系统中可以创建对其它的文件或目录的符号链接.
所有支持命名空间的PHP版本支持三种别名或导入方式: 为类名称使用别名. 为接口使用别名或为命名空间名称使用别名. PHP 5.6 开始允许导入函数或常量或者为它们设置别名
在PHP 中, 别名是通过操作符 use 来实现的
注意对命名孔家那种的名称( 包含命名空间分隔符的完全限定名称如 Foo\Bar 以及相对的不包含命名空间分隔符的全局名称如FooBar) 来说, 前导的反斜杠是不必要的也不推荐的, 因为导入的名称必须是完全限定的, 不会根据当前的命名空间作相对解析.为了简化操作, PHP 还支持在一行中使用多个use 语句
导入操作是在编译执行的, 但动态的类名称, 函数名称或常量名称则不是
另外, 导入操作只影响非姓丁名称和限定名称. 完全限定名称由于是确定的, 故不受导入的影响
####全局空间 如果没有定义任何命名空间, 所有的类与函数的定义都是在全局空间, 与PHP引入命名空间概念前一样. 在名称前加上前缀\ 表示该名称是全局空间中的名称, 即使该名称位于其它的命名空间中时也是如此
####使用命名空间: 后备全局函数/常量 在一个命名孔家那种, 当PHP 遇到一个非限定的类, 函数或常量名称时, 它使用不同的优先政策解析该名称. 类名称总是解析到当前命名空间中的名称. 因此在访问系统内部或不包含在命名空间中的类名称时, 必须使用完全限定名称
对于函数和常量来说, 如果当前命名空间中不存在该函数或常量, PHP 会退而使用全局空间中的函数或常量. For functions and constants, PHP will fall back to blobal functions or constants if a namespaced function of constant ddoes not exist
####名称解析规则 在说明名称解析规则之前, 我们先看一些重要的定义: #####命名空间名称定义 非限定名称 Unqualified name 名称中不包含命名空间分隔符的标识符, 例如 Foo
限定名称 Qualified name 名称中含有命名空间分隔符的标识符, 例如 Foo\Bar
完全限定名称 Fully qualified name 名称中包含命名空间分隔符, 并以命名空间分隔符开始的标识符, 例如 \Foo\Bar namespace\Foo 也是一个完全限定名称
名称解析准遵循下列规则: 1. 对完全限定名称的函数, 类和常量的调用在编译时解析. 例如 new \A\B 解析为类 A\B 2. 所有的非限定名称和限定名称 ( 非完全限定名称) 根据当前你的导入规则在编译时 进行转换. 例如, 如果命名空间 A\B\C 被导入为C, 那么对 C\D\e() 的调用就会被进行装换. 例如, 如果命名空间 A\B\C 被导入为 C, 那么对C\D\e()的调用就会被转换为 A\B\C\D\e() 3. 在命名空间内部, 所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空面名称. 例如, 在命名空间 A\B 内部调用 C\D\e() 会被转换为A\B\C\D\e() 4. 非限定类名根据当前的导入规则在编译时转换 (用全名代替短的导入名称). 例如, 如果命名空间 A\B\C 导入为C, 则 new C() 被转换为 new A\B\C() 5. 子啊命名空间内部 (例如 A\B) , 对非限定名称的函数调用实在运行时解析的. 例如 对函数foo()调用是这样解析的: 对函数foo() 的调用是这样解析的: 1. 当前命名空间中查找名为 A\B\foo() 的函数 2. 尝试查找并调用 全局(global) 空间中的函数 foo() 6. 在命名空间 ()
####FAQ: 你需要知道的关于命名空间的事情
####语言结构 语言结构就是 PHP 语言的关键词, 语言语法的一部分; 它不可被用户定义或者添加到语言扩展 echo print die exit isset() empty() unset() array() list() eval() include 注意 include_once()是函数 require 注意 require_once()是函数
注:后面没带括号的表示容许不带括号! Note: 因为是一个语言结构器而不是一个函数, 不能被可变函数调用