PHP 学习框架

入门概述(Introduction)

PHP 是一种被广泛应用的开放源代码的多用途脚本语言,它可嵌入到 HTML中,尤其适合 web 开发。

版本迭代(Version Iteration)

当前最新稳定版本是 PHP 7.1.X,相比 PHP 5.X 版本增加了不少新特性。PHP 引擎也大部分被重写,PHP 的运行速度也提到了极大的提升。建议使用 PHP 7.1.X 作为开发和生产的环境。

注释与代码分割(Comments & Split)

注释的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
// 注释方式一
/* 注释方式二 */
/**
* 注释方式三
*
* @param string name
*
* @return string
*/

代码分割的代码如下:

1
2
3
4
5
<?php
require("../path/file.php");
require_once("../path/file.php");
include("../path/file.php");
include_once("../path/file.php");

require()、require_once()、include()、include_once() 的异同:

  • 运行时
    • require()require_once()include()include_once() 的区别是带 once 后缀的会判断在这个文件之前是否已经加载过了文件,避免重复加载。
    • require()require_once() 加载文件时,如果出错,将会产生一个 E_COMPILE_ERROR 级别的错误,脚本将会终止运行。
    • include()include_once() 加载文件时,如果出错,只会产生一个 E_WARNING 级别的警告,脚本将会继续运行。
  • 性能上
    • include()include_once() 执行时,文件每次都要进行读取和评估。
    • require()require_once() 执行时,文件只处理一次。
    • include_once()require_once() 是在 include()require() 的基础上进步一步封装。
  • 返回值
    • 如果加载文件内使用 return 返回,那么 require()``、require_once() 加载文件成功会有该文件 return 值的返回,失败会产生致命错误。
    • 如果加载文件内使用 return 返回,那么 include()include_once() 加载文件成功会有该文件 return 值的返回,失败会抛出一个警告并返回 boolean(false)
    • 如果加载文件内没有使用 return 返回,那么 require()``、require_once()include()include_once() 加载文件成功会返回 int(1),失败分别会产生致命错误和抛出警告并返回 boolead(false)

构建与依赖管理(Builder & Dependence Manager)

PEAR

PEAR 是一个常用的依赖包管理器。PEAR 需要扩展包有专属的结构, 开发者在开发扩展包的时候要提前考虑为 PEAR 定制, 否则后面将无法使用 PEAR。

PEAR 安装扩展包的时候, 是全局安装的, 意味着一旦安装了某个扩展包, 同一台服务器上的所有项目都能用上, 当然, 好处是当多个项目共同使用同一个扩展包的同一个版本, 坏处是如果你需要使用不同版本的话, 就会产生冲突.

Composer 与 Packagist

Composer 是另一个常用的依赖包管理器。在 composer.json 文件中列出你项目所需的依赖包,加上一点简单的命令,Composer 将会自动帮你下载并设置你的项目依赖。Composer 有点类似于 Node.js 里的 NPM,或者 Ruby 世界里的 Bundler。

Packagist 是一个 Composer 官方的依赖包仓库,可以在 Packagist 上寻找想要的 PHP 依赖包。

语法基础(Syntax)

表达式(Expression)

变量(Variable)
  • 变量声明(Variable Declaration)
    • 基础变量
    • 预定义变量
    • 变量范围
    • 可变变量
    • 来自 PHP 之外的变量
  • 变量提升(Variable Hoisting)
  • 常量声明(Constants Declaration)
    • 语法
    • 预定义常量(魔术常量)

变量与常量声明(Variable Declaration)

1.基础变量

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

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

2.预定义变量

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

  • $GLOBALS:获取全局变量
  • $_SERVER:获取服务器相关信息
  • $_GET:获取 HTTP GET 请求数据
  • $_POST:获取 HTTP POST 请求数据
  • $_FILES:获取 HTTP 上传文件请求数据
  • $_COOKIE:获取 COOKIE 数据
  • $_SESSION:获取 SESSION 数据
  • $_REQUEST:获取 HTTP 请求数据
1
2
3
4
<?php
foreach($_SERVER as $key => $server){
echo $key."\n";
}

