基本语法
短格式的 echo
标记 <?=
总会被识别并且合法 【PHP 5.4起】
类型
整型
<?php
$a = 1234; // 十进制数
$a = -123; // 负数
$a = 0123; // 八进制数 (等于十进制 83)
$a = 0x1A; // 十六进制数 (等于十进制 26)
$a = 0b11; // 二进制数(等于十进制 3)
浮点型
浮点型(也叫浮点数 float,双精度数 double 或实数 real)可以用以下任一语法定义:
<?php
$a = 1.234;
$b = 1.2e3;
$c = 7E-10;
?>
在PHP
中 double
和 float
是相同的,由于一些历史的原因,这两个名称同时存在。PHP
使用 IEEE 754
双精度格式
浮点型的精度问题详见:浮点型运算会有误差
字符串
一个字符串可以用 4 种方式表达:
单引号
单引号内部的变量及转义字符(如
\n
\t
等)不会被解析双引号
双引号内部的变量会被解析
heredoc 语法结构
类似于双引号结构,内部的变量会被解析。
<?php
$str = <<<EOD
这里是字符串
EOD;
nowdoc 语法结构(自 PHP 5.3.0 起)
类似单引号结构,内部的变量不会被解析。
<?php
$str = <<<'EOD'
这里是字符串
EOD;
字符串中的变量解析
当字符串用双引号或 heredoc
结构定义时,其中的变量将会被解析。
简单规则
<?php
$a = "world";
echo "hello $a";//hello world
复杂规则
<?php
$a = "world";
echo "hello {$a}";//hello world
存取和修改字符串中的字符
string
中的字符可以通过一个从 0
开始的下标,用类似 array
结构中的方括号 []
包含对应的数字来访问和修改,比如 $str[42]
。可以把 string
当成字符组成的 array
。
string
也可用花括号访问,比如$str{42}
。
【PHP5.5开始】 增加了直接在字符串原型中用 [] 或 {} 访问字符的支持。
<?php
echo 'abcd'[2];//c
数组
定义数组
可以用 array()
语言结构来新建一个数组。自 【PHP 5.4】 起可以使用短数组定义语法,用 []
替代 array()
。
最后一个数组单元之后的逗号可以省略。通常用于单行数组定义中,例如常用 array(1, 2)
而不是 array(1, 2, )
。对多行数组定义通常保留最后一个逗号,这样要添加一个新单元时更方便。
键名可以是 integer
或者 string
,其他类型会有如下的强制转换:
包含有合法整型值的字符串会被转换为整型。例如键名 “8” 实际会被储存为 8。但是 “08” 则不会强制转换,因为其不是一个合法的十进制数值。
浮点数也会被转换为整型,意味着其小数部分会被舍去。例如键名 8.7 实际会被储存为 8。
布尔值也会被转换成整型。即键名 true 实际会被储存为 1 而键名 false 会被储存为 0。
Null 会被转换为空字符串,即键名 null 实际会被储存为 “”。
数组和对象不能被用为键名。坚持这么做会导致警告:Illegal offset type。
访问数组
自 【PHP 5.4】 起可以用数组间接引用函数或方法调用的结果。之前只能通过一个临时变量。
<?php
function getArray() {
return array(1, 2, 3);
}
// on PHP 5.4
$secondElement = getArray()[1];
资源
资源 resource
是一种特殊变量,保存了到外部资源的一个引用。
释放资源
由于 PHP 4 Zend
引擎引进了引用计数系统,可以自动检测到一个资源不再被引用了(和 Java 一样)。这种情况下此资源使用的所有外部资源都会被垃圾回收系统释放。因此,很少需要手工释放内存。
持久数据库连接比较特殊,它们不会被垃圾回收系统销毁。
伪类型
mixed
mixed
说明一个参数可以接受多种不同的(但不一定是所有的)类型。number
number
说明一个参数可以是integer
或者float
。void
void
作为返回类型意味着函数的返回值是无用的。void
作为参数列表意味着函数不接受任何参数。
变量
使用未初始化的变量会发出 E_NOTICE
错误,但是在向一个未初始化的数组附加单元时不会。isset() 语言结构可以用来检测一个变量是否已被初始化。
<?php
//echo $a;//E_NOTICE
$b[] = 'b';//不会E_NOTICE
var_dump(isset($c));//不会E_NOTICE
var_dump(isset($d['a']));//不会E_NOTICE
var_dump(empty($e['f']));//不会E_NOTICE
全局变量
global
global
关键字在函数内部声明一个全局变量的同名引用。<?php
#声明全局变量
function a()
{
global $a;
echo $a; //1
$a++;
echo $a; //2。局部变量$a变为 2,全局变量$a也变为 2
}
$a = 1;
a();
echo $a;//2。局部变量$a变为 2,全局变量$a也变为 2
#声明全局变量后,将引用指向另一个变量
function b()
{
global $b, $c;
echo $b;//10
$b = &$c;
echo $b;//100。局部变量$b变为100,全局变量$b仍是10
}
$b = 10;
$c = 100;
b();
echo $b;//10。局部变量$b变为100,全局变量$b仍是10
#声明全局变量后,unset局部变量
function c()
{
global $c;
echo $c;//100;
unset($c);
echo $c;//undefined。局部变量$c被unset不影响全局变量,全局变量$c仍是100
}
$c = 100;
c();
echo $c;//100。内部$c被unset不影响全局变量,全局变量$c仍是100
$GLOBALS
$GLOBALS
是一个关联数组,每一个全局变量为一个元素,键名对应变量名,值对应变量的内容。$GLOBALS
作为超全局变量,可以在任何作用域使用。操作$GLOBALS['x']
就是操作$x
<?php
function a()
{
global $a;
$a = 1;
$b = 10;
echo $a;//1
echo $b;//10
echo $GLOBALS['a'];//1
echo $GLOBALS['b'];//undefined;
}
a();
echo $a;//1
echo $GLOBALS['a'];//1
变量中的点
通常,PHP
不会改变传递给脚本中的变量名。然而应该注意到点(句号)不是 PHP
变量名中的合法字符。至于原因,看看:
<?php
$varname.ext; /* 非法变量名 */
?>
这时,解析器看到是一个名为 $varname
的变量,后面跟着一个字符串连接运算符,后面跟着一个裸字符串(即没有加引号的字符串,且不匹配任何已知的健名或保留字)ext
。很明显这不是想要的结果。
出于此原因,PHP
会自动将来自 PHP
之外的变量名中的点替换成下划线。
常量
可以简单的通过指定其名字来取得常量的值。如果常量名是动态的,也可以用函数 constant()
来获取常量的值。用 get_defined_constants()
可以获得所有已定义的常量列表
运算符
运算符是可以通过给出的一或多个值(用编程行话来说,表达式)来产生另一个值(因而整个结构成为一个表达式)的东西。
算数运算符
//求幂
echo 2 ** 3;//8
赋值运算符
=
传值赋值将原变量的值拷贝到新变量中(传值赋值),所以改变其中一个并不影响另一个。
例外: 赋值为对象时是引用赋值
&=
引用赋值两个变量指向了同一个数据,改变其中一个会影响另一个
比较运算符
??
null
合并运算符,返回从左往右第一个存在且不为null
的操作数 【PHP7起】<=>
太空船操作符,又叫组合比较运算符,或结合比较符。
$a<=>$b
,$a
小于、等于、大于$b
时分别返回-1
,0
,1
【PHP7起】
执行运算符
执行运算符的效果与函数 shell_exec()
相同。
$output = `ls -al`;
数组合并
多个数组合并时,新增一个空数组,将参与合并的数组的元素依次追加到新数组的末尾,两种合并数组的方式:
+
号如果键名已存在,则丢弃后续的元素
array_merge
如果原键名是数字,从 0 开始重建数字索引
如果键名是字符串,且键名已存在,则用后添加的元素覆盖之前添加的元素
array_merge_recursive
如果原键名是数字,从 0 开始重建数字索引
如果键名是字符串,且键名已存在,则同键名的元素组成数组作为该键名的值
如果值本身是数组,将递归执行合并
array_replace
如果键名已存在(不管是数字键名还是字符串键名),则用后添加的元素覆盖之前添加的元素
array_replace_recursive
如果键名已存在(不管是数字键名还是字符串键名),则用后添加的元素覆盖之前添加的元素
如果值本身是数组,将递归执行覆盖
类型运算符 instanceof
用于确定某一对象是否是某一类的实例
用于确定一个变量是不是继承自某一父类的子类的实例
用于确定一个变量是不是实现了某个接口的对象的实例
以下三种判断方法结果相同, instanceof
效率最高
$obj instanceof className;
get_class($obj) == 'className';
is_a($obj,'className');
流程控制
require 和 include
require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止而 include 只产生警告(E_WARNING),脚本会继续运行。
switch
为避免错误,理解 switch
是怎样执行的非常重要。switch
语句一行接一行地执行(实际上是语句接语句)。开始时没有代码被执行。仅当一个 case
语句中的值和 switch
表达式的值匹配时 PHP
才开始执行语句,直到 switch
的程序段结束或者遇到第一个 break
语句为止。如果不在 case
的语句段最后写上 break
的话,PHP
将继续执行下一个 case
中的语句段
<?php
$i = 0;
switch ($i) {
case 0:
echo "i equals 0";
case 1:
echo "i equals 1";
case 2:
echo "i equals 2";
}
//将输出“i equals 0i equals 1i equals 2”
return
如果在一个函数中调用
return
语句,将立即结束此函数的执行并将它的参数作为函数的值返回。如果当前脚本文件是被
include
的或者require
的,则控制交回调用文件。此外,如果当前脚本是被include
的,则return
的值会被当作include
调用的返回值。如果在主脚本文件中调用
return
,则脚本中止运行。
goto
goto
操作符可以用来跳转到程序中的另一位置。该目标位置可以用目标名称加上冒号来标记,而跳转指令是 goto
之后接上目标位置的标记。PHP
中的 goto
有一定限制,目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch
结构中。可以跳出循环或者 switch
通常的用法是用
goto
代替多层的break
可以试试用
goto
代替if
或者跳出多层if
函数
可变数量的参数列表
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
匿名函数(闭包)
闭包可以使用 use
从父作用域中继承变量的值(在闭包定义之后,继承的值如果发生变化,闭包内变量的值不会改变)。
$c = 5;
$d = function () use ($c)
{
var_dump($c);
};
$c = 10;
$d();//输出5
从父作用域中继承变量与使用全局变量是不同的。全局变量存在于一个全局的范围,无论当前在执行的是哪个函数。而 闭包的父作用域是定义该闭包的函数(不一定是调用它的函数)。
看起来 function () use($a){}
跟 function($a){}
的效果没什么区别。变量作用域方便原则:变量跟定义函数的脚本在相同作用域,use方便;变量跟调用函数的脚本在相同作用域,直接传参方便
命名空间
在命名空间中以非限定名称访问的函数或常量(这里不包含类)未定义,则会被解析为全局函数名称或常量名称
动态访问类名称时必须使用完全限定名称。因为在动态的类名称、函数名称、常量名称中,非限定名称和限定名称会被强行在名称前增加反斜杠变为完全限定名称去解析
namespace
关键字用来显式访问当前命名空间的元素;__NAMESPACE__
常量表示当前命名空间名称的字符串,用来在动态名称中拼接完全限定名称的字符串<?php
namespace a\b\
{
function test(){}
}
namespace a
{
namespace\b\test();
$c = __NAMESPACE__ . '\b\test';
$c();
}
使用
use
引入命名空间、类、接口、函数、常量,并可以为其指定别名(导入操作只影响非限定名称和限定名称,不影响完全限定名称)引入函数:
use function My\functionName
引入常量:
use const My\CONSTANT
引入类:
use My\className
PHP 7 之后可以一次引入多个命名空间、类、函数、接口、常量等,例如:
use My\{class1, class2, class3}
错误/异常处理
PHP7
改变了大多数错误的报告方式,大多数错误被作为 Error
异常抛出
try{...}catch(Error $e){...}
捕捉E_ERROR & E_PARSE
等被作为Error
异常抛出E_PARSE
在语法检查阶段,输出致命错误,中断程序执行。语法检查会检查主文件及通过include
系列包含的文件。如果语法错误发生在通过
__autoload
或spl_autoload_register
自动加载 的文件中,则在语法检查阶段不会检查也就不会报错,在执行阶段抛出Error
异常。try{...}catch(Exception $e){...}
捕获throw
的异常set_exception_handler
捕获所有未被捕获的Error
异常、Exception
异常、其他继承Error
或Exception
的异常set_error_handler
捕捉E_WARNING
E_NOTICE
E_DEPRECATED
E_USER_*
等错误,不能捕获E_ERROR
、E_PARSE
、E_CORE_ERROR
、E_CORE_WARNING
、E_COMPILE_ERROR
、E_COMPILE_WARNING
等错误error_get_last
(依靠register_shutdown_function
触发) 可以捕获所有非Error
异常的错误
Iterator (迭代器)接口
可在内部迭代自己的外部迭代器或类的接口。
迭代器接口的方法
- Iterator::rewind — 返回到迭代器的第一个元素
- Iterator::valid — 检查当前位置是否有效
- Iterator::current — 返回当前元素
- Iterator::key — 返回当前元素的键
- Iterator::next — 向前移动到下一个元素
使用 foreach
循环实现迭代器接口的类的对象时,迭代器方法调用顺序:rewind、 valid、 current、 key、 next、 valid、 current….
<?php
class a implements Iterator
{
...
}
$obj = new a();
foreach ($obj as $k=>$v) {
...
}
//相当于
$obj->rewind();
while ($obj->valid()) {
$v = $obj->current();
$k = $obj->key();
$obj->next();
}
生成器
生成器语法
一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以 yield
生成许多它所需要的值。
<?php
function a()
{
yield 1;
yield 2;
yield 3;
}
function b() {
for ($i = 0; $i <= 10; $i++) {
yield $i;
}
}
当一个生成器被调用的时候,它返回一个可以被遍历的对象(生成器类的对象)。当你遍历这个对象的时候(例如通过一个 foreach
循环),PHP
将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。
一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。
一个生成器不可以返回值: 这样做会产生一个编译错误。然而return空是一个有效的语法并且它将会终止生成器继续执行。
yield关键字
生成器函数的核心是 yield
关键字。它最简单的调用形式看起来像一个 return
申明,不同之处在于普通 return
会返回值并终止函数的执行,而 yield
会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
yield的几种用法:
生成值
//生成值的表达式本身的值为null
yield 1;//生成值
yield $k=>$v;//指定健名来生成值
接收值
//接收Generator::send方法向生成器传值,表达式的值为传入的值
$receive = yield;//接收值
接收值的同时生成值
$receive = yield 2;
生成器的好处
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator
接口的方式,性能开销和复杂性大大降低。
生成器允许你在 foreach
代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间,可以对比以下两种方式占用的内存:
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step <= 0) {
throw new Exception('Step must be +ve');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new Exception('Step must be -ve');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
var_dump(memory_get_usage());//此函数仅在 linux 下有效
xrange(1, 1000 * 1000);
var_dump(memory_get_usage());
range(1, 1000 * 1000);
var_dump(memory_get_usage());
Generator(生成器)类
此类不能使用 new
实例化,只能通过生成器返回。
生成器类的方法
- Generator::rewind — 重置迭代器
- Generator::valid — 检查迭代器是否被关闭
- Generator::current — 返回当前产生的值
- Generator::key — 返回当前产生的键
- Generator::next — 生成器继续执行
- Generator::send — 向生成器中传入一个值
- Generator::throw — 向生成器中抛入一个异常
- Generator::__wakeup — 序列化回调
引用的解释
PHP 中的引用意味着不同的名字访问同一个变量内容。
引用是符号表别名,最接近的比喻是文件名和文件本身,引用可以看做文件系统中的硬链接,不能理解成内存地址的指针