• Expression(表达式)
    • Variable(变量)
      • Variable And Constants Declaration(变量与常量声明)
      • Variable Hoisting(变量提升)
    • Assignment(变量赋值)
      • Pass-by-Value & Pass-by-Reference(传值与传引用)
      • Continuous Assignment(连续赋值)
      • Destructuring Assignment(解构赋值)
      • Copy Composite DataTypes(复合类型拷贝)
  • Operator(运算符)
    1. - Basic(基本运算符)
    2. - Bit Operator(位运算符)
    3. - Operator Overloading(运算符重载)
  • Scope & Closure(作用域与闭包)
  • Lazy Evaluation(惰性求值)

    表达式(Expression)

变量(Variable)

变量与常量声明(Variable And Constants Declaration)

变量声明(Variable Declaration)

1.基础变量

PHP 中的变量用一个美元符号后面跟变量名来表示。变量名是区分大小写的。变量名与 PHP 中其它的标签一样遵循相同的规则。一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。按照正常的正则表达式,它将被表述为:[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff]*

Example

  1. <?php
  2. $username = "Leon Kennedy"; // 通过
  3. $_username = "Leon Kennedy"; // 通过
  4. $username1 = "a1"; // 通过
  5. $user.name = "Leon Kennedy"; // 报错
  6. $用户名称 = "初七"; // 通过
  7. $UserName = "Leon Kennedy"; // 通过
  8. $userName = "Leon"; // 通过
  9. $user_name = "Cissy"; // 通过
  10. $1 = 100; // 报错
  11. $_100 = 100; // 通过

2.**预定义变量

PHP 提供了大量的预定义变量。由于许多变量依赖于运行的服务器的版本和设置,及其它因素,所以并没有详细的说明文档。一些预定义变量在 PHP 以命令行形式运行时并不生效。PHP 中的许多预定义变量都是“超全局”,这意味它们在一个脚本的全部作用域中都可用。

  • $GLOBALS:获取全局变量
  • $_SERVER:获取服务器相关信息
  • $_GET:获取 HTTP GET 请求数据
  • $_POST:获取 HTTP POST 请求数据
  • $_FILES:获取 HTTP 上传文件请求数据
  • $_COOKIE:获取 COOKIE 数据
  • $_SESSION:获取 SESSION 数据
  • $_REQUEST:获取 HTTP 请求数据
  • $_ENV:获取环境变量
  • $php_errormsg:前一个错误信息
  • $HTTP_RAW_POST_DATA:原生POST数据
  • $http_response_header:HTTP 响应头
  • $argc:传递给脚本的参数数目
  • $argv:传递给脚本的参数数组

Example

  1. <?php
  2. foreach($_SERVER as $key => $value){
  3. echo "{$key} => {$value}\n";
  4. }
  1. [ ]() [ ]() <br />**3.变量范围**<br />**<br />变量的范围即它定义的上下文背景(也就是它的生效范围)。大部分的 PHP 变量只有一个单独的范围。可以使用 global 关键字声明全局变量。
  1. <?php
  2. $a = 1;
  3. $b = 2;
  4. function sum()
  5. {
  6. global $a, $b;
  7. $b = $a + $b;
  8. }
  9. sum();
  10. echo $b;
  11. ?>
  12. <?php
  13. function sum_again()
  14. {
  15. // $_GLOBALS 是一个关联数组,每一个变量为一个元素,键名对应变量名,值对应变量的内容
  16. $GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
  17. }
  18. sum();
  19. echo $b;
  20. ?>

也可以使用 static 关键字在声明静态变量。

  1. <?php
  2. function test(){
  3. static $a = 0;
  4. echo $a;
  5. $a++;
  6. }
  7. test();
  8. test();
  9. test();
  10. test(); // 输出结果为:0123

4.可变变量
**
有时候使用可变变量名是很方便的。就是说,一个变量的变量名可以动态的设置和使用。

  1. <?php
  2. $Bar = "a";
  3. $Foo = "Bar";
  4. $World = "Foo";
  5. $Hello = "World";
  6. $a = "Hello";
  7. echo $a."\n"; // Hello
  8. echo $$a."\n"; // World
  9. echo $$$a."\n"; // Foo
  10. echo $$$$a."\n"; // Bar
  11. echo $$$$$a."\n"; // a
  12. echo $$$$$$a."\n"; // Hello
  13. echo $$$$$$$a."\n"; // World

5.来自 PHP 之外的变量

  • HTML 表单(GET 和 POST)
    • 通过预定义变量 $_GET$_POST 获取。
    • 变量名中的点和空格被转换成下划线。例如 <input name="a.b" /> 变成了 $_REQUEST["a_b"]
  • HTTP 表单(Form)提交数据的方式
    1. - `enctype="application/x-www-form-urlencoded"` (默认值)
    2. - `enctype="multipart/form-data"`
    3. - 只有当 POST 方法配合 `enctype=multipart/form-data` 时才能正确传输文件。
    4. - 使用 PUTPATCH 方法时,需要使用 `enctype="x-www-form-urlencoded"`
  • HTTP Cookies
    • 如果要将多个值赋给一个 cookie 变量,必须将其赋成数组。

