1、PHP简史
- PHP最初是作为工具包出现。
- 1996发布2.0版本,可以访问数据库,还可以把PHP代码嵌入到HTML页面中。
- PHP3.0重写了代码,具有强大的可扩展性。
- 2000发布4.0,出现Zend引擎,性能提升近10倍。
- 2004发布5.0,引入了面向对象、类型提示和异常处理。
- 2005年发起的6.0最终被取消,但命名空间、匿名函数、闭包等特性加入到5.x版本。
- 2016发布PHP7,性能提升一倍。
- 2020发布PHP8,引入JIT 编译、联合类型、属性等,性能提升一倍。
2、PHP程序执行方式
PHP为解释执行,首先将源码编译为opcode指令集合,后PHP虚拟机解释执行opcode。PHP8又引入了JIT编译。PHP程序执行由Zend引擎完成,底层是C语言实现。
PHP 源码目录:
- sapi:输入输出层的抽象,常用实现有:
- apache2handler,编译生成动态链接库,当有http请求到Apache时,根据配置调用此动态链接库,执行PHP代码;
- cgi-fcgi,编译生成支持CGI协议的可执行程序,通过CGI协议把请求传给CGI进程,执行代码将结果返回,退出进程;
- fpm-fcgi,FastCGI进程管理器,若使用Nginx,Nginx按照FastCGI协议把请求交给php-fpm进程处理;
- cli,命令行交互接口;
- Zend:Zend引擎核心代码,包括编译器、解释器、内存管理、底层数据结构等
- main:SAPI层和Zend层的黏合剂,解析参数,调用PHP脚本
- ext:PHP扩展,比如定义了array、str、pdo等函数
- TSRM:线程安全资源管理器,为每个线程提供独立的全局变量副本
PHP虚拟机架构:
PHP内存管理:
- 采用了内存池,用于提高内存分配、释放的性能
- 借鉴了jemalloc和tcmalloc这两个成熟的内存管理方案
- 垃圾回收为引用计数法
3、变量
PHP变量为弱类型,类型在运行时动态决定,底层由结构体zval表示,占16字节。
$an_int = 12; // 变量声明
- 四种标量类型:
bool(布尔型):值为 true 或 false
int(整型)
float(浮点型,也称作 double)
string(字符串):二进制安全 - 四种复合类型:
array(数组):实现为HashTable
object(对象):属性通过HashTable实现
callable(可调用):支持简单函数、类的方法、实现__invoke方法的对象
iterable(可迭代):配合foreach使用,支持array 、Traversable、yield - 两种特殊类型:
resource(资源):文件句柄、Socket连接、数据库连接等
NULL(无类型)
var_dump() // 表达式的值和类型
gettype() // 表达式的类型
is_int() // is_type函数,检查类型
$foo = (int) $bar; // 强制类型转换
引用:
$a =& $b; // 意味着 $a 和 $b 指向了同一个变量
变量的作用域:
局部变量、全局变量、静态变量、常量
智能字符串:
smart_str,用于频繁对一个字符串进行扩容修改。
4、数组 array
数组的语义:
- PHP数组是一个字典,存储key-value对,键是整型或字符串。
- PHP数组是有序的,有序指插入顺序。
数组的实现为Hashtable:
- bucket:存储单元,存储key、value、h值等
- slot:一个slot下可以有多个bucket
- 冲突解决:链地址法,即将同一个slot中的bucket通过链表连接起来
- hash1函数:字符串key计算出一个h值,h值为数字,数字key返回数字本身
- hash2函数:将h值映射为slot的索引值
- h和key:“h”代表数字key,“key”代表字符串key
数组的数据结构:
- 所有bucket都分配在连续的数组内存中,冲突解决是记录下一个bucket在数组中的索引
- bucket分为未使用、有效、无效三种。插入操作永远在下一个未使用bucket上进行。删除操作将有效标记为无效。当无效bucket较多时,对整个bucket数组进行rehash操作,一部分有效和无效bucket会被释放出来,重新变为未使用bucket。
packed array 和 hash array:
$a = array(1,2,3); // packed array
$b = array('x'=>1, 'y'=>2, 'z'=>3); // hash array
- packed array
数组的key即为bucket数组的下标 - hash array
数组的key通过hash函数得到索引,再通过索引数组得到所在bucket
5、面向对象
class SimpleClass extends BaseClass {
// 常量
const CONST_VALUE = 'A constant value';
// 声明属性
public $var = 'a default value';
// 构造方法
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
// 声明方法
public function displayVar() {
echo $this->var;
}
}
$obj = new SimpleClass();
echo $obj::CONST_VALUE;
echo SimpleClass::CONST_VALUE;
可见性:public(默认)、protected、private
static:静态属性/方法,可以直接通过类访问
final:final方法无法被子类覆盖,final类不能被继承
抽象类和接口:
abstract class AbstractClass {
abstract protected function getValue();
}
interface iTemplate {
public function setVariable($name, $var);
}
trait:
组合的代码复用方式;
当前类的成员覆盖 trait 的方法,而 trait 覆盖被继承的方法;
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
// 使用多个trait
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A; // 同名时使用B的smallTalk
B::bigTalk as talk; // B的bigTalk起一个别名
}
}
匿名类:
var_dump(new class(10) {
private $num;
public function __construct($num) {
$this->num = $num;
}
});
重载:
PHP的重载指动态地创建类属性和方法。
重载方法必须声明为 public。
// 以下“不可访问”指未定义或不可见
// 在给不可访问属性赋值时,__set() 会被调用。
public __set ( string $name , mixed $value ) : void
// 读取不可访问属性的值时,__get() 会被调用。
public __get ( string $name ) : mixed
// 当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
public __isset ( string $name ) : bool
// 当对不可访问属性调用 unset() 时,__unset() 会被调用。
public __unset ( string $name ) : void
// 在对象中调用一个不可访问方法时,__call() 会被调用。
public __call ( string $name , array $arguments ) : mixed
// 在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
public static __callStatic ( string $name , array $arguments ) : mixed
遍历属性:
// 遍历所有可见属性
foreach($objA as $key => $value) {
print "$key => $value\n";
}
也可以实现 Iterator 接口或 IteratorAggregate 接口,自行决定如何遍历。
魔术方法:
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __serialize(), __unserialize(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo() 等方法。
自定义方法建议不要以 __ 为前缀。
对象复制:
// 浅拷贝
$copy_of_object = clone $object;
对象比较:
- $o1 == $o2 两个对象的属性和属性值都相等,且是同一个类的实例
- $o1 === $o2 指向同一个对象
后期静态绑定:
用于在继承范围内引用静态调用的类。
class A {
public static function foo() {
static::who();
}
public static function who() {
echo __CLASS__." ";
}
}
class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}
public static function who() {
echo __CLASS__." ";
}
}
class C extends B {
public static function who() {
echo __CLASS__." ";
}
}
C::test(); // 输出 A C C
协变与逆变:
协变使子类比父类方法能返回更具体的类型; 逆变使子类比父类方法参数类型能接受更模糊的类型。
6、CGI (Common Gateway Interface)
CGI 是一种数据协议,用来规范web服务器传输到php解释器中的数据类型以及数据格式,包括URL、查询字符串、POST数据、HTTP header等。CGI程序可以由不同语言实现。
PHP Web服务处理请求过程:
CGI 与 Servlet 的区别:
- Servlet 处于服务器进程中,它通过多线程方式运行其 service 方法,一个实例可以服务于多个请求,并且其实例一般不会销毁
- CGI 对每个请求都产生新的进程,服务完成后就销毁,称作 fork-and-execute 模式,效率上低于servlet
因为CGI效率低,但对配置很敏感,通常被用在开发和调试阶段。
FastCGI:
FastCGI 是 CGI 的增强版本,主要用来提高CGI程序性能,同样可以由不同语言实现。工作方式:
- Web Server启动同时,加载FastCGI进程管理器(nginx的php-fpm或微软IIS的ISAPI或Apache的Module)
- FastCGI进程管理器读取php.ini配置文件,对自身进行初始化,启动多个CGI解释器进程(php-cgi),等待来自Web Server的连接
- 当Web Server接收到客户端请求时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server会将相关环境变量和标准输入发送到FastCGI子进程php-cgi进行处理
- 在Linux中,nginx加php-fpm是最主流的使用方式
PHP SAPI(Server Application Programimg Interface):
- 相当于PHP外部环境的代理器。PHP可以应用在终端上(CLI SAPI),也可以应用在Web服务器中(CGI SAPI)。
- 底层实现为 _sapi_module_struct。
Nginx + PHP-FPM 模式:
- FPM(FastCGI Process Manager)是一个FastCGI进程管理器,可以有效控制内存和进程,支持平滑重启PHP及重载PHP配置。
- FPM是多进程的服务,其中有一个master进程(做管理工作)和多个worker进程(处理数据请求)。
- 执行过程主要分为5大阶段,分别是模块初始化、请求初始化、执行、请求关闭和模块关闭。由于FPM是常驻内存的进程,所以其模块初始化只做一次,便进入循环,而模块关闭在进程退出时也只做一次。
PHP-FPM 进程有三种设置方式:
- static:固定数量的worker进程
- dynamic:设置一个范围,worker进程数动态变化
- ondemand:worker进程闲置了指定秒数后就会被杀掉
通信示意图:
7、控制语句
条件语句:
if…elseif…else 、 switch…case
循环语句:
while 、 do…while 、 for 、 foreach
break 、 continue
代替语法:
把左花括号 { 换成冒号 :,把右花括号 } 分别换成 endif;,endwhile;,endfor;,endforeach; 以及 endswitch;
if ($a == 5):
echo "a equals 5";
else:
echo "a is not 5";
endif;
goto:
for($i=0,$j=50; $i<100; $i++) {
while($j--) {
if($j==17) goto end;
}
}
echo "i = $i";
end:
echo 'j hit 17';
match:
$expressionResult = match ($condition) {
1, 2 => foo(),
3, 4 => bar(),
default => baz(),
};
8、函数
生成器:
yeild
function makeRange($length) {
for($i = 0; $i < $length; $i++) {
yield $i;
}
}
foreach (makeRange(100) as $i) {
echo $i . PHP_EOL;
}
闭包(匿名)函数:
$message = 'hello';
// 继承 $message
$example = function () use ($message) {
var_dump($message);
};
$example();
箭头函数:
匿名函数和箭头函数都是 Closure 类的实现
$y = 1;
// 可以自动捕获变量的值
$fn1 = fn($x) => $x + $y;
var_export($fn1(3));
Callable 类型:
// An example callback function
function my_callback_function() {
echo 'hello world!';
}
// An example callback method
class MyClass {
static function myCallbackMethod() {
echo 'Hello World!';
}
}
// Type 1: Simple callback
call_user_func('my_callback_function');
// Type 2: Static class method call
call_user_func(array('MyClass', 'myCallbackMethod'));
// Type 3: Object method call
$obj = new MyClass();
call_user_func(array($obj, 'myCallbackMethod'));