在面向對象語言中,都會內置一些語言內置提供的基本功能類,比如JavaScript中的Array,Number等類,PHP中也有很多這種類,比如Directory,stdClass,Exception等類,同時一些標準擴展比如PDO等擴展中也會定義一些類,PHP中類是不允許重復定義的,所以在編寫代碼時不允許定義已經存在的類。
同時PHP中有一些特殊的類:self,static和parent,相信讀者對這self和parent都比較熟悉了,而static特殊類是PHP5.3才引入的。
PHP中的static關鍵字非常多義:
- 在函數體內的修飾變量的static關鍵字用于定義靜態局部變量。
- 用于修飾類成員函數和成員變量時用于聲明靜態成員。
- (PHP5.3)在作用域解析符(::)前又表示靜態延遲綁定的特殊類。
這個關鍵字修飾的意義都表示"靜態",在[PHP手冊中](http://cn.php.net/manual/en/language.oop5.paamayim-nekudotayim.php)提到self,parent和static這幾個關鍵字,但實際上除了static是關鍵字以外,其他兩個均不是關鍵字,在手冊的[關鍵字列表](http://cn.php.net/manual/en/reserved.keywords.php)中也沒有這兩個關鍵字,要驗證這一點很簡單:
<?php
var_dump(self); // -> string(4) "self"
上面的代碼并沒有報錯,如果你把error_reporting(E_ALL)打開,就能看到實際是什么情況了:運行這段代碼會出現“ Notice: Use of undefined constant self - assumed 'self'“,也就是說PHP把self當成一個普通常量了,嘗試未定義的常量會把產量本身當成一個字符串,例如上例的”self",不過同時會出一個NOTICE,這就是說self這個標示符并沒有什么特殊的。
<?php
define('self',"stdClass");
echo self; // stdClass
> 不同語言中的關鍵字的意義會有些區別,Wikipedia上的[解釋](http://en.wikipedia.org/wiki/Keyword_(computer_programming))是: 具有特殊含義的標示符或者單詞,從這個意義上說$this也算是一個關鍵字,但在PHP的關鍵字列表中并沒有。 PHP的關鍵字和C/C++一樣屬于保留字(關鍵字),關鍵字用于表示特定的語法形式,例如函數定義,流程控制等結構。 這些關鍵字有他們的特定的使用場景,而上面提到的self和parent并沒有這樣的限制。
## self,parent,static類[]()
前面已經說過self的特殊性。self是一個特殊類,它指向當前類,但只有在類定義內部才有效,但也并不一定指向類本身這個特殊類,比如前面的代碼,如果放在類方法體內運行,echo self; 還是會輸出常量self的值,而不是當前類,它不止要求在類的定義內部,還要求在類的上下文環境,比如 new self()的時候,這時self就指向當前類,或者self::$static_varible,self::CONSTANT類似的作用域解析符號(::),這時的self才會作為指向本身的類而存在。
同理parent也和self類似。下面先看看在在類的環境下的編譯吧$PHP_SRC/Zend/zend_language_parser.y:
class_name_reference:
class_name { zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
| dynamic_class_name_reference { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
;
在需要獲取類名時會執行zend_do_fetch_class()函數:
void zend_do_fetch_class(znode *result, znode *class_name TSRMLS_DC)
{
// ...
opline->opcode = ZEND_FETCH_CLASS;
if (class_name->op_type == IS_CONST) {
int fetch_type;
?
fetch_type = zend_get_class_fetch_type(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len);
switch (fetch_type) {
case ZEND_FETCH_CLASS_SELF:
case ZEND_FETCH_CLASS_PARENT:
case ZEND_FETCH_CLASS_STATIC:
SET_UNUSED(opline->op2);
opline->extended_value = fetch_type;
zval_dtor(&class_name->u.constant);
break;
default:
zend_resolve_class_name(class_name, &opline->extended_value, 0 TSRMLS_CC);
opline->op2 = *class_name;
break;
}
} else {
opline->op2 = *class_name;
}
// ...
}
上面省略了一些無關的代碼,重點關注fetch_type變量。這是通過zend_get_class_fetch_type()函數獲取到的。
int zend_get_class_fetch_type(const char *class_name, uint class_name_len)
{
if ((class_name_len == sizeof("self")-1) &&
!memcmp(class_name, "self", sizeof("self")-1)) {
return ZEND_FETCH_CLASS_SELF;
} else if ((class_name_len == sizeof("parent")-1) &&
!memcmp(class_name, "parent", sizeof("parent")-1)) {
return ZEND_FETCH_CLASS_PARENT;
} else if ((class_name_len == sizeof("static")-1) &&
!memcmp(class_name, "static", sizeof("static")-1)) {
return ZEND_FETCH_CLASS_STATIC;
} else {
return ZEND_FETCH_CLASS_DEFAULT;
}
}
前面的代碼是Zend引擎編譯類相關操作的代碼,下面就到執行階段了,self,parent等類的指向會在執行時進行獲取,找到執行opcode為ZEND_FETCH_CLASS的執行函數:
zend_class_entry *zend_fetch_class(const char *class_name, uint class_name_len, int fetch_type TSRMLS_DC)
{
zend_class_entry **pce;
int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0;
int silent = (fetch_type & ZEND_FETCH_CLASS_SILENT) != 0;
?
fetch_type &= ZEND_FETCH_CLASS_MASK;
?
check_fetch_type:
switch (fetch_type) {
case ZEND_FETCH_CLASS_SELF:
if (!EG(scope)) {
zend_error(E_ERROR, "Cannot access self:: when no class scope is active");
}
return EG(scope);
case ZEND_FETCH_CLASS_PARENT:
if (!EG(scope)) {
zend_error(E_ERROR, "Cannot access parent:: when no class scope is active");
}
if (!EG(scope)->parent) {
zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent");
}
return EG(scope)->parent;
case ZEND_FETCH_CLASS_STATIC:
if (!EG(called_scope)) {
zend_error(E_ERROR, "Cannot access static:: when no class scope is active");
}
return EG(called_scope);
case ZEND_FETCH_CLASS_AUTO: {
fetch_type = zend_get_class_fetch_type(class_name, class_name_len);
if (fetch_type!=ZEND_FETCH_CLASS_DEFAULT) {
goto check_fetch_type;
}
}
break;
}
?
if (zend_lookup_class_ex(class_name, class_name_len, use_autoload, &pce TSRMLS_CC) == FAILURE) {
if (use_autoload) {
if (!silent && !EG(exception)) {
if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) {
zend_error(E_ERROR, "Interface '%s' not found", class_name);
} else {
zend_error(E_ERROR, "Class '%s' not found", class_name);
}
}
}
}
return NULL;
}
return *pce;
}
從這個函數就能看出端倪了,當需要獲取self類的時候,則將EG(scope)類返回,而EG(scope)指向的正是當前類。如果時parent類的話則從去EG(scope)->parent也就是當前類的父類,而static獲取的時EG(called_scope),分別說說EG宏的這幾個字段,前面已經介紹過EG宏,它可以展開為如下這個結構體:
struct _zend_executor_globals {
// ...
zend_class_entry *scope;
zend_class_entry *called_scope; /* Scope of the calling class */
// ...
}
?
struct _zend_class_entry {
char type;
char *name;
zend_uint name_length;
struct _zend_class_entry *parent;
}
#define struct _zend_class_entry zend_class_entry
其中的zend_class_entry就是PHP中類的內部結構表示,zend_class_entry有一個parent字段,也就是該類的父類。在EG結構體中的中called_scope會在執行過程中將當前執行的類賦值給called_scope,例如如下代碼:
<?php
class A {
public static funcA() {
static::funcB();
}
}
?
class B {
public static funcB() {
echo "B::funcB()";
}
}
?
B::funcA();
代碼B::funcA()執行的時候,實際執行的是B的父類A中定義的funcA函數,A::funcA()執行時當前的類(scope)指向的是類A,而這個方法是從B類開始調用的,called_scope指向的是類B,static特殊類指向的正是called_scope,也就是當前類(觸發方法調用的類),這也是延遲綁定的原理。
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和Zend引擎
- 第二節 SAPI概述
- Apache模塊
- 嵌入式
- FastCGI
- 第三節 PHP腳本的執行
- 詞法分析和語法分析
- opcode
- opcode處理函數查找
- 第四節 小結
- 第三章 變量及數據類型
- 第一節 變量的結構和類型
- 哈希表(HashTable)
- PHP的哈希表實現
- 鏈表簡介
- 第二節 常量
- 第三節 預定義變量
- 第四節 靜態變量
- 第五節 類型提示的實現
- 第六節 變量的生命周期
- 變量的賦值和銷毀
- 變量的作用域
- global語句
- 第七節 數據類型轉換
- 第八節 小結
- 第四章 函數的實現
- 第一節 函數的內部結構
- 函數的內部結構
- 函數間的轉換
- 第二節 函數的定義,傳參及返回值
- 函數的定義
- 函數的參數
- 函數的返回值
- 第三節 函數的調用和執行
- 第四節 匿名函數及閉包
- 第五節 小結
- 第五章 類和面向對象
- 第一節 類的結構和實現
- 第二節 類的成員變量及方法
- 第三節 訪問控制的實現
- 第四節 類的繼承,多態及抽象類
- 第五節 魔術方法,延遲綁定及靜態成員
- 第六節 PHP保留類及特殊類
- 第七節 對象
- 第八節 命名空間
- 第九節 標準類
- 第十節 小結
- 第六章 內存管理
- 第一節 內存管理概述
- 第二節 PHP中的內存管理
- 第三節 內存使用:申請和銷毀
- 第四節 垃圾回收
- 新的垃圾回收
- 第五節 內存管理中的緩存
- 第六節 寫時復制(Copy On Write)
- 第七節 內存泄漏
- 第八節 小結
- 第七章 Zend虛擬機
- 第一節 Zend虛擬機概述
- 第二節 語法的實現
- 詞法解析
- 語法分析
- 實現自己的語法
- 第三節 中間代碼的執行
- 第四節 PHP代碼的加密解密
- 第五節 小結
- 第八章 線程安全
- 第二節 線程,進程和并發
- 第三節 PHP中的線程安全
- 第九章 錯誤和異常處理
- 第十章 輸出緩沖
- 第十六章 PHP語言特性的實現
- 第一節 循環語句
- foreach的實現
- 第二十章 怎么樣系列(how to)
- 附錄
- 附錄A PHP及Zend API
- 附錄B PHP的歷史
- 附錄C VLD擴展使用指南
- 附錄D 怎樣為PHP貢獻
- 附錄E phpt測試文件說明
- 附錄F PHP5.4新功能升級解析
- 附錄G:re2c中文手冊