常量声明(Constants Declaration)

1.语法
**
可以用 define() 函数来定义常量,在 PHP 5.3.0 以后,可以使用 const关键字在类定义之外定义常量。一个常量一旦被定义,就不能再改变或者取消定义。

  1. <?php
  2. define("SEBASTIAN", "Sebastian");
  3. const KENNEDY = "Kennedy";
  4. echo SEBASTIAN.' '.KENNEDY."\n";
  5. const ARR = ['sebastian', 'kennedy'];
  6. var_dump(ARR);

2.预定义常量(魔术常量)
**

  • __LINE__:文件中的当前行号。
  • __FILE__:文件的完整路径和文件名。
  • __DIR__:文件所在的目录。
  • __FUNCTION__:返回函数被定义时的名字(区分大小写)。
  • __CLASS__:返回该类被定义时的名字(区分大小写)。
  • __TRAIT__:返回 trait 被定义时的名字(区分大小写)。
  • __METHOD__:返回该方法被定义时的名字(区分大小写)。
  • __NAMESPACE__:当前命名空间的名称(区分大小写)。
  1. <?php
  2. namespace SebastianKennedy\Code;
  3. $line = __LINE__."\n";
  4. $filePath = __FILE__."\n";
  5. $dirPath = __DIR__."\n";
  6. function testFunc(){
  7. return __FUNCTION__."\n";
  8. }
  9. echo $line;
  10. echo $filePath;
  11. echo $dirPath;
  12. echo testFunc();
  13. trait TestTrait
  14. {
  15. public function testTrait(){
  16. return __TRAIT__."\n";
  17. }
  18. }
  19. class TestClass
  20. {
  21. use TestTrait;
  22. public function __construct(){
  23. return __CLASS__."\n";
  24. }
  25. public function testMethod(){
  26. return __METHOD__."\n";
  27. }
  28. public function testNameSpace(){
  29. return __NAMESPACE__."\n";
  30. }
  31. }
  32. $class = new TestClass;
  33. echo $class->testMethod();
  34. echo $class->testTrait();
  35. echo $class->testNameSpace();

变量提升(Variable Hoisting)

因为 PHP 区别全局作用域、函数作用域和块级作用域,PHP 不存在变量提升这个特性。

  1. <?php
  2. $param = 10;
  3. function test($param){
  4. echo $param;
  5. $param = "Luis Edware";
  6. }
  7. test($param);

变量赋值(Assignment)

传值与传引用(Pass by Value & Pass by Reference)

PHP 支持四种标量值(标量值不能拆分为更小的单元):整型值(integer)、浮点数值(float)、字符串值(string)和布尔值(boolean)。PHP 也支持两种复合类型:数组(Array)和对象(Object)。PHP 还支持两种特殊类型:资源(resource)和空(null)。

PHP 变量默认总是传值赋值。那也就是说,当将一个表达式的值赋予一个变量时,整个原始表达式的值被赋值到目标变量。这意味着,例如,当一个变量的值赋予另外一个变量时,改变其中一个变量的值,将不会影响到另外一个变量。PHP 也提供了另外一种方式给变量赋值:引用赋值。这意味着新的变量简单的引用了原始变量。改动新的变量将影响到原始变量,反之亦然。PHP 的引用就是同一个变量的别名。

  1. <?php
  2. $foo = 'Bob';
  3. $bar = &$foo;
  4. $bar = "My name is $bar";
  5. echo $bar,$foo;
  6. // 非法的引用赋值;引用没有名字的表达式
  7. $bar = &(24 * 7);
  8. // 非法的引用赋值;只有已经命名的变量才可以引用赋值
  9. function test()
  10. {
  11. return 25;
  12. }
  13. $bar = &test();

PHP 引用相关文章:

连续赋值(Continuous Assignment)

  1. <?php
  2. $a = $b = $c = 200;
  3. var_dump($a);
  4. var_dump($b);
  5. var_dump($c);
  6. $foo = $bar = $baz = "Sebastian Kennedy\n";
  7. echo $foo, $bar, $baz;
  8. $arr1 = $arr2 = $arr3 = ['github','coding','gitlab'];
  9. var_dump($arr1);
  10. var_dump($arr2);
  11. var_dump($arr3);
  12. // 可以通过 () 来改变运算符的优先级
  13. $a = ($b = 4) + 5;
  14. echo $a."\n";
  15. // 组合赋值
  16. $a .= $b .= "foo";
  17. echo $a."\n";
  18. // 不用第三个变量,交换两个变量的值
  19. $a = 666;
  20. $b = 233;
  21. $a ^= $b ^= $a ^= $b;
  22. echo $a." - ",$b."\n";
  23. // 不用第三个变量,交换两个变量的值
  24. $a = 222;
  25. $b = 333;
  26. list($a, $b) = [$b, $a];
  27. echo $a." - ",$b."\n";

