1.PHP内核Zend
PHP由内核Zend引擎和扩展层组成,PHP内核负责处理请求、完成文件流错误处理等操作,Zend引擎可以将PHP程序文件转换成可在虚拟机上运行的机器语言,扩展层提供一些应用层操作需要的函数类库等,比如数组和MySQL数据库的操作等。
Zend引擎是用C语言实现的,将PHP代码通过词法语法解析成可执行的opcode并实现相应的处理方法、基本的数据结构内存分配和管理等,对外提供相应的可供调用的API方法。Zend引擎是PHP的核心,所有的外围功能都是围绕它实现的。扩展层通过组件的方式提供各种基础服务、内置函数,标准库都是通过它实现的。用户也可以编写自己的扩展来实现特定的需求。服务端应用编程接口(Server Application Programming Interface, SAPI),通过一系列钩子函数使得PHP可以和外围交互数据。我们平时编写的PHP程序就是通过不同的SAPI方式得到不同的应用模式,如通过WebServer实现的Web应用和在命令行下运行的脚本等。一段PHP程序被执行的时候会先被解析成opcode指令,然后在虚拟机中按顺序执行,由于PHP本身是用C语言开发的,所以其在执行的时候调用的都是C的函数。opcode是PHP程序执行的最基本单位。
HashTable是Zend的核心数据结构,实现了PHP里几乎所有的功能,支持key->value查询,添加删除的复杂度是O(1),支持线性遍历和混合类型。在HashTable中既有key->value形式的散列结构,也有双向链表模式,使得它能够非常方便地支持快速查找和线性遍历。Zend的散列结构是典型的hash表模型,通过链表的方式来解决冲突。Zend的HashTable是一个自增长的数据结构,当hash表数目满了之后,其本身会动态地以2倍的方式扩容并重新布置元素位置,初始大小均为8。另外,在进行key->value快速查找的时候,Zend本身还做了一些优化,通过空间换时间的方式加快速度。比如在每个元素中都会用一个变量nKeyLength标识key的长度以做快速判定。Zend HashTable通过一个链表结构实现了元素的线性遍历。理论上,做遍历使用单向链表就够了,使用双向链表的主要目的是为了快速删除链表元素,避免遍历。PHP是一门弱类型语言,本身不严格区分变量的类型。PHP在声明变量的时候不需要指定类型。PHP在程序运行期间可能进行变量类型的隐式转换。和其他强类型语言一样,程序中也可以进行显式的类型转换。Zval是Zend中另一个非常重要的数据结构,用来标识并实现PHP变量。
2.php核心数据结构
Zval主要由以下3部分组成。
- Type指定了变量所述的类型(整数、字符串、数组等)。
- refcount&is_ref用来实现引用计数。
- value是核心部分,存储了变量的实际数据。
Zval用来保存一个变量的实际数据。因为要存储多种类型,所以zval是一个union,也由此实现了弱类型。引用计数在内存回收、字符串操作等地方使用得非常广泛。PHP中的变量就是引用计数的典型应用。Zval的引用计数通过成员变量is_ref和ref_count实现。通过引用计数,多个变量可以共享同一份数据,避免频繁复制带来的大量消耗。
在进行赋值操作时,Zend将变量指向相同的Zval,同时ref_count++,在unset操作时,对应的ref_count-1。只有ref_count为0时才会真正执行销毁操作。如果是引用赋值,Zend就会修改is_ref为1。PHP变量通过引用计数实现变量共享数据,当试图写入一个变量时,Zend若发现该变量指向的Zval被多个变量共享,则为其复制一份ref_count为1的Zval,并递减原Zval的refcount,这个过程称为“Zval分离”。可见,只有在有写操作发生时,Zend才进行复制操作,因此也叫copy-on-write(写时复制)。对于引用型变量,其要求和非引用型相反,引用赋值的变量间必须是捆绑的,修改一个变量就修改了所有捆绑变量。
3.php7的新特性
相较于以前的版本,PHP 7在语言语法层面和底层架构层面都有一些改进。在语法层面的改进主要是增加了一些新特性、移除了一些扩展、改变了错误异常处理等。在底层结构方面,改变了存储各种变量的Zval和Zend_String结构体、优化了Zend Array的HashTable、改进了函数的调用机制等。这些底层结构的改进大幅提升了PHP的执行效率,使得其执行速度比PHP 5高出一倍左右。PHP是一个弱类型的语言,不过在PHP 7中支持变量类型的定义,引入了一个开关指令declare(strict_type=1);。这个指令一旦开启,就会强制当前文件下的程序遵循严格的函数传参类型和返回类型。不开启strict_type, PHP将会尝试转换成要求的类型;开启之后,PHP不再做类型转换,类型不匹配就会抛出错误。要使用严格模式,一个declare声明指令必须放在文件的顶部。这意味着严格声明标量是基于文件可配的。这个指令不仅影响参数的类型声明,还影响函数的返回值声明。
PHP 7中的新特性主要有以下几点:
- (1)标量类型声明。
- (2)函数返回值类型声明。
- (3)新增null合并运算符。
- (4)新增组合比较符。
- (5)支持通过define()定义常量数组。
- (6)新增支持匿名类。
- (7)支持Unicode codepoint转译语法。
- (8)更好的闭包支持。
- (9)为unserialize()提供过滤。
- (10)新增加IntlChar类。
- (11)支持use语句从同一namespace导入类、函数和常量。
- (12)新增整除函数intdiv()。
- (13)session_start()支持接收数组参数。
4.php数据类型
数据类型是指对数据的抽象描述,比如“整型数据”就是对所有整数数字的抽象。PHP的数据类型包括String(字符串)、Integer(整型)、Float(浮点型)、Boolean(布尔型)、Array(数组)、Object(对象)、NULL(空值)7种,本节介绍这些数据类型的定义和使用。
- 字符串
$str = "hello world";
eche $str;
- 整型
$num = 10;
- 浮点型
$price = 1.0
- Boolean
$isFlag = true;
- array
$arr = array("hello","world"=>array("php","is","the","best"))
- object
class Hello {
var $str;
function get_str() {
return $this->str;
}
function set_str($str = "hello world") {
$this->str = str;
}
}
- 常量
可以用define定义,也可以用const定义(php7)
可以是标量和资源。
define ("Foo", "bar");
const Version = "1.0.0";
自定义常量尽量不要用下划线__Name__
,一般留给内置的预定义常量
- 预定义常量
__LINE__ 行号
__FILE__ 文件路径和文件名
__DIR__ 文件所在的目录
__FUNCTION__ 函数名
__CLASS__ 类名
__TRAIT__ trait的名字
__METHOD__类的方法名
__NAMESPACE__ 命名空间名
4.php流程控制
- if else
if ($a < $b) {
return $a;
} else if ($a == $b) {
return $b;
} else {
return $b;
}
- switch
switch ($num1) {
case $num1 == "hello":
echo $num1;
break;
case $num1 == "world":
echo $num1;
break;
default:
echo "hello world";
break;
}
- while
while ($num < 10) {
echo $num;
-$num;
}
- do while
do {
echo $num;
} while ($num < 10);
- for
for ($num = 0; $num < 100; $num++) {
echo $num;
}
- foreach
foreach ($num_array as $num) {
echo $num;
}
foreach($num_array as $key => $value) {
echo $key;
echo $value;
}
在PHP 5版本中,当foreach开始循环执行时,每次数组内部指针都会自动向后移动一个单元,但是在PHP 7中却不是这样
在PHP 7中,按照值进行循环时,foreach是对数组的复制操作,在循环过程中对数组的修改不会影响循环行为,但在PHP 5中却会有影响。
在PHP 7中按照引用循环的时候对数组的修改会影响循环,在PHP 5中则不会改变。
- goto
goto语句可以用来跳转到程序中的另一个位置。该目标位置可以用目标名称加上冒号来标记,而跳转指令是goto之后接上目标位置的标记。PHP中对goto语句有一定限制,即目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入另一个函数、其他循环或者switch结构中。可以跳出循环或者switch,goto语句常用来代替多层的break语句。
goto a;
echo "hello";
a:
echo "world";
主要用来跳出复杂循环
for ($num = 0; $num < 100; ++$num) {
if ($num === 3) {
echo "is 3";
goto a;
}
echo $num;
}
a:
ehco "end loop";
- 包含语句用于在PHP文件中引入另一个文件,这样有利于代码重用。PHP中共有4个包含外部文件的方法,分别是include、include_once、require、require_once。
- include
include语句包含并运行指定文件。被包含文件先按参数给出的路径寻找,如果没有给出目录(只有文件名),就按照include_path(在配置文件中可查看include_path)指定的目录寻找。如果在include_path下没找到该文件,那么include最后会在调用脚本文件所在的目录和当前工作目录下寻找。如果最后仍未找到文件,include结构就会发出一条警告,例如:
当一个文件被包含时,其中所包含的代码继承了include所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用。不过所有在包含文件中定义的函数和类都具有全局作用域。如果include出现于调用文件中的一个函数里,那么被调用的文件中所包含的所有代码将表现得如同它们是在该函数内部定义的一样。所以它将遵循该函数的变量范围。此规则的一个例外是魔术常量,它们是在发生包含之前就已被解析器处理的.
include_once
include_once和includ效果类似,唯一的区别是如果文件已经被包含,就不会再被包含。
用于在脚本执行期间同一个文件只会执行一次的情况确保它只被执行一次,以避免重复定义,变量重新赋值的问题。require
require和include几乎一模一样,不同的是当包含不存在的文件时,require会爆出一个Fatal Error错误并且终止执行,include则会发出一个warning但继续执行代码。require_once
require_once功能和include_once效果基本上相同,唯一的区别是php会检查文件是否存在。
5.函数
将一段代码封装成一段函数,在调用的时候只需要这个函数名即可。
function foo($argc_1, $argc_2) {
state;
}
其中foo是函数名,argc_2是函数的参数。函数的参数可以是零个或者任意个。任何有效的php代码都可以写在函数体内。php的函数作用域是全局的,任何一个文件内的函数定义后可以在该文件的任何地方调用。
function add_sum($num1, $num2) {
return $num1 + $num2;
}
6.函数的参数
PHP支持按值传递参数(默认),通过引用传递参数及默认参数,也支持可变长度参数列表。PHP支持函数参数类型声明。
-按照值传递
function add($num1, $num2) {
$num1 += $num2;
}
echo $num1; // won't change
- 按照参数传递
function add(&$num1, $num2) {
$num1 += $num2;
}
echo $num1;//change
- 默认参数
function add($arr = array("hello","world"), $num1 = 100) {
}
- 参数的类型声明
php5已经引入参数类型声明,如果给定的值不是合法类型,php5会返回一个Fatal Error,php7则会返回一个 TypeError Exception
class/interface php5.0
Array php5.1
Callable php5.4//有效的回调用
Bool php7
Float php7
Int php7
String php7