PHP Internals - PHP 深度探索


Entries Tagged as 'PHP Internals'

PHP源代码分析- OPCODE选解系列:ZEND_TICKS

11月 30th, 2008 · No Comments

PHP源代码分析- OPCODE选解系列:ZEND_TICKS
昨天有位朋友在杭州的PHPer群里面贴出了下面的一段代码并给出了运行结果:
源程序:
<?php
function doTicks ()
{
    echo ‘Ticks’;
}
register_tick_function(’doTicks’);
declare(ticks = 1) {
    for ($x = 1; $x < 10; ++ $x) {
        echo $x * $x . ‘<br />’;
    }
}
?>
运行结果:
1
TicksTicks4
TicksTicks9
TicksTicks16
TicksTicks25
TicksTicks36
TicksTicks49
TicksTicks64
TicksTicks81
TicksTicksTicksTicks
他对运行结果感到疑惑,问了三个问题:
(1) 为什么先输出1之后才输出“Ticks”? 
(2) 为什么在输出81后还输出四个Ticks ?
(3)  declare中的for循环怎么分解成低级语句(low-level)?
register_tick_function函数定义了每个tick事件发生时的处理函数。那么什么是tick事件呢?先看手册上对ticks的解释:
A tick is an event that occurs for every N low-level statements executed by the parser within the declare block. The value for N is [...]

[Read more →]

Tags: PHP Internals

PHP源代码分析:BREAK/CONTINUE

11月 28th, 2008 · 1 Comment