解构赋值(Destructuring Assignment)

  1. <?php
  2. $string = "七月十五九月初七,十二月初一九月二十九";
  3. $arr = [
  4. ["id" => 1, "name" => 'Sebastian'],
  5. ["id" => 2, "name" => 'Kennedy'],
  6. ];
  7. // list() style
  8. list($foo, $bar) = explode(',', $string);
  9. echo $foo." - ".$bar."\n";
  10. list("id" => $id1, "name" => $name1) = $arr[0];
  11. echo $id1." => ". $name1."\n";
  12. // [] style
  13. [$a, $b] = explode(',', $string);
  14. echo $a." - ". $b."\n";
  15. ["id" => $id2, "name" => $name2] = $arr[1];
  16. echo $id2." => ", $name2."\n";

复合类型拷贝(Copy Composite DataTypes)

对象复制可以通过 clone 关键字来完成,对象中的 __clone()方法不能被直接调用。当对象被复制后,PHP 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。

注意: 引用和拷贝的区别是:拷贝是将原来的变量内容复制下来,拷贝后的变量与原来的变量使用各自的内存,互不干扰。 引用相当于是变量的别名,其实就是用不同的名字访问同一个变量内容。当改变其中一个变量的值时,另一个也跟着发生变化。

对于 PHP 对象来说,简单的赋值并不能实现克隆,因为对象是通过伪引用传递的。

  1. <?php
  2. class Example{
  3. private $a = 1;
  4. public function plus() {
  5. $this->a++;
  6. }
  7. public function foo() {
  8. echo "a={$this->a}\n";
  9. }
  10. }
  11. $obj = new Example;
  12. $obj->foo(); // 输出 a=1
  13. $obj2 = $obj;
  14. $obj2->plus();
  15. $obj2->foo(); // 输出 a=2
  16. $obj->foo(); // 输出 a=2

PHP 提供了 clone 关键字来实现对象的克隆,如下:

  1. <?php
  2. class Example{
  3. private $a = 1;
  4. public function plus() {
  5. $this->a ++;
  6. }
  7. public function foo() {
  8. echo "a={$this->a}\n";
  9. }
  10. }
  11. $obj = new Example();
  12. $obj2 = clone $obj;
  13. $obj->foo(); // 输出 a=1
  14. $obj2->plus();
  15. $obj2->foo(); // 输出 a=2
  16. $obj->foo(); // 输出 a=1

当复制完成时, 如果定义了 __clone() 方法, 则新创建的对象中的 __clone() 方法会被调用, 可用于修改属性的值,如下:

  1. <?php
  2. class Example{
  3. private $a = 1;
  4. private $instance_no; // instance 编号
  5. static public $all = 0; // instance 总数
  6. public function foo() {
  7. echo "a={$this->a}\n";
  8. }
  9. public function bar() {
  10. echo "instance={$this->instance_no}\n";
  11. }
  12. public function __construct() {
  13. // 实例号+1
  14. $this->instance_no = ++self::$all;
  15. }
  16. public function __clone() {
  17. // 实例号+1
  18. $this->instance_no = ++self::$all;
  19. }
  20. }
  21. $obj = new Example();
  22. $obj->foo();
  23. $obj->bar();
  24. echo $obj::$all."\n";
  25. $obj2 = clone $obj;
  26. $obj2->foo();
  27. $obj2->bar();
  28. echo $obj2::$all."\n";

在 PHP 中,克隆会把对象的所有属性进行克隆,但是如果一个对象中包含引用属性,那么克隆之后,在新生成的对象中这个属性仍然是指向于原来变量的引用,这个成为浅克隆,代码如下:

  1. <?php
  2. class Example{
  3. private $a = 1;
  4. public $obj;
  5. public function plus() {
  6. $this->a++;
  7. }
  8. public function foo() {
  9. echo "a={$this->a}\n";
  10. }
  11. }
  12. class Example2{
  13. public $b = 1;
  14. public function plus() {
  15. $this->b ++;
  16. }
  17. public function foo() {
  18. echo "b={$this->b}\n";
  19. }
  20. }
  21. $obj = new Example;
  22. $obj->obj = new Example2;
  23. $obj2 = clone $obj;
  24. // 修改对象的普通属性
  25. $obj->plus();
  26. $obj->foo(); // 输出 a=2
  27. $obj2->foo(); // 输出 a=1
  28. // 修改对象的引用属性
  29. $obj->obj->plus();
  30. $obj->obj->foo(); // 输出 b=2
  31. $obj2->obj->foo(); // 输出 b=2