3.变量范围

变量的范围即它定义的上下文背景(也就是它的生效范围)。大部分的 PHP 变量只有一个单独的范围。可以使用 global 关键字声明全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
$a = 1;
$b = 2;
function Sum()
{
global $a, $b;
$b = $a + $b;
}
Sum();
echo $b;
?>
<?php
$a = 1;
$b = 2;
function Sum()
{
// $GLOBALS 是一个关联数组,每一个变量为一个元素,键名对应变量名,值对应变量的内容
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
Sum();
echo $b;
?>

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

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

4.可变变量

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

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

5.来自 PHP 之外的变量

  • HTML 表单(GET 和 POST)
    • 通过预定义变量 $_GET$_POST 获取。
    • 变量名中的点和空格被转换成下划线。例如 <input name="a.b" /> 变成了 $_REQUEST["a_b"]
  • HTTP Cookies
    • 如果要将多个值赋给一个 cookie 变量,必须将其赋成数组。

变量提升(Variable Hoisting)

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

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

常量声明(Constants Declaration)

1.语法

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

1
2
3
4
5
6
7
8
<?php
define("LUIS", "Luis");
const EDWARE = "Edware";
echo LUIS.' '.EDWARE."\n";
const ARR = ['luis', 'edware'];
var_dump(ARR);

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

  • __LINE__:文件中的当前行号。
  • __FILE__:文件的完整路径和文件名。
  • __DIR__:文件所在的目录。
  • __FUNCTION__:返回函数被定义时的名字(区分大小写)。
  • __CLASS__:返回该类被定义时的名字(区分大小写)。
  • __TRAIT__:返回 trait 被定义时的名字(区分大小写)。
  • __METHOD__:返回该方法被定义时的名字(区分大小写)。
  • __NAMESPACE__:当前命名空间的名称(区分大小写)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
namespace LuisEdware\Code;
$line = __LINE__."\n";
$filePath = __FILE__."\n";
$dirPath = __DIR__."\n";
function testFunc(){
return __FUNCTION__."\n";
}
trait TestTrait
{
public function testTrait(){
return __TRAIT__."\n";
}
}
class TestClass
{
use TestTrait;
public function __construct(){
return __CLASS__."\n";
}
public function testMethod(){
return __METHOD__."\n";
}
public function testNameSpace(){
return __NAMESPACE__."\n";
}
}
echo $line;
echo $filePath;
echo $dirPath;
echo testFunc();
$class = new TestClass;
echo $class->testMethod();
echo $class->testTrait();
echo $class->testNameSpace();
变量赋值(Assignment)
  • 传值与传引用(Pass-by-Value & Pass-by-Reference)
  • 连续赋值(Continuous Assignment)
  • 解构赋值(Destructuring Assignment)
  • 复合类型拷贝(Copy Composite DataTypes)

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

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

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

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

2.连续赋值(Continuous Assignment)

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

3.解构赋值(Destructuring Assignment)

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

4.复合类型拷贝(Copy Composite DataTypes)

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

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

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

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

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

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

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

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

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

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

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

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

1.基本运算符(Basic Operators)

1.1.算术运算符(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 次平方

1.2.比较运算符(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 开始提供。

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

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

1.4.执行运算符(Execution Operator)

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

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

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

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

一个简单的示例脚本

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

1.6.逻辑运算符(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。

1.7.字符串运算符(String Operator)

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

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

1.8.数组运算符(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。

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

instanceof 用于确定一个 PHP 变量是否属于某一类 class 的实例:

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

2.位运算符(Bit Operator)

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

3.运算符重载(Operator Overloading)

在计算机程序设计中,运算符重载是多态的一种。这里,运算符被当作多态函数,他们的行为随着其参数类型的不同而不同。
简而言之,运算符重载使程序员能够根据运算子类型的不同来决定运算符功能的不同。PHP 支持运算符重载。

PHP 5.6 之后,GMP 支持运算符重载,并且造型成数值类型,这使得使用 GMP 的代码更加直观。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$a = gmp_init(42);
$b = gmp_init(17);
if (version_compare(PHP_VERSION, '5.6', '<')) {
echo gmp_intval(gmp_add($a, $b)), PHP_EOL;
echo gmp_intval(gmp_add($a, 17)), PHP_EOL;
echo gmp_intval(gmp_add(42, $b)), PHP_EOL;
} else {
echo $a + $b, PHP_EOL;
echo $a + 17, PHP_EOL;
echo 42 + $b, PHP_EOL;
}

4.运算符优先级(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 类来实现的。

创建简单的闭包

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

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

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

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

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

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

以下是使用 use 关键字附加闭包的状态

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

以下是使用 bindTo() 方法附加闭包的状态

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

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

惰性求值(Lazy Evaluation)

// TODO

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

分支选择(Branch)

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

  • if
  • switch

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
$a = 1;
if ($a > $b)
echo "a is bigger than b";
if ($a > $b) {
echo "a is bigger than b";
$b = $a;
}
if ($a > $b) {
echo "a is greater than b";
} else {
echo "a is NOT greater than b";
}
if ($a > $b) {
echo "a is bigger than b";
} elseif ($a == $b) {
echo "a is equal to b";
} else {
echo "a is smaller than b";
}
if($a > $b):
echo $a." is greater than ".$b;
elseif($a == $b): // 注意使用了一个单词的 elseif
echo $a." equals ".$b;
else:
echo $a." is neither greater than or equal to ".$b;
endif;
?>

php if 和 HTML / JavaScript / CSS 混合使用:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
// 整型的 switch 结构
switch ($integer) {
case 0:
echo "integer equals 0";
break;
case 1:
echo "integer equals 1";
break;
case 2:
echo "integer equals 2";
break;
}
// 字符串型的 switch 结构
switch ($string) {
case "apple":
echo "string is apple";
break;
case "bar":
echo "string is bar";
break;
case "cake":
echo "string is cake";
break;
}

switch 语句一行接一行地执行(实际上是语句接语句)。开始时没有代码被执行。仅当一个 case 语句中的值和 switch 表达式的值匹配时 PHP 才开始执行语句,直到 switch 的程序段结束或者遇到第一个 break 语句为止。如果不在 case 的语句段最后写上 break 的话,PHP 将继续执行下一个 case 中的语句段。

1
2
3
4
5
6
7
8
9
<?php
switch ($i) {
case 0:
echo "i equals 0";
case 1:
echo "i equals 1";
case 2:
echo "i equals 2";
}

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

循环(Loop)
  • for
  • foreach
  • while
  • do-while
  • break / continue
  • goto

1.for

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

1
2
for (expr1; expr2; expr3)
statement

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

for 示例代码如下:

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

2.foreach

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

foreach 的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
// Example 1 while 和 foreach 实现相同功能的代码
$arr = array("apple", "baidu", "coding");
reset($arr);
while (list(, $value) = each($arr)) {
echo "Value: $value<br>\n";
}
foreach ($arr as $value) {
echo "Value: $value<br />\n";
}
// Example 2 while 和 foreach 实现相同功能的代码
$arr = array("one", "two", "three");
reset($arr);
while (list($key, $value) = each($arr)) {
echo "Key: $key; Value: $value<br />\n";
}
foreach ($arr as $key => $value) {
echo "Key: $key; Value: $value<br />\n";
}
// Example 3 多位数组遍历
$a = array();
$a[0][0] = "a";
$a[0][1] = "b";
$a[1][0] = "y";
$a[1][1] = "z";
foreach ($a as $v1) {
foreach ($v1 as $v2) {
echo "$v2\n";
}
}
// Example 4 用 list() 给嵌套的数组解包 1
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a, $b)) {
echo "A: $a; B: $b\n";
}
// Example 5 用 list() 给嵌套的数组解包 2
$array = [
[
'username' => "Luis.Edward",
'phone' => "13405871293",
"address" => "广东省中山市",
],
[
'username' => "Ann.Eason",
'phone' => "15048773920",
"address" => "广东省中山市",
]
];
foreach($array as list('username' => $username,'phone' => $phone)){
echo "$username 's phone is $phone\n";
}
// Example 6 使用 foreach 的替代语法
$names = ['Luis', 'Edware', 'Ann', 'Eason'];
foreach($names as $value):
echo "$value\n";
endforeach;

3.while

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

while 的示例代码如下:

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

4.do-while

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

do-while 的实例代码:

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

5.break / continue

  • break 结束当前 forforeachwhiledo-while 或者 switch 结构的执行。
  • continueforforeachwhiledo-while 或者 switch 结构用用来跳过本次循环中剩余的代码并在条件求值为真时开始执行下一次循环。

break / continue 的示例代码如下:

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

6.goto

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

goto 的实例代码如下:

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

错误处理(ErrorHandling)
  • 错误和异常定义与类型(Error & Exception)
  • 错误和异常抛出(Throw)
  • 错误和异常捕获(Try-Catch-Finally)
  • 错误和异常的部署配置(Deploy & Configuration)

1.错误和异常定义与类型(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

2.错误和异常抛出(Throw)

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

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

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

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

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

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

// TODO

函数(Function)

函数定义(Function Definition)

任何有效的 PHP 代码都有可能出现在函数内部,甚至包括其它函数和类定义。

函数名和 PHP 中的其它标识符命名规则相同。有效的函数名以字母或下划线打头,后面跟字母,数字或下划线。可以用正则表达式表示为:[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff]*

PHP 中的所有函数和类都具有全局作用域,可以定义在一个函数之内而在之外调用,反之亦然。

PHP 不支持函数重载,也不可能取消定义或者重定义已声明的函数。

PHP 函数示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
// Example #1 函数中的函数
function foo()
{
function bar()
{
echo "I don't exist until foo() is called.\n";
}
}
/* 现在还不能调用bar()函数,因为它还不存在 */
foo();
/* 现在可以调用bar()函数了,因为foo()函数
的执行使得bar()函数变为已定义的函数 */
bar();
// Example #2 递归函数
function recursion($a)
{
if($a < 20){
echo "$a\n";
recursion(++$a);
}
}
/* 要避免递归函数 / 方法调用超过 100 - 200 层,因为可能
会使堆栈崩溃从而使当前脚本终止。无限递归可视为编程错误 */

函数参数(Function Args)
  • 默认参数(Default Arguments)
  • 必要参数(Required Arguments)
  • 可变参数(Variable Arguments)

1.默认参数

函数可以定义 C++ 风格的标量参数默认值,默认值必须是常量表达式,
不能是诸如变量,类成员,或者函数调用等,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
// Example #1 标量类型默认参数
function makecoffee($type = "cappuccino")
{
return "Making a cup of $type.\n";
}
echo makecoffee(); // 输出 Making a cup of cappuccino.
echo makecoffee(null); // 输出 Making a cup of .
echo makecoffee("espresso"); // 输出 Making a cup of espresso.
// Example #2 非标量类型作为默认参数
function makecoffee($types = ["cappuccino"], $coffeeMaker = NULL)
{
$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
return "Making a cup of ".join(", ", $types)." with $device.\n";
}
echo makecoffee();
echo makecoffee(["cappuccino", "lavazza"], "teapot");
// Example #3 默认参数类型严格模式
declare(strict_types=1);
function sum(int $a, int $b) {
return $a + $b;
}
try {
var_dump(sum(1, 2)); // 通过
var_dump(sum(1.5, 2.5)); // 报错
} catch (TypeError $e) {
echo 'Error: '.$e->getMessage();
}

2.必要参数

PHP 函数的必要参数示例代码如下:

1
2
3
4
5
6
7
8
9
<?php
// 使用该函数时,必须传递两个参数
$a = "Luis";
$b = " Edward";
function requireArgs($a, $b)
{
return $a.$b;
}
echo requireArgs($a, $b);

3.可变参数

PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 ... 语法实现;在 PHP 5.5 及更早版本中,使用函数 func_num_args()func_get_arg(),和 func_get_args() 来实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
// Example #1 使用 ... 访问函数参数
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
// 使用 func_get_args() 访问函数参数
function sum() {
$acc = 0;
foreach (func_get_args() as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
// Example #2 使用 ... 提供函数参数
function add($a, $b) {
return $a + $b;
}
echo add(...[1, 2])."\n";
$a = [1, 2];
echo add(...$a);
// Example #3 类型声明可变参数
function total_intervals($unit, DateInterval ...$intervals) {
$time = 0;
foreach ($intervals as $interval) {
$time += $interval->$unit;
}
return $time;
}
$a = new DateInterval('P1D');
$b = new DateInterval('P2D');
echo total_intervals('d', $a, $b).' days';
// 报错
echo total_intervals('d', null);

函数调用(Function Call)
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// 普通函数调用
function test_normal_function()
{
return true;
}
test_normal_function();
// 匿名函数调用
$closure = function(){
return true;
};
$closure();
装饰器(Decorator)

PHP 目前不存在装饰器的语法糖,需要通过手动编写或设计模式来实现装饰器,实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
// Example #1 函数实现装饰器的效果
function text(){
echo "hello World";
}
function leet_text(){
echo text().", Luis Edward\n";
}
leet_text();
// Example #2 OOP 实现装饰器的效果
class Text {
protected $string;
/**
* @param string $string
*/
public function __construct($string) {
$this->string = $string;
}
public function __toString() {
return $this->string;
}
}
class LeetText {
protected $text;
/**
* @param Text $text A Text object.
*/
public function __construct($text) {
$this->text = $text;
}
public function __toString() {
return strtr($this->text->__toString(), 'eilto', '31170');
}
}
$text = new LeetText(new Text('Hello world'));
echo $text;

迭代器与生成器(Iterator & Generator)

参考文献

1.迭代器

PHP 迭代器的实现离不开一个无法被单独实现的基本抽象接口:Traversable (遍历接口),它必须由 IteratorAggregateIterator 接口实现。
Traversable 接口没有任何方法,它的作用仅仅是作为所有可遍历类的基本接口。

迭代是指反复执行一个过程,每执行一次叫做一次迭代。例如 foreach 遍历一个 array 变量,foreach 实际上在每一次迭代过程都会调用该对象的一个方法,让数组在自己内部进行一次变动(迭代),随后通过另一个方法取出当前数组对象的键和值。这样一个通过外部遍历其内部数据的对象就是一个迭代器对象,其遵循的统一的访问接口就是一个 迭代器接口

PHP 迭代器接口摘要:

1
2
3
4
5
6
7
8
9
<?php
Iterator extends Traversable
{
abstract public mixed current (void)
abstract public scalar key (void)
abstract public void next (void)
abstract public void rewind (void)
abstract public boolean valid (void)
}

  • Iterator::current:返回当前元素
  • Iterator::key:返回当前元素的键
  • Iterator::next:向前移动到下一个元素
  • Iterator::rewind:返回到迭代器的第一个元素
  • Iterator::valid:检查当前位置是否有效

PHP 迭代器的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php
class myIterator implements Iterator
{
private $position = 0;
private $array = [
"firstElement",
"secondElement",
"lastElement",
];
public function __construct()
{
$this->position = 0;
}
function rewind()
{
var_dump(__METHOD__);
$this->position = 0;
}
function current()
{
var_dump(__METHOD__);
return $this->array[$this->position];
}
function key()
{
var_dump(__METHOD__);
return $this->position;
}
function next()
{
var_dump(__METHOD__);
++$this->position;
}
function valid()
{
var_dump(__METHOD__);
return isset($this->array[$this->position]);
}
}
$it = new myIterator;
foreach ($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}

PHP 聚合式迭代器接口摘要:

1
2
3
4
5
6
<?php
IteratorAggregate extends Traversable
{
/* 获取一个外部迭代器 */
abstract public Traversable getIterator (void);
}

PHP 聚合式迭代器接口的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
// Example #1
class myObject implements IteratorAggregate
{
public $property1 = "Public property one";
public $property2 = "Public property two";
public $property3 = "Public property three";
public function __construct() {
$this->property4 = "last property";
}
public function getIterator() {
return new ArrayIterator($this);
}
}
$obj = new myObject;
foreach($obj as $key => $value) {
echo "$key => $value\n";
}
// Example #2
class Collection implements IteratorAggregate
{
private $items = [];
public function __construct($items = [])
{
$this->items = $items;
}
public function getIterator()
{
return (function () {
reset($this->items);
while(list($key, $val) = each($this->items)) {
yield $key => $val;
}
})();
}
}
$data = [ 'A', 'B', 'C', 'D' ];
$collection = new Collection($data);
foreach ($collection as $key => $val) {
echo sprintf("[%s] => %s\n", $key, $val);
}

PHP 数组式访问(ArrayAccess)接口摘要:

1
2
3
4
5
6
7
8
<?php
ArrayAccess
{
abstract public boolean offsetExists (mixed $offset)
abstract public mixed offsetGet (mixed $offset)
abstract public void offsetSet (mixed $offset, mixed $value)
abstract public void offsetUnset (mixed $offset)
}

  • ArrayAccess::offsetExists:检查一个偏移位置是否存在
  • ArrayAccess::offsetGet:获取一个偏移位置的值
  • ArrayAccess::offsetSet:设置一个偏移位置的值
  • ArrayAccess::offsetUnset:复位一个偏移位置的值

PHP 数组式访问(ArrayAccess)接口的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<?php
// Example #1 Basic Usage
class obj implements arrayaccess
{
private $container = [];
public function __construct()
{
$this->container = [
"one" => 1,
"two" => 2,
"three" => 3,
];
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$obj = new obj;
var_dump(isset($obj["two"]));
var_dump($obj["two"]);
unset($obj["two"]);
var_dump(isset($obj["two"]));
$obj["two"] = "A value";
var_dump($obj["two"]);
$obj[] = 'Append 1';
$obj[] = 'Append 2';
$obj[] = 'Append 3';
print_r($obj);
// Example #2 使用访问数组的方式去访问对象
<?php
/**
* ArrayAndObjectAccess
* Yes you can access class as array and the same time as object
*
* @author Yousef Ismaeil <cliprz@gmail.com>
*/
class ArrayAndObjectAccess implements ArrayAccess
{
/**
* Data
*
* @var array
* @access private
*/
private $data = [];
/**
* Get a data by key
*
* @param $key string The key data to retrieve
*
* @access public
*/
public function &__get($key)
{
return $this->data[$key];
}
/**
* Assigns a value to the specified data
*
* @param $key string The data key to assign the value to
* @param $value mixed The value to set
*
* @access public
*/
public function __set($key, $value)
{
$this->data[$key] = $value;
}
/**
* Whether or not an data exists by key
*
* @param string An data key to check for
*
* @access public
* @return boolean
* @abstracting ArrayAccess
*/
public function __isset($key)
{
return isset($this->data[$key]);
}
/**
* Unset an data by key
*
* @param $key string The key to unset
*
* @access public
*/
public function __unset($key)
{
unset($this->data[$key]);
}
/**
* Assigns a value to the specified offset
*
* @param $offset string The offset to assign the value to
* @param mixed
*
* @access public
* @abstracting ArrayAccess
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
/**
* Unset an offset
*
* @param $offset string The offset to unset
*
* @access public
* @abstracting ArrayAccess
*/
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->data[$offset]);
}
}
/**
* Whether or not an offset exists
*
* @param $offset string An offset to check for
*
* @access public
* @return boolean
* @abstracting ArrayAccess
*/
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
/**
* Returns the value at specified offset
*
* @param $offset string The offset to retrieve
*
* @access public
* @return mixed
* @abstracting ArrayAccess
*/
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->data[$offset] : null;
}
}
$foo = new ArrayAndObjectAccess();
// Set data as array and object
$foo->firstName = 'Luis';
$foo->lastName = 'Edward';
// Call as object
echo 'firstName as object ' . $foo->firstName . "\n";
// Call as array
echo 'lastName as array ' . $foo['lastName'] . "\n";
// Reset as array
$foo['firstName'] = 'AnnEason';
$foo[] = 'Luis Edward';
echo $foo['firstName'] . "\n";
var_export($foo);

2.生成器

生成器提供了一种更容易的方法来实现简单对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。

生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,
或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是,
生成器可以根据需要 yield 多次,以便生成需要迭代的值。

一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,
然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。

做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到 1K 字节的内存,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
function xrange($start, $limit, $step = 1)
{
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException('Step must be +ve');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be -ve');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
/*
* 注意下面range()和xrange()输出的结果是一样的。
*/
echo 'Single digit odd numbers from range(): ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n";
echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}

一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以 yield 生成许多它所需要的值。

当一个生成器被调用的时候,它返回一个可以被遍历的对象.当你遍历这个对象的时候,PHP 将会在每次需要值的时候调用生成器函数,
并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。

一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。

生成器函数的核心是 yield 关键字。它最简单的调用形式看起来像一个 return 申明,不同之处在于普通 return 会返回值并终止函数的执行,
yield 会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
// Example #1 Basic Usage
function gen_one_to_three()
{
for ($i = 1; $i <= 3; $i++) {
//注意变量$i的值在不同的yield之间是保持传递的。
yield $i;
}
}
$generator = gen_one_to_three();
foreach ($generator as $value) {
echo "$value\n";
}
// Example #2 指定键名来生成值
$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;
function input_parser($input)
{
foreach (explode("\n", $input) as $line) {
$value = explode(';', $line);
$key = array_shift($value);
yield $key => $value;
}
}
foreach (input_parser($input) as $id => $fields) {
echo "$id:\n";
echo " $fields[0]\n";
echo " $fields[1]\n";
}

在 PHP 7 中,生成器委托可以允许用户通过 yield from 关键字从其他生成器、数组或 Traverable 对象 yield 值。
然后外部生成器将会从内部生成器、Traverable 对象、数组 yield 所有值,直到所有值都被 yield 完毕,外部生成器才会继续执行。

如果生成器与 yield 一起使用,则表达式的 yield 将返回内部生成器返回的任何值。

PHP yield form 的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?php
// Example #1 Basic Usage of yield from
function count_to_ten()
{
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
yield 9;
yield 10;
}
function seven_eight()
{
yield 7;
yield from eight();
}
function eight()
{
yield 8;
}
foreach (count_to_ten() as $num) {
echo "$num ";
}
// Example #2 yield from and return values
function count_to_ten2()
{
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
return yield from nine_ten();
}
function seven_eight()
{
yield 7;
yield from eight();
}
function eight()
{
yield 8;
}
function nine_ten()
{
yield 9;
return 10;
}
$gen = count_to_ten();
foreach ($gen as $num) {
echo "$num ";
}
echo $gen->getReturn();

yield 会返回一个 Generator 对象,Generator 类实现了 Iterator 接口,
从本质上来讲,生成器也属于迭代器的一种。Generator 类摘要如下:

1
2
3
4
5
6
7
8
9
10
11
<?php
Generator implements Iterator {
public mixed current (void)
public mixed key (void)
public void next (void)
public void rewind (void)
public mixed send (mixed $value)
public void throw (Exception $exception)
public bool valid (void)
public void __wakeup (void)
}

  • Generator::current:返回当前产生的值
  • Generator::key:返回当前产生的键
  • Generator::next:生成器继续执行
  • Generator::rewind:重置迭代器
  • Generator::send:向生成器中传入一个值
  • Generator::throw:向生成器中抛入一个异常
  • Generator::valid:检查迭代器是否被关闭
  • Generator::__wakeup:序列化回调

类与对象(Class & Object)

类定义(Class Definition)
  • 类属性定义(Class Property Definition)
  • 类方法定义(Class Methods Definition)
    • 构造函数(Constructor Function)
    • 析构函数(Destructor Function)
    • Getter / Setter
  • 访问控制(Access Control)
    • 访问修饰符(Access Modifiers)
    • 私有数据(Private)
对象(Object)
  • 实例化(Instantiation)
  • 单例模式(Singleton Pattern)
  • this
类继承(Inheritance)
内部类(Inner Class)
反射/自省(Reflect)

输入输出流(IO Stream)

模块(Modularity)

数据结构(Data Structure)

类型与值判断(Type Judgement)

引用或值相等(Reference/Value Equality)
动态类型检查(Runtime Type Checking)
常用属性判断(Property Checking)
  • 空间占用(Size / Length)
  • 存在性(Existing)

基本类型(Basic)

数值类型(Numeric)
  • 随机数(Random)
  • 科学计算(Scientific Computing)
  • 类型转换(Type Conversion)
空类型(Nullable)
布尔类型(Bool)
可选类型(Optional)
枚举类型(Enum)

字符串类型(String)

增删复替(Manipulation)
  • 格式化字符串(Format)
索引遍历(Index & Traversal)
类型编码(type & encode)*
  • 字符集
  • URL/HTML
  • 拼音
模式匹配(Pattern Match)
  • 正则表达式(RegEx)
  • 字符串校验(Valid)
  • 模糊搜索(Fuzzy Search)

时间与日期(DateTime)

时间解析(Parse)
  • 时间戳(TimeStamp)
  • 时间日期字符串(DateTimeString)
  • 时区转换(TimeZone)
  • 时间比较(Comparison)
时间展示(Display)
  • 格式化(Format)
  • 相对格式(Relative Format)
  • 时长(Duration)
  • i18n(国际化)
时间操作(Manipulation)
  • 读取与设置(Get & Set)
  • 时间增减(Add & Subtract)
  • 时间偏移计算(Diff)
日历(Calendar)

序列类型(IndexedCollection)

数组(Array)
  • 增删复替(Manipulation)
  • 索引遍历(Index & Traversal)
  • 转换(Transform)
    • map/flatMap
    • reduce
    • filter
    • sort
集合(Set)

索引类型(KeyedCollection)

映射
  • 增删复替(Manipulation)
  • 索引遍历(Index & Traversal)

不可变对象(Immutable)

流(Stream)

序列化与反序列化(Serialization)

  • Json
  • XML
  • CSV

功能(Functionality)

存储(Storage)

缓存(Cache)
数据库(DataBase)
  • MySQL
  • Redis
  • MongoDB
文件系统(FileSystem)
  • FilePath(文件寻址)
  • Monitor(文件监控)

网络(Network)

网络管理(Network Management)
套接字(Socket)
HTTP 访问(HTTP Client)
  • URI 处理工具集(URI Handler)
  • 请求构造(Request Generator)
  • 请求执行(Request Executor)
  • 响应解析(Responsive)
  • 复杂请求管理(Advanced Request)
WebSocket
远程调用(RPC)

系统进程(SysProc)

切面编程(AOP)
并发编程(ConcurrentProgramming)
  • 线程(Thread)
    • 线程池(ThreadPool)
    • 协程(Coroutines)
  • 并发控制(ConcurrencyControl)
    • 锁(Lock)
    • 锁与事务(Atomic & Transaction)
    • 数据一致性(Consistency)
  • 异步模式(AsynchronousPattern)
    • 回调(Callback)
    • Promise / Future /Defer
    • Generator
    • Async / Await
系统调用(System)
响应式编程(Reactive)
  • RxExtension
  • Actor
  • 流式 API(StreamAPI)

工程实践(Engineering Practices)

样式指南(StyleGuide)

  • 代码风格与约定(CodeStyle)
    • 命名约定(NamingConventions)
    • 文档与注释(Documentation/Comments)
  • 项目/模块架构(Architecture)
  • 语法检查(Lint)

设计模式(DesignPattern)

  • SOLID
  • Observable

性能优化&代码优化(Performance & CodeEfficiency)

测试与发布(Test & Release)

调试(Debug)
Test(测试)
  • 断言(Assert)
  • Mock & Stub
  • TestRunner
发布(Release)
日志(Log)

进阶(Advanced)

泛型编程(Generics)

元编程(Meta Programming)

反射与代理(Reflection & Proxy)
代码生成(Code Generation)
依赖注入/控制反转(DI/IOP)

函数式编程(Functional Programming)

响应式编程(Reactive Programming)

EventStream
TFRP
RxExtension
Actor

内存管理(Memory Management)

内存结构(Memory Structure)
内存分配(Memory Allocation)
垃圾回收(Garbage Collection)

数据结构与算法(Algo Data Structure)