PHP
由于PHP 5及之前的版本历史太悠久, 语言层级的错误五花八门, 需要注意辨认网络上的代码使用的PHP版本.
PHP可能是市场占比最高的服务端语言, 但有相当比例的使用者是非专业用户.
PHP的代码经常有很多错误实践, 这主要因为它的代码非常容易编写猴子补丁.
PHP可能是市场占比最高的服务端语言, 但有相当比例的使用者是非专业用户.
PHP的代码经常有很多错误实践, 这主要因为它的代码非常容易编写猴子补丁.
2015年发布的PHP 7被认为是现代PHP时代的开始.
现代PHP受到JavaScript/TypeScript及其他现代编程语言和工具链的强烈启发.
现代PHP受到JavaScript/TypeScript及其他现代编程语言和工具链的强烈启发.
现代PHP比过去快得多, 能够在多项性能基准测试里击败Python和Ruby.
https://www.php.net/manual/zh/migration70.incompatible.php
总的来说, PHP 5到PHP 7的改变让PHP从一团彻底的混沌变得较为正常.
官方的迁移指南暴露出了PHP 5的很多设计是多么不正常, 很高兴这些有问题的行为都在新版本里被改正了.
官方的迁移指南暴露出了PHP 5的很多设计是多么不正常, 很高兴这些有问题的行为都在新版本里被改正了.
一个用C++编写的高性能PHP异步事件驱动网络通信引擎.
使用Swoole可以将PHP作为一般意义上的服务器端语言, 而不仅仅是Web服务语言使用.
使用Swoole可以将PHP作为一般意义上的服务器端语言, 而不仅仅是Web服务语言使用.
- PHP代码与HTML混写, 因此它可以直接作为模板引擎使用, 现代PHP较少依赖此特性.
- 基于文件系统的路由, 现代PHP较少依赖此特性.
- 一次性执行, PHP的每个进程只响应一个请求, 代码使用的内存空间会在响应完毕后立即销毁,
PHP因此非常依赖外部状态, 像Redis这样的服务对PHP很重要. - 优点: 这使得PHP代码很难积累内存泄漏问题.
- 缺点: 每次运行PHP代码, 都需要重新初始化相关组件, 导致PHP代码的效率很差.
- 缺乏泛型支持.
- 社区还没有完全走向异步化.
- 语言本身与Web服务这一应用场景绑定过深.
自PHP 7开始, 大多数错误都会以内部错误类Error的异常抛出.
Error类(PHP内部错误)和Exception类(用户定义的错误)没有继承关系,
不能通过Exception来捕获.
Error类(PHP内部错误)和Exception类(用户定义的错误)没有继承关系,
不能通过Exception来捕获.
可以用set_execption_handler捕获传播到顶级的异常.
// 抛出异常throw new Exception('Hello World');// 将throw作为表达式使用(自PHP 8.0开始可用)do_something() or throw new Exception('Hello World');// 抑制错误(set_error_handler设置的错误处理函数仍然会被调用)@do_something(); // 抑制后表达式返回假值// 借用抑制错误来单行处理处理错误@do_something() or die('Failed'); // die函数是exit函数的别名// 捕获异常try { // ...} catch (ExceptionA | ExceptionB $e) { // ...} catch (Exception $e) { // ...} finally { // ...}
// 声明命名空间, 一个文件里可以定义多个命名空间, 第一个命名空间必须是文件的第一条语句namespace MyProject;const MYCONST = 1;// '\'可以指代当前命名空间$a = \MYCONST;// 等价于(此处的namespace相当于类中的self)$a = namespace\MYCONST;// 等价于$a = MyProject\MYCONST;// 声明分层的命名空间, 用'\'作为分隔符namespace MyProject\Sub\Level;// 大括号语法(推荐)namespace MyProject { const MYCONST = 1;}// 导入命名空间use MyProject\Sub\Level;// 以别名形式导入use MyProject\Sub\Level as NewName;// 导入命名空间中的常量(也可以导入其他内容)use const MyProject\Sub\Level\MYCONST;
class SimpleClass extends BaseClass { // 类常量, 没有访问修饰符时默认为public const CONSTANT = 'something'; // 属性必须有访问修饰符 public ?string $prop; // 自PHP 8.1开始支持只读修饰符 public readonly string $readonlyProp; // 构造函数(是魔术方法的一种) // 没有访问修饰符时默认为public function __construct(int $x /* 一般参数 */, protected int $y /* 带修饰符的构造器参数会被提升为属性 */) { parent::__construct(); //... $this->prop = 'value'; } // 析构函数(是魔术方法的一种) function __destruct(): void { parent::__destruct(); // ... } // 如果方法覆盖了父类的方法, 则方法签名必须兼容, 否则会抛出错误 function method() { parent::method(); // ... } // 静态方法 static function staticMethod(): static { return new static(); }}SimpleClass::CONSTANT;SimpleClass::staticMethod();$instance = new SimpleClass();$instance->method();// 带有null检查的成员访问$instance?->method();// 复制一个对象实例$instanceClone = clone $instance
用于定义抽象类或抽象方法.
final修饰的方法不能被子类覆盖.
final修饰的类不能被继承.
final修饰的类不能被继承.
self总是指向它定义位置所在的类.
被调用的方法属于父类时, self会指向父类而不是子类.
被调用的方法属于父类时, self会指向父类而不是子类.
当static在方法中作为一个引用使用时, 可以用static指代该方法被调用时的类本身.
即使被调用的方法属于父类, static仍然会指向调用时的那个子类.
即使被调用的方法属于父类, static仍然会指向调用时的那个子类.
// 创建匿名类的实例new class { // ...}
interface Interface { // 接口中定义的方法必须是public. public function method(); // 接口可以定义常量, 并且常量可以直接以`接口名::常量名`的方式访问 const CONTANT = 'interface constant';}// 继承接口interface SubInterface extends Interface { // ...}// 实现接口, 一个类可以同时实现多个接口class Class implements Interface, SubInterface { // ...}
trait是一种基于组合的代码复用机制, trait可以在trait, class, enum内使用.
支持抽象成员和静态成员.
支持抽象成员和静态成员.
trait SomeTrait { public $prop = 'value'; public function method() { // ... }}class A { use SomeTrait; // 可以用逗号隔开多个trait}class B { use SomeTrait;}
trait Trait { public function methodA() { // ... } public function methodB() { // ... }}class Class { use Trait { methodA as private; // 修改访问修饰符 methodB as anotherMethod; // 重命名 }}
当一个类使用多个trait, 且trait内有同名的方法时, 需要手动解决冲突.
trait A { public function method() { // ... }}trait B { public function method() { // ... }}// 通过手动选择一个方法来解决冲突class Class { use A, B { B::method insteadof A; // 用B中的method替代A中的method, 最终使用的是B::method. }}// 通过重命名一个方法来解决冲突class Class { use A, B { B::method insteadof A; // 用B中的method替代A中的method, 最终使用的是B::method. A::method as methodA; // 将A中的method重命名为methodA }}
类型名可以用于现代PHP支持的类型声明.
// 开启类型严格模式declare(strict_types=1);// 查看变量类型echo gettype($variable);// 输出变量var_dump($variable);// 强制类型转换((bool) "");
A | B // unionA & B // intersection
- void
- never
- static
有两个常量,
true
和 false
, 使用时不区分大小写.出于历史原因, float还有一个别称叫double, 实际上是一样的类型.
有一个特殊值NAN, 该值需要通过
有一个特殊值NAN, 该值需要通过
is_nan()
函数判定.PHP的字符串是可写的, 允许原地修改某个下标的字符.
使用
主要表达方式:
使用
.
运算符连接字符串.主要表达方式:
- 单引号字符串: 不会发生转义和变量解析的字符串, 原生支持用来表示多行字符串.
- 双引号字符串: 会发生转义和变量解析的字符串.
array的命名有误, 它其实是一个有序的
array的赋值是值拷贝而不是引用复制, 如果需要引用复制, 则需要手动在数组前加上取引用运算符
Map<string | int, mixed>
, 因此可以用来表示各种对象.array的赋值是值拷贝而不是引用复制, 如果需要引用复制, 则需要手动在数组前加上取引用运算符
&
.数组的相等性可以通过
===
判断.// 通过构造函数创建映射$array = array( 'foo' => 'bar', 'bar' => 'foo');// 通过语法创建映射(推荐)$array = [ 'foo' => 'bar', 'bar' => 'foo'];// 通过构造函数创建数组$array = array('foo', 'bar');// 通过语法创建数组(推荐)$array = ['foo', 'bar'];// 在数组尾部追加一个元素, 数组尾部是由最大的int索引决定的$array[] = 'hello world';// 解构赋值(自PHP 7.1起可用)[$a, $b, $c] = [1, 2, 3]// 等价于list(, $second) = [1, 2, 3]// 删除数组元素, 这只会将对应索引位置的元素移除, 数组本身不会用后面的元素向前补充unset($array[1]);// 用array_values函数可以重建索引$array = array_values($array);// 删除数组unset($array);// 遍历数组foreach ($array as $value) { // ...}// 遍历类实例的可见属性foreach ($instance as $key => $value) { // ...}
从PHP 7.2开始可用.
作为类型声明使用, 代表任何PHP类的实例.
作为类型声明使用, 代表任何PHP类的实例.
作为类型声明使用, 代表一个可调用的函数.
PHP的一个奇葩设计是它在将函数作为参数时, 参数是函数名字符串而不是函数的引用.
PHP的一个奇葩设计是它在将函数作为参数时, 参数是函数名字符串而不是函数的引用.
从PHP 8.0开始可用.
作为类型声明使用, 代表"任何类型".
作为类型声明使用, 代表"任何类型".
从PHP 7.1开始可用.
作为类型声明使用, 它相当于
作为类型声明使用, 它相当于
array | Traversable
的别名.// 参数是iterable类型function foo(iterable $iter) { // ...}// 返回值是iterable类型function bar(): iterable { return [];}// iterable作为生成器的返回值类型function foobar(): iterble { yield 1; yield 2; yield 3;}
自PHP 8.1可用.
枚举除了可以当值使用, 还可以作为类型使用.
PHP的枚举本质上是一个精简版的类, 可以实现接口和添加方法,
但不能有构造函数和析构函数, 不支持继承, 不支持属性.
枚举除了可以当值使用, 还可以作为类型使用.
PHP的枚举本质上是一个精简版的类, 可以实现接口和添加方法,
但不能有构造函数和析构函数, 不支持继承, 不支持属性.
// 值为int的枚举enum Color { case Red; case Green; case Blue;}Color::cases(); // 返回所有枚举值Color::Red;Color::Red->name; // 枚举值的字符串名称// 值为string的枚举enum Color: string { case Red = '#FF0000'; case Green = '#00FF00'; case Blue = '#0000FF';}Color::cases(); // 返回所有枚举值Color::Red;Color::Red->value; // '#FF0000'
- resource: 一种特殊的类型, 代表着一个外部资源引用, 比如文件句柄.
- NULL
// 用const关键字定义常量(推荐)const NAME = 'value';// 用define函数定义常量define('NAME, 'value');
$a = 1$b = 2function sum() { // 函数默认的作用域是本地作用域, 通过global让它访问到全局作用域的变量 // 推荐使用$GLOBALS替代, 因为global容易不慎污染本地作用域 global $a, $b; return $a + $b;}// 使用$GLOBALSfunction sum() { $a = $GLOBAL['a'] $b = $GLOBAL['b'] return $a + $b}
声明静态作用域, 该特性不应该在任何现代编程语言里使用.
函数声明具有提升机制, 并且在函数体内可以访问到外部作用域的类和函数.
// 让参数引用传递而不是值传递function increase(&$value) { $value++;}$var = 0;increase($var);// 从函数返回引用function &foo() { return ['hello', 'world'];}$result = &foo;// 可选参数(推荐)function bar(?A $a) {}// 等价于function bar(A $a = NULL) {}// 可变数量参数function sum(int ...$nums) {}// 从PHP 8.0开始调用时支持使用命名参数, 结合位置参数使用时, 命名参数必须在位置参数之后func(pararmName: $value);// 在PHP里, 函数的引用是通过函数名字符串表示的.$func = 'bar'; // 将一个函数引用赋值给变量$func$func(); // 调用函数// 匿名函数$foo = function($name): string {}// 从父作用域继承变量$foo = function($name) use ($message): string {}// 箭头函数(自PHP 7.4开始可用), 是匿名函数的简写, 区别在于父作用域的变量会自动继承$foo = fn($name): string => {}
function gen1() { yield 1; yield 2; yield 3;}function gen2() { // yield另一个可迭代对象的值 yield from gen1();}
clonenewinstanceof // 类型判断运算符, 右值不仅可以是类, 也可以是接口, 或二者的字符串形式// 逻辑运算符|| // 逻辑或(优先级不够低)&& // 逻辑与(优先级不够低)and // 逻辑与(优先级最低, 推荐使用)xor // 逻辑异或(优先级最低, 推荐使用)or // 逻辑或(优先级最低, 推荐使用):: // 范围解析操作符, 可用于访问类的静态成员, 类常量, 以及接在self, parent, static之后.== // 松散相等, 不同类型之间会触发类型转换(因此禁用)=== // 严格相等, 要求类型也相同!= // 松散不等, 不同类型之间会触发类型转换(禁用)<> // 松散不等, 不同类型之间会触发类型转换(禁用)!== // 严格不等, ===的反面, 转换后的值不同, 或者类型不同都会返回真.<=> // 组合比较, 当左值小于右值时, 返回小于0的整数, 当左值等于右值时, 返回0, 当左值大于右值时, 返回大于1的整数`` // 执行反引号内的shell命令, 设计与Bash相同?? // null合并运算符? : // 三元运算符
require和include的功能一样, 都是在运行时加载另一个PHP脚本.
require和include的区别在于require时出错会抛出错误, 导致脚本终止, 而include只产生警告.
require和include分别有require_once和include_once用来确保只导入相同文件一次.
require和include的区别在于require时出错会抛出错误, 导致脚本终止, 而include只产生警告.
require和include分别有require_once和include_once用来确保只导入相同文件一次.
建议使用require_once.
require('module.php');
大部分流程控制语句都有另一种类似Basic语言的形式, 鉴于C类语法是默认选项, 不推荐使用Basic形式.
// if// 形式1if ($condition1) { // ...} elseif ($condition2) { // ...} else { // ...}// 形式2if ($condition1): // ...elseif ($condition2): // ...else: // ...endif;// switchswitch ($value) { case $case1: // ... break; case $case2: // ... break; default: // ...}
从PHP 8.0开始可用.
用于模式匹配, 使用的是严格比较.
match表达式内必须包括所有可能的情况, 否则会抛出UnhandledMatchError.
match表达式内必须包括所有可能的情况, 否则会抛出UnhandledMatchError.
$result = match ($value) { $condition1 => 1, $condition2 => 2, $condition3, $condition4 => 3, default => 4};
//while// 形式1while ($condition) { // ...}// 形式2while ($condition): // ...endwhile;// do whiledo { // ...} while ($condition);// forfor ($i = 0; $i < 10; $i++) { // ...}// foreachforeach ($arr as $value) { // ...}
现代PHP包管理器的事实标准.
Composer的公共包存储库.
学习曲线陡峭, 在开发者中的声誉比Laravel好.
微框架.
微框架.
Drupal v8使用Symfony作为其框架.
建立在Symfony之上的轻量级框架, 是目前最受欢迎的PHP框架.
https://lumen.laravel.com/
基于Laravel的微框架.