PHP 如果需要实现深克隆,需要自己实现对引用属性的完全复制,代码如下:

  1. <?php
  2. class Example{
  3. private $a = 1;
  4. public $obj;
  5. public function plus() {
  6. $this->a ++;
  7. }
  8. public function foo() {
  9. echo "a={$this->a}\n";
  10. }
  11. public function __clone(){
  12. // 当存在对象数组的时候,可以使用以下代码实现深克隆
  13. foreach($this as $key => $value){
  14. if (is_object($value) || (is_array($value))) {
  15. $this->{$key} = unserialize(serialize($value));
  16. }
  17. }
  18. }
  19. }
  20. class Example2{
  21. public $b = 1;
  22. public function plus() {
  23. $this->b ++;
  24. }
  25. public function foo() {
  26. echo "b={$this->b}\n";
  27. }
  28. }
  29. $obj = new Example;
  30. $obj->obj = new Example2;
  31. $obj2 = clone $obj;
  32. // 修改对象的普通属性
  33. $obj->plus();
  34. $obj->foo(); // 输出 a=2
  35. $obj2->foo(); // 输出 a=1
  36. // 修改对象的引用属性
  37. $obj->obj->plus();
  38. $obj->obj->foo(); // 输出 b=2
  39. $obj2->obj->foo(); // 输出 b=1

运算符(Operator)

运算符是可以通过给出的一或多个值(用编程行话来说,表达式)来产生另一个值(因而整个结构成为一个表达式)的东西。运算符的种类有:

  • 基本运算符(Basic Operators)
    • 算术运算符(Assignment Operator)
    • 比较运算符(Comparison Operator)
    • 错误控制运算符(Error Control Operator)
    • 执行运算符(Execution Operator)
    • 递增/递减运算符(Incrementing / Decrementing Operator)
    • 逻辑运算符(Logical Operator)
    • 字符串运算符(String Operator)
    • 数组运算符(Array operator)
    • 类型运算符(Type Operator)
  • 位运算符(Bit Operator)
  • 运算符重载(Operator Overloading)

基本运算符(Basic Operators)

算术运算符(Assignment Operator)

例子 名称 结果
-$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 次平方

比较运算符(Comparison Operator)

例子 名称 结果
$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 小于、等于、大于 $b 时 分别返回一个小于、等于、大于 0 的 integer 值。 PHP 7 开始提供.
$a ?? $b ?? $c NULL 合并操作符 从左往右第一个存在且不为 NULL 的操作数。如果都没有定义且不为 NULL,则返回 NULL。PHP 7 开始提供。

错误控制运算符(Error Control Operator)

PHP 支持一个错误控制运算符:@ 。当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都被忽略掉。

执行运算符(Execution Operator)