PHP源代码分析:OPCODE选解系列:BRK/CNT
在PHP的循环控制中,有两个改变能循环流程的语句,分别是break, contine。今天我们研究一下它们的实现。
首先我们看一个例子:
<?php
 echo “Break/Continue OPCODE Study Sample “;
 
 for ($i = 1; $i < 5; $i ++) {
   for ($j = 1; $j < 5; $j ++) {
     for ($k = 1; $k < 5; $k ++) {
       for ($l = 1; $l < 5; $l ++) {
         echo “i = $i, j = $j , k [...]

[Read more →]

Tags: PHP Internals

PHP Opcode: The complete reference

11月 25th, 2008 · No Comments

重新将PHP的opcode的各个OPCODE功能,参数整理了一遍。还有部分工作正在整理。
具体请参考:
http://www.phpinternals.com/opcode/

[Read more →]

Tags: PHP Internals

PHP autoload机制详解

11月 21st, 2008 · No Comments

PHP autoload机制详解
(1) autoload机制概述
 
 在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利。这也是OO设计的基本思想之一。在PHP5之前,如果需要使用一个类,只需要直接使用include/require将其包含进来即可。下面是一个实际的例子:
/* Person.class.php */
<?php
 class Person {
  var $name, $age;
  
  function __construct ($name, $age)
  {
   $this->name = $name;
   $this->age = $age;
  }
 }
?>
/* no_autoload.php */
<?php
 require_once (”Person.class.php”);
 
 $person = new Person(”Altair”, 6);
 var_dump ($person);
?>
在这个例子中,no-autoload.php文件需要使用Person类,它使用了require_once将其包含,然后就可以直接使用Person类来实例化一个对象。
但随着项目规模的不断扩大,使用这种方式会带来一些隐含的问题:如果一个PHP文件需要使用很多其它类,那么就需要很多的require/include语句,这样有可能会造成遗漏或者包含进不必要的类文件。如果大量的文件都需要使用其它的类,那么要保证每个文件都包含正确的类文件肯定是一个噩梦。
PHP5为这个问题提供了一个解决方案,这就是类的自动装载(autoload)机制。autoload机制可以使得PHP程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件include进来,这种机制也称为lazy loading。
下面是使用autoload机制加载Person类的例子:
/* autoload.php */
<?php
 function __autoload($classname) {
  require_once ($classname . “class.php”);
 }
 
 $person = new Person(”Altair”, 6);
 var_dump ($person);
 ?>
 
 通常PHP5在使用一个类时,如果发现这个类没有加载,就会自动运行__autoload()函数,在这个函数中我们可以加载需要使用的类。在我们这个简单的例子中,我们直接将类名加上扩展名”.class.php”构成了类文件名,然后使用require_once将其加载。从这个例子中,我们可以看出autoload至少要做三件事情,第一件事是根据类名确定类文件名,第二件事是确定类文件所在的磁盘路径(在我们的例子是最简单的情况,类与调用它们的PHP程序文件在同一个文件夹下),第三件事是将类从磁盘文件中加载到系统中。第三步最简单,只需要使用include/require即可。要实现第一步,第二步的功能,必须在开发时约定类名与磁盘文件的映射方法,只有这样我们才能根据类名找到它对应的磁盘文件。
 因此,当有大量的类文件要包含的时候,我们只要确定相应的规则,然后在__autoload()函数中,将类名与实际的磁盘文件对应起来,就可以实现lazy loading的效果。从这里我们也可以看出__autoload()函数的实现中最重要的是类名与实际的磁盘文件映射规则的实现。
 
 但现在问题来了,如果在一个系统的实现中,如果需要使用很多其它的类库,这些类库可能是由不同的开发人员编写的,其类名与实际的磁盘文件的映射规则不尽相同。这时如果要实现类库文件的自动加载,就必须在__autoload()函数中将所有的映射规则全部实现,这样的话__autoload()函数有可能会非常复杂,甚至无法实现。最后可能会导致__autoload()函数十分臃肿,这时即便能够实现,也会给将来的维护和系统效率带来很大的负面影响。在这种情况下,难道就没有更简单清晰的解决办法了吧?答案当然是:NO! 在看进一步的解决方法之前,我们先来看一下PHP中的autoload机制是如何实现的。
(2) PHP的autoload机制的实现
 我们知道,PHP文件的执行分为两个独立的过程,第一步是将PHP文件编译成普通称之为OPCODE的字节码序列(实际上是编译成一个叫做zend_op_array的字节数组),第二步是由一个虚拟机来执行这些OPCODE。PHP的所有行为都是由这些OPCODE来实现的。因此,为了研究PHP中autoload的实现机制,我们将autoload.php文件编译成opcode,然后根据这些OPCODE来研究PHP在这过程中都做了些什么:
 /* autoload.php 编译后的OPCODE列表,是使用作者开发的OPDUMP工具
     * 生成的结果,可以到网站 http://www.phpinternals.com/ 下载该软件。
     */
    1: <?php
    2:  // require_once (”Person.php”);
    3:  
    4:  function [...]

[Read more →]

Tags: PHP Internals

[收藏]理解PHP5对象模型(幻灯片)

11月 6th, 2008 · No Comments

Understanding the PHP Object Model
View SlideShare presentation or Upload your own. (tags: php oop)

[Read more →]

Tags: PHP Internals

PHP源代码分析:Zend HashTable详解

11月 4th, 2008 · Comments Off

PHP源代码分析:Zend HashTable详解
By Altair(eniac2008@hotmail.com), http://www.phpinternals.com
本文的PDF版本下载地址:http://www.phpinternals.com/download/zend_hash_v0.2.pdf
在PHP的Zend引擎中,有一个数据结构非常重要,它无处不在,是PHP数据存储的核心,各种常量、变量、函数、类、对象等都用它来组织,这个数据结构就是HashTable。
HashTable在通常的数据结构教材中也称作散列表,哈希表。其基本原理比较简单(如果你对其不熟悉,请查阅随便一本数据结构教材或在网上搜索),但PHP的实现有其独特的地方。理解了HashTable的数据存储结构,对我们分析PHP的源代码,特别是Zend Engine中的虚拟机的实现时,有很重要的帮助。它可以帮助我们在大脑中模拟一个完整的虚拟机的形象。它也是PHP中其它一些数据结构如数组实现的基础。
Zend HashTable的实现结合了双向链表和向量(数组)两种数据结构的优点,为PHP提供了非常高效的数据存储和查询机制。
Let’s begin!
一、 HashTable的数据结构
在Zend Engine中的HashTable的实现代码主要包括zend_hash.h, zend_hash.c这两个文件中。Zend HashTable包括两个主要的数据结构,其一是Bucket(桶)结构,另一个是HashTable结构。Bucket结构是用于保存数据的容器,而HashTable结构则提供了对所有这些Bucket(或桶列)进行管理的机制。
<code>
typedef struct bucket {
 ulong h;      /* Used for numeric indexing */
 uint nKeyLength;    /* key 长度 */
 void *pData;     /* 指向Bucket中保存的数据的指针 */
 void *pDataPtr;    /* 指针数据 */
 struct bucket *pListNext;  /* 指向HashTable桶列中下一个元素 */
 struct bucket *pListLast;   /* 指向HashTable桶列中前一个元素 */
 struct bucket *pNext;   /* 指向具有同一个hash值的桶列的后一个元素 */
 struct bucket *pLast;   /* 指向具有同一个hash值的桶列的前一个元素 */
 char arKey[1];     /* 必须是最后一个成员,key名称*/
} Bucket;
</code>
在Zend HashTable中,每个数据元素(Bucket)有一个键名(key),它在整个HashTable中是唯一的,不能重复。根据键名可以唯一确定HashTable中的数据元素。键名有两种表示方式。第一种方式使用字符串arKey作为键名,该字符串的长度为nKeyLength。注意到在上面的数据结构中arKey虽然只是一个长度为1的字符数组,但它并不意味着key只能是一个字符。实际上Bucket是一个可变长的结构体,由于arKey是Bucket的最后一个成员变量,通过arKey与nKeyLength结合可确定一个长度为nKeyLength的key。这是C语言编程中的一个比较常用的技巧。另一种键名的表示方式是索引方式,这时nKeyLength总是0,长整型字段h就表示该数据元素的键名。简单的来说,即如果nKeyLength=0,则键名为h;否则键名为arKey, 键名的长度为nKeyLength。
当nKeyLength > 0时,并不表示这时的h值就没有意义。事实上,此时它保存的是arKey对应的hash值。不管hash函数怎么设计,冲突都是不可避免的,也就是说不同的arKey可能有相同的hash值。具有相同hash值的Bucket保存在HashTable的arBuckets数组(参考下面的解释)的同一个索引对应的桶列中。这个桶列是一个双向链表,其前向元素,后向元素分别用pLast, pNext来表示。新插入的Bucket放在该桶列的最前面。
在Bucket中,实际的数据是保存在pData指针指向的内存块中,通常这个内存块是系统另外分配的。但有一种情况例外,就是当Bucket保存的数据是一个指针时,HashTable将不会另外请求系统分配空间来保存这个指针,而是直接将该指针保存到pDataPtr中,然后再将pData指向本结构成员的地址。这样可以提高效率,减少内存碎片。由此我们可以看到PHP HashTable设计的精妙之处。如果Bucket中的数据不是一个指针,pDataPtr为NULL。
HashTable中所有的Bucket通过pListNext, [...]

[Read more →]

Tags: PHP Internals · 未分类

Developing PHP internals on Windows

11月 4th, 2008 · No Comments

Developing PHP internals on Windows
View SlideShare presentation or Upload your own.

[Read more →]

Tags: PHP Internals

PHP OPCode Viewer(opdumper) (2008.11.14更新至v0.07版)

10月 22nd, 2008 · 11 Comments

今天将前些日子写的PHP OPcode转储工具改写了一下。现在支持类及自定义函数的opcode转储。
目前本软件只能在windows平台下DOS里使用。
主要功能:转储/查看PHP编译后的OPCODE代码。
有时间的话会写个GUI.
Opdump - PHP Script’s OPcode dumper, version 0.0.5 [For PHP 5.3.x]
Copyright(c) 2008 by Altair, All rights reserved.
You can get the lastest update at http://www.phpinternals.com
Usage: opdump [options] -o<output_filename> <php_script_file_name>
       -e                                   enable extended info output
       -o<output_filename>    specify output filename
下载地址:
最新版本:v0.07 (PHP 5.3.0alpha2)
http://www.phpinternals.com/download/opdump_0.0.7.rar v0.07版 [PHP 5.3.0alpha2]
http://www.phpinternals.com/download/opdump_0.0.5.rar v0.05版 [PHP 5.3.0alpha1]
http://www.phpinternals.com/download/opdump_0.0.4.rar v0.04版 [PHP 5.2.x]
http://www.phpinternals.com/download/opdump_0.0.2.rar v0.02版

[Read more →]

Tags: PHP Internals

PHP Opcode Dumper

06月 18th, 2008 · No Comments

一个DOS下的PHP脚本的OP code转储工具,非常简单的版本,可以用来查看一个PHP脚本编译后的OP Code。目前还不支持自定义函数和类。
按此下载 PHP Script OP CODE Dumper(v0.01)
使用方法很简单,解压后在命令行下输入命令: opdump <php_script_file_name>即可。
Opdump - PHP Script’s OP Code dumper, version 0.01
Copyright(c) 2008 by Altair, http://www.phpinternals.com
Usage: opdump <php_script_file_name>

[Read more →]

Tags: PHP Internals