PHP 支持一个执行运算符:反引号(`)。注意这不是单引号!PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回(即,可以赋给一个变量而不是简单地丢弃到标准输出)。使用反引号运算符“”的效果与函数 shell_exec() 相同。

  1. <?php
  2. $output = `ls -al`;
  3. echo "<pre>$output</pre>";

递增/递减运算符(Incrementing / Decrementing Operator)

例子 名称 结果
++$a 前加 $a 的值加一,然后返回 $a。
$a++ 后加 返回 $a,然后将 $a 的值加一。
–$a 前减 $a 的值减一, 然后返回 $a。
$a– 后减 返回 $a,然后将 $a 的值减一。

一个简单的示例脚本

  1. <?php
  2. echo "<h3>Postincrement</h3>";
  3. $a = 5;
  4. echo "Should be 5: " . $a++ . "\n";
  5. echo "Should be 6: " . $a . "\n";
  6. echo "<h3>Preincrement</h3>";
  7. $a = 5;
  8. echo "Should be 6: " . ++$a . "\n";
  9. echo "Should be 6: " . $a . "\n";
  10. echo "<h3>Postdecrement</h3>";
  11. $a = 5;
  12. echo "Should be 5: " . $a-- . "\n";
  13. echo "Should be 4: " . $a . "\n";
  14. echo "<h3>Predecrement</h3>";
  15. $a = 5;
  16. echo "Should be 4: " . --$a . "\n";
  17. echo "Should be 4: " . $a . "\n";

逻辑运算符(Logical Operator)

例子 名称 结果
$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。

字符串运算符(String Operator)

有两个字符串(string)运算符。第一个是连接运算符(“.”),它返回其左右参数连接后的字符串。
第二个是连接赋值运算符(“.=”),它将右边参数附加到左边的参数之后。代码如下:

  1. <?php
  2. $a = "Hello ";
  3. $b = $a . "World!"; // now $b contains "Hello World!"
  4. $a = "Hello ";
  5. $a .= "World!"; // now $a contains "Hello World!"

数组运算符(Array Operator)

例子 名称 结果
$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。

对象类型运算符(Object Type Operator)

instanceof 用于确定一个 PHP 变量是否属于某一类 class 的实例,常见的场景是异常错误捕获

  1. <?php
  2. class ParentClass
  3. {
  4. }
  5. class MyClass extends ParentClass
  6. {
  7. }
  8. $a = new MyClass;
  9. var_dump($a instanceof MyClass);
  10. var_dump($a instanceof ParentClass);

位运算符

例子 名称 结果
$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 Shift left(左移) 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。
$a >> $b Shift right(右移) 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。

运算符重载(Operator Overloading)- [Web 领域几乎没有使用场景]

PHP 5.6新特性之一:内部操作符重载

在计算机程序设计中,运算符重载是多态的一种。这里,运算符被当作多态函数,他们的行为随着其参数类型的不同而不同。简而言之,运算符重载使程序员能够根据运算子类型的不同来决定运算符功能的不同。PHP 支持运算符重载。PHP 5.6 之后,GMP 支持运算符重载,并且造型成数值类型,这使得使用 GMP 的代码更加直观。

  1. <?php
  2. $a = gmp_init(42);
  3. $b = gmp_init(17);
  4. // 意思就是 $a + $b 的 + 的作用等同于 gmp_add() 函数,让代码更加直观。
  5. if (version_compare(PHP_VERSION, '5.6', '<')) {
  6. echo gmp_intval(gmp_add($a, $b)), PHP_EOL;
  7. echo gmp_intval(gmp_add($a, 17)), PHP_EOL;
  8. echo gmp_intval(gmp_add(42, $b)), PHP_EOL;
  9. } else {
  10. echo $a + $b, PHP_EOL;
  11. echo $a + 17, PHP_EOL;
  12. echo 42 + $b, PHP_EOL;
  13. }

运算符优先级(Operator Precedence)

  1. 递增/递减 > ! > 算术运算符 > 大小比较 > (不)相等比较 > 引用 >
  2. 位运算符 ^ > 位运算符 | > 逻辑与 > 逻辑或 > 三目 > 赋值 > and > xor > or
  • 算术运算符(Assignment Operator)
  • 比较运算符(Comparison Operator)
  • 错误控制运算符(Error Control Operator)
  • 执行运算符(Execution Operator)
  • 递增/递减运算符(Incrementing / Decrementing Operator)
  • 逻辑运算符(Logical Operator)
  • 字符串运算符(String Operator)
  • 数组运算符(Array operator)
  • 类型运算符(Type Operator)

匿名函数与闭包(Anonymous functions & Closure)

闭包和匿名函数在 PHP 5.3 中被引入。闭包是指在创建时封装函数周围状态的函数,即使闭包所在的环境不存在了,闭包封装的状态依然存在。匿名函数其实就是没有名称的函数,匿名函数可以赋值给变量,还能像其他任何 PHP 对象那样传递。最经常用作回调函数参数的值。匿名函数和闭包目前是通过 Closure 类来实现的。

Example:

  1. <?php
  2. $closure = function($name){
  3. return sprintf('hello %s', $name);
  4. };
  5. echo $closure("Sebastian Kennedy");

之所以能够调用 $closure 变量,是因为这个变量的值是一个闭包对象,而且闭包对象实现了 __invoke() 魔术方法。只要变量名后有 (),PHP 就会查找并调用 __invoke() 方法:

  1. <?php
  2. class testClosureClass
  3. {
  4. public function __invoke()
  5. {
  6. echo "Hello World";
  7. }
  8. }
  9. $closure = new testClosureClass;
  10. $closure(); // 输出 Hello World

PHP 闭包一般当做函数或方法的回调来使用,如下:

  1. <?php
  2. $numbersPlusOne = array_map(function ($number) {
  3. return $number + 1;
  4. }, [1, 2, 3]);
  5. var_export($numbersPlusOne);

PHP 闭包不会像 JavaScript 闭包那样自动封装应用的状态。在 PHP 中,必须手动调用闭包对象的 bindTo() 方法或者使用 use 关键字,把状态附加到 PHP 闭包上。以下是使用 use 关键字附加闭包的状态:

  1. <?php
  2. function enclosePerson($name)
  3. {
  4. return function ($doCommand) use ($name) {
  5. return sprintf('%s, %s', $name, $doCommand);
  6. };
  7. }
  8. //把字符串 "Clay" 封装在闭包中
  9. $clay = enclosePerson('Clay');
  10. //传入参数,调用闭包
  11. echo $clay('get me sweat tea!'); // Clay, get me sweat tea!

我们可以通过 bindTo() 方法的第一个参数把 Closure 对象的内部状态绑定到其他对象上。可以通过 bindTo()方法的第二个参数指定绑定闭包的那个对象所属的 PHP 类。以下是使用 bindTo() 方法附加闭包的状态:

  1. <?php
  2. class App
  3. {
  4. protected $routes = [];
  5. protected $responseStatus = '200 OK';
  6. protected $responseContentType = 'text/html';
  7. protected $responseBody = 'Hello world';
  8. public function addRoute($routePath, $routeCallback)
  9. {
  10. $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
  11. }
  12. public function dispatch($currentPath)
  13. {
  14. foreach($this->routes as $routePath => $callback) {
  15. if ($routePath === $currentPath) {
  16. $callback();
  17. }
  18. }
  19. header('HTTP/1.1' . $this->responseStatus);
  20. header('Content-type: ' . $this->responseContentType);
  21. header('Content-length: ' . mb_strlen($this->responseBody));
  22. echo $this->responseBody;
  23. }
  24. }
  25. $app = new App();
  26. $app->addRoute('/users/1', function () {
  27. $this->responseContentType = 'application/json;charset=utf8';
  28. $this->responseBody = '{"name" : "Luis Edware", "phone" : "14089950522"}';
  29. });
  30. $app->dispatch('/users/1');

惰性求值(Lazy Evaluation)

  1. <?php
  2. class Shooting
  3. {
  4. public function __construct(){
  5. echo "Biu! Biu! Biu! Da! Da! Da!\n";
  6. }
  7. }
  8. class Scar
  9. {
  10. private $closure;
  11. private $instance;
  12. public function __construct(Closure $closure){
  13. $this->closure = $closure;
  14. $this->instance = $closure->bindTo($this, __CLASS__);
  15. }
  16. public function shoot(){
  17. $closure = $this->closure;
  18. return $closure();
  19. }
  20. public function shootAgain(){
  21. $closure = $this->instance;
  22. return $closure();
  23. }
  24. }
  25. $scar = new Scar(function(){
  26. return new Shooting;
  27. });
  28. // 不触发射方法就不会 Biu Biu Biu!
  29. $scar->shoot();
  30. $scar->shootAgain();
  • 流程控制与异常处理(ControlFlow)
    • 分支选择(Branch)
      • if
      • switch
    • 循环(Loop)
      • for
      • white
      • break / continue
    • 错误处理(ErrorHanding)
      • 异常抛出(Throw)
      • 异常捕获(Try Catch Finally)

流程控制与异常处理(ControlFlow)

分支选择(Branch)

PHP 提供了一些流程控制的替代语法,包括 ifwhileforforeachswitch。 替代语法的基本形式是把左花括号 { 换成冒号 :,把右花括号 } 分别换成 endif;endwhile;endfor;endforeach; 以及 endswitch;

if

if 结构是很多语言包括 PHP 在内最重要的特性之一,它允许按照条件执行代码片段。PHP 的 if 结构和 C 语言相似:

  1. <?php
  2. $a = 1;
  3. $b = 2;
  4. if ($a > $b) {
  5. echo "a is bigger than b\n";
  6. }
  7. if ($a > $b) {
  8. echo "a is bigger than b\n";
  9. $b = $a;
  10. }
  11. if ($a > $b) {
  12. echo "a is greater than b\n";
  13. } else {
  14. echo "a is NOT greater than b\n";
  15. }
  16. if ($a > $b) {
  17. echo "a is bigger than b\n";
  18. } elseif ($a == $b) {
  19. echo "a is equal to b\n";
  20. } else {
  21. echo "a is smaller than b\n";
  22. }
  23. if ($a > $b):
  24. echo $a." is greater than ".$b."\n";
  25. elseif ($a == $b):
  26. echo $a." equals ".$b."\n";
  27. else:
  28. echo $a." is neither greater than or equal to ".$b."\n";
  29. endif;

和 HTML / JavaScript / CSS 混合使用:

  1. <?php if (condition): ?>
  2. html code to run if condition is true
  3. <?php else: ?>
  4. html code to run if condition is false
  5. <?php endif ?>

Switch

switch 语句类似于具有同一个表达式的一系列 if 语句。 很多场合下需要把同一个变量(或表达式)与很多不同的值比较,并根据它等于哪个值来执行不同的代码。switch 是松散比较。

  1. <?php
  2. // 整型的 switch 结构
  3. $integer = array_rand(range(0, 2));
  4. switch ($integer) {
  5. case 0:
  6. echo "integer equals 0\n";
  7. break;
  8. case 1:
  9. echo "integer equals 1\n";
  10. break;
  11. case 2:
  12. echo "integer equals 2\n";
  13. break;
  14. }
  15. // 字符串型的 switch 结构
  16. $string = array_rand(['foo' => 0, 'bar' => 1, 'baz' => 2]);
  17. switch ($string) {
  18. case "foo":
  19. echo "string is foo\n";
  20. break;
  21. case "bar":
  22. echo "string is bar\n";
  23. break;
  24. case "baz":
  25. echo "string is baz\n";
  26. break;
  27. }

这里如果 $i 等于 0,PHP 将执行所有的 echo 语句!如果 $i 等于 1,PHP 将执行后面两条 echo 语句。只有当 $i 等于 2 时,才会得到“预期”的结果——只显示“i equals 2”。

循环(Loop)

for

for 循环是 PHP 中最复杂的循环结构。for 循环的语法如下:

  1. for (expr1; expr2; expr3)
  2. statement
  3. expr1 在循环开始前无条件求值(并执行)一次。
  4. expr2 在每次循环开始前求值。如果值为 true,则继续循环,执行嵌套的循环语句。如果值为 false,则终止循环。
  5. expr3 在每次循环之后被求值(并执行)。

for 示例代码如下:

  1. <?php
  2. // Example 1
  3. for ($i = 1; $i <= 10; $i++) {
  4. echo $i."\n";
  5. }
  6. // Example 2
  7. for ($i = 1; ; $i++) {
  8. if ($i > 10) {
  9. break;
  10. }
  11. echo $i."\n";
  12. }
  13. // Example 3
  14. for ($i = 1, $j = 0; $i <= 10; $j += $i, print $i."\n", $i++);
  15. // Example 4
  16. $people = [
  17. ['name' => 'Kalle', 'salt' => 856412],
  18. ['name' => 'Pierre', 'salt' => 215863],
  19. ];
  20. $count = count($people);
  21. for ($i = 0, $size = $count; $i < $size; ++$i) {
  22. $people[$i]['salt'] = rand(000000, 999999);
  23. }
  24. var_export($people);

foreach

foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,
如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。

foreach 的示例代码

  1. <?php
  2. // Example 1 while 和 foreach 实现相同功能的代码
  3. $arr = ["foo", "bar", "baz"];
  4. // 输出数组中的当前元素和下一个元素的值,然后把数组的内部指针重置到数组中的第一个元素
  5. reset($arr);
  6. while (list(, $value) = each($arr)) {
  7. echo "Value: $value<br>\n";
  8. }
  9. foreach ($arr as $value) {
  10. echo "Value: $value<br />\n";
  11. }
  12. // Example 2 while 和 foreach 实现相同功能的代码
  13. $arr = ["one", "two", "three"];
  14. reset($arr);
  15. while (list($key, $value) = each($arr)) {
  16. echo "Key: $key; Value: $value<br />\n";
  17. }
  18. foreach ($arr as $key => $value) {
  19. echo "Key: $key; Value: $value<br />\n";
  20. }
  21. // Example 3 多维数组遍历
  22. $a = [];
  23. $a[0][0] = "a";
  24. $a[0][1] = "b";
  25. $a[1][0] = "y";
  26. $a[1][1] = "z";
  27. foreach ($a as $v1) {
  28. foreach ($v1 as $v2) {
  29. echo "$v2\n";
  30. }
  31. }
  32. // Example 4 用 list() 给嵌套的数组解包 1
  33. $array = [
  34. [1, 2],
  35. [3, 4],
  36. ];
  37. foreach ($array as list($a, $b)) {
  38. echo "A: $a; B: $b\n";
  39. }
  40. // Example 5 用 list() 给嵌套的数组解包 2
  41. $array = [
  42. [
  43. 'username' => "Sebastian",
  44. 'phone' => "13405871293",
  45. "address" => "广东省深圳市",
  46. ],
  47. [
  48. 'username' => "Kennedy",
  49. 'phone' => "15048773920",
  50. "address" => "广东省深圳市",
  51. ],
  52. ];
  53. foreach ($array as list('username' => $username, 'phone' => $phone)) {
  54. echo "$username 's phone is $phone\n";
  55. }
  56. // Example 6 使用 foreach 的替代语法
  57. $names = ['foo', 'bar', 'baz', 'qux'];
  58. foreach ($names as $value):
  59. echo "$value\n";
  60. endforeach;

while

while 循环是 PHP 中最简单的循环类型。只要 while 表达式的值为 true 就重复执行嵌套中的循环语句。表达式的值在每次开始循环时检查,所以即使这个值在循环语句中改变了,语句也不会停止执行,直到本次循环结束。while 示例代码如下:

  1. <?php
  2. // Example 1
  3. $i = 1;
  4. while ($i <= 10) {
  5. echo $i++."\n";
  6. }
  7. // Example 2
  8. $i = 1;
  9. while ($i <= 10):
  10. print $i."\n";
  11. $i++;
  12. endwhile;

do-while

do-while 循环和 while 循环非常相似,区别在于表达式的值是在每次循环结束时检查而不是开始时。和一般的 while 循环主要的区别是 do-while 的循环语句保证会执行一次。do-while 示例代码如下:

  1. <?php
  2. // Example 1 do-while 只执行一次
  3. $i = 0;
  4. do {
  5. echo $i."\n";
  6. } while ($i > 0);
  7. // Example 2 do-while 执行 5 次
  8. $i = 0;
  9. do {
  10. $i++;
  11. if ($i > 5) {
  12. break;
  13. }
  14. echo "i is not big enough\n";
  15. } while (1);

break / continue

break 结束当前 for,foreach,while,do-while 或者 switch 结构的执行。continue 在 for,foreach,while,do-while 或者 switch 结构用用来跳过本次循环中剩余的代码并在条件求值为真时开始执行下一次循环。break / continue 的示例代码如下:

  1. <?php
  2. $arr = ['one', 'two', 'three', 'four', 'stop', 'five'];
  3. while (list (, $val) = each($arr)) {
  4. if ($val != 'stop') {
  5. continue;
  6. }
  7. if ($val == 'stop') {
  8. echo "$val\n";
  9. break;
  10. }
  11. echo "$val<br />\n";
  12. }
  13. // 多层 continue
  14. $i = 0;
  15. while ($i++ < 5) {
  16. echo "Outer<br />\n";
  17. while (1) {
  18. echo "Middle<br />\n";
  19. while (1) {
  20. echo "Inner<br />\n";
  21. continue 3;
  22. }
  23. echo "This never gets output.<br />\n";
  24. }
  25. echo "Neither does this.<br />\n";
  26. }

goto

goto 操作符可以用来跳转到程序中的另一位置。该目标位置可以用目标名称加上冒号来标记,而跳转指令是 goto 之后接上目标位置的标记。PHP 中的 goto 有一定限制,目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中。goto 示例代码如下:

  1. <?php
  2. // Example 1
  3. goto a;
  4. echo "foo\n";
  5. a:
  6. echo "bar\n";
  7. // Example 2
  8. for ($i = 0, $j = 50; $i < 100; $i++) {
  9. while ($j--) {
  10. if ($j === 17) {
  11. goto end;
  12. }
  13. }
  14. }
  15. echo "i = $i\n";
  16. end:
  17. echo 'j hit 17';

错误处理(Error Handing)

错误和异常定义与类型(Error & Exception)

PHP 7 改变了大多数错误的报告方式。不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出。而异常是 Exception 类的对象,在遇到无法修复的状况时抛出。例如,远程 API 无响应,数据库查询失败,或者无法满足前置条件。这些状况被归纳为异常状况。出现问题时,异常常用于主动出击,委托职责,异常还可以用于防守,预测潜在的问题,减轻其影响。

这种 Error 异常可以像 Exception 异常一样被第一个匹配的 try / catch 块所捕获。如果没有匹配的 catch 块,
则调用异常处理函数(事先通过 set_exception_handler() 注册)进行处理。如果尚未注册异常处理函数,则按照传统方式处理:被报告为一个致命错误(Fatal Error)。

Error 类并非继承自 Exception 类,所以不能用 catch (Exception $e) { … } 来捕获 Error。在 PHP 7 以后,可以使用 Throwable 类来捕获 Error,或者你也可以用 catch (Error $e) { … },或者通过注册异常处理函数( set_exception_handler())来捕获 Error。

错误和异常抛出(Throw)

错误和异常实例化时可以赋值给变量,不过一定要把错误或异常抛出。抛出错误或异常后代码会立即停止执行,后续的 PHP 代码都不会运行。抛出错误或异常的方式是使用 throw 关键字,后面要跟着要抛出的 Throwable 实例。

  1. <?php
  2. // Throwable 能够捕获 Exception 和 Error
  3. throw new \Throwable("Something went wrong. Time for lunch!");
  4. // Exception 能够捕获 Exception,不能捕获 Error
  5. throw new \Exception("Something went wrong. Time for dinner!");
  6. // Error 能够捕获 Error,不能捕获 Exception
  7. throw new \Error("Something went wrong. Time for Sleep!");

错误和异常捕获(Try-Catch-Finally)

拦截并处理潜在错误或异常的方式是,把可能抛出异常的代码放在 try / catch 块中,示例代码如下:

  1. <?php
  2. // Example 1
  3. try {
  4. $pdo = new PDO('mysql://host=wrong_host;dbname=wrong_name');
  5. } catch (PDOException $e) {
  6. $code = $e->getCode();
  7. $message = $e->getMessage();
  8. echo 'Something went wrong.Check back soon, Please';
  9. exit;
  10. }
  11. // Example 2
  12. try {
  13. throw new Exception("Not a PDO exception\n");
  14. $pdo = new PDO('mysql://host=wrong_host;dbname=wrong_name');
  15. } catch (PDOException $e) {
  16. echo 'Caught PDO exception';
  17. } catch (Throwable $t) {
  18. // 处理其他异常
  19. echo $t->getMessage();
  20. }finally {
  21. // 这里的代码始终都会执行
  22. echo 'Always do this';
  23. }

错误和异常的部署配置(Deploy & Configuration)

// TODO