深入理解PHP内核(十四)类的成员变量及方法,深入理解内核
原文链接:http://www.orlion.ga/1237/
类的成员变量在PHP中本质是一个变量,只是这些变量都归属于某个类,并且给这些变量是有访问控制的。
类的成员方法在PHP中本质是一个函数,只是这个函数以类的方法存在,他可能是一个类方法也可能是一个实例方法,并且在这些方法都加上了类的访问控制。类的成员方法是现实世界实体行为的抽象,可以用来实现类的行为。
一、成员变量
成员变量在编译时已经注册到了类的结构中。在编译时类的声明编译会调用zend_do_begin_class_declaration函数。此函数用来初始化类的基本信息,其中包括类的成员变量。其调用顺序为[zend_do_begin_class_declaration]–>[zend_initalize_class_data]–>[zend_hash_init_ex]
zend_hash_init_ex(&ce->default_properties, 0, NULL, zval_ptr_dtor_func, persistent_hashes, 0);
因为类的成员变量是保存在HashTable,所以其数据的初始化使用zend_hash_init_ex函数来进行。
在声明类的时候初始化了类的成员变量所在的HashTable,之后如果有新的成员变量属性声明时,在编译时zend_do_declare_property。函数首先检查成员变量不允许的一些情况:
-
接口中不允许使用成员变量
-
成员变量不能拥有抽象属性
-
不能声明成员变量为final
-
不能重复声明属性
如果在类中将一个属性声明为final:
public final $var
会报错:Fatal error: Cannot declare property …这个错误由zend_do_declare_property函数抛出:
if (access_type & ZEND_ACC_FINAL) { zend_error(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, the final modifier is allowed only for methods and classes", CG(active_class_entry)->name, var_name- >u.constant.value.str.val); }
在定义检查没有问题之后,函数会进行成员变量的初始化操作。
ALLOC_ZVAL(property); // ¾ĘŴ if (value) { // ÒʻĻļUɩ ȐďĤ *property = value->u.constant; } else { INIT_PZVAL(property); Z_TYPE_P(property) = IS_NULL; }
在初始化过程中,程序会先分配内存,如果这个成员变量有初始化的数据,则将数据直接赋值给该属性,否则初始化ZVAL,并将其类型设置为IS_NULL。在初始化过程完成后,程序通过调用zend_declare_property_ex函数将此成员变量添加到指定的类结构中。
常规的成员变量最后都会注册到类的default_propertiles字段。在我们平时的工作中,可能会用不到上面所说的这些过程,但是我们可能会使用get_class_vars()函数来查看类的成员变量。此函数返回由类的默认属性组成的关联数组,这个数组的元素以varname=>value的形式存在。其实现核心代码如下:
if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC) == FAILURE) { RETURN_FALSE; } else { array_init(return_value); zend_update_class_constants(*pce TSRMLS_CC); add_class_vars(*pce, &(*pce)->default_properties, return_value TSRMLS_CC); add_class_vars(*pce, CE_STATIC_MEMBERS(*pce), return_value TSRMLS_CC); }
首先调用zend_lookup_class函数查找名为class_name的类,并将复制给pce变量。这个查找的过程最核心是一个HashTable的查找函数zend_hash_quick_find,它会查找EG(class_table)。判断类是否存在,如果存在则直接返回。如果不存在,则需要判断是否可以自动加载,如果可以自动加载,则会加载类后再返回。如果不能找到类,则返回FALSE。如果找到了类,则初始化返回的数组,更新类的静态成员变量,添加类的成员变量到返回的数组。这里针对类的静态成员变量有一个更新的过程,关于这个过程我们在下面有关于静态变量中做相关介绍。
二、静态成员变量
类的静态成员变量是所有实例公用的,它归属于这个类,因此它也叫做类变量。在PHP的类结构中,类本身的静态变量存在在类结构的default_static_memebers字段中。
与普通成员变量不同,类变量可以直接通过类名调用,这也体现其称作类变量的特别。一个PHP实例:
class Tipi { public static $var = 10; } Tipi::$var;
通过VLD扩展查看其生成的中间代码:
function name: (null) number of ops: 6 compiled vars: !0 = $var line # * op fetch ext return operands ------------------------------------------------------------------------------- - - 2 0 > EXT_STMT 1 NOP 6 2 EXT_STMT 3 ZEND_FETCH_CLASS :1 'Tipi' 4 FETCH_R static member 'var' 5 > RETURN 1 branch: # 0; line: 2- 6; sop: 0; eop: 5 path #1: 0, Class Tipi: [no user functions]
这段中间代码仅仅与Tipi::$var这段调用对应,它与前面的类定义没有多大关系。根据VLD生成的内容我们可以知道PHP代码:Tipi::$var,生成的中间代码包括ZEND_FETCH_CLASS和FETCH_R。这里只是一个静态变量的调用,但是它却生成了两个中间代码。原因:我们要调用一个类的静态变量,当然要先找到这个类,然后再获取这个类的变量。从PHP源码来看,这是由于在编译时其调用了zend_do_fetch_static_member函数,而在此函数中又调用了zend_do_fetch_class函数,从而会生成ZEND_FETCH_CLASS中间代码。它所对应的执行函数为ZEND_FETCH_CLASS_SPEC_CONST_HANDLER。此函数会调用zend_fetch_class函数(Zend/zend_execute_API.c)。而zend_fetch_class函数最终也会调用zend_lookup_class_ex函数查找类。
找到了类接着应该就是查找类的静态成员变量,其最终调用的函数为:zend_std_get_static_property。这里由于第二个参数的类型为ZEND_FETCH_STATIC_MEMBER。这个函数最后是从static_members字段中查找对应的值返回。而在查找前会和前面一样,执行zend_update_class_constant函数,从而更新此类的所有静态成员变量,静态变量更新流程图:
三、成员方法
成员方法从本质上来将也是一种函数,所以其存储结构也和常规函数一样,存储在zend_function结构体中。对于一个类的多个成员方法,它是以HashTable的数据结构存储了多个zend_function结构体。和前面的成员变量一样,在类声明时成员方法也通过调用zend_initalize_class_data方法,初始化了整个方法列表所在的HashTable。
除去访问控制关键字,一个成员方法和常规函数是一样的,从语法解析中调用的函数一样(都是zend_do_begin_function_declaration函数),但是其调用的参数有一些不同,第三个参数is_method,成员方法的赋值为1,表示它作为成员方法的属性。在这个函数中会有一系统的编译判断,比如在接口中不能声明私有的成员方法。
在此程序判断后,程序将方法直接添加到类结构的function_table字段,在此之后,又是若干的编译检测。比如接口的一些魔术方法不能设置为非公有,不能被设置为static,如__call()、__callStatic()、__get()等。
与成员变量一样,成员方法也有一个返回所有成员方法的函数–get_class_methods()。此函数返回由指定的类中定义的方法名所组成的数组。
四、静态成员方法
类的静态成员方法通常也叫做类方法。与静态成员变量不同,静态成员方法与成员方法都存储在类结构的function_table字段。
class Tipi{ public static function t() { echo 1; } } Tipi::t();
以上的代码在VLD扩展下生成的部分中间代码:
number of ops: 8 compiled vars: none line # * op fetch ext return operands ------------------------------------------------------------------------------- -- 2 0 > EXT_STMT 1 NOP 8 2 EXT_STMT 3 ZEND_INIT_STATIC_METHOD_CALL 'Tipi','t' 4 EXT_FCALL_BEGIN 5 DO_FCALL_BY_NAME 0 6 EXT_FCALL_END 9 7 > RETURN 1 branch: # 0; line: 2- 9; sop: 0; eop: 7 path #1: 0, Class Tipi: Function t: Finding entry points Branch analysis from position: 0
从以上的内容可以看出整个静态成员方法的调用是一个先查找方法再调用的过程。而对于调用操作,对应的中间代码为ZEND_INIT_STATIC_METHOD_CALL。由于类名和方法名都是常量,于是我们可以知道中间代码对应的函数是ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER。在这个函数中,它会首先调用zend_fetch_class函数,通过类名在EG(class_table)中查找类,然后再执行静态方法的获取方法。
if (ce->get_static_method) { EX(fbc) = ce->get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); } else { EX(fbc) = zend_std_get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); }
如果类结构中的get_static_method方法存在,则调用此方法,如果不存在,则调用zend_std_get_static_method。在PHP的源码中get_static_method方法一般都是NULL,这里我们重点查看zend_std_get_static_method函数。此函数会查找ce->function_table列表,在查找到方法后检查方法的访问控制权限,如果不允许访问,则报错,否则返回函数结构体。

PHP在現代編程中仍然是一個強大且廣泛使用的工具,尤其在web開發領域。 1)PHP易用且與數據庫集成無縫,是許多開發者的首選。 2)它支持動態內容生成和麵向對象編程,適合快速創建和維護網站。 3)PHP的性能可以通過緩存和優化數據庫查詢來提升,其廣泛的社區和豐富生態系統使其在當今技術棧中仍具重要地位。

在PHP中,弱引用是通過WeakReference類實現的,不會阻止垃圾回收器回收對象。弱引用適用於緩存系統和事件監聽器等場景,需注意其不能保證對象存活,且垃圾回收可能延遲。

\_\_invoke方法允許對象像函數一樣被調用。 1.定義\_\_invoke方法使對象可被調用。 2.使用$obj(...)語法時,PHP會執行\_\_invoke方法。 3.適用於日誌記錄和計算器等場景,提高代碼靈活性和可讀性。

Fibers在PHP8.1中引入,提升了並發處理能力。 1)Fibers是一種輕量級的並發模型,類似於協程。 2)它們允許開發者手動控制任務的執行流,適合處理I/O密集型任務。 3)使用Fibers可以編寫更高效、響應性更強的代碼。

PHP社區提供了豐富的資源和支持,幫助開發者成長。 1)資源包括官方文檔、教程、博客和開源項目如Laravel和Symfony。 2)支持可以通過StackOverflow、Reddit和Slack頻道獲得。 3)開發動態可以通過關注RFC了解。 4)融入社區可以通過積極參與、貢獻代碼和學習分享來實現。

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP不是在消亡,而是在不斷適應和進化。 1)PHP從1994年起經歷多次版本迭代,適應新技術趨勢。 2)目前廣泛應用於電子商務、內容管理系統等領域。 3)PHP8引入JIT編譯器等功能,提升性能和現代化。 4)使用OPcache和遵循PSR-12標準可優化性能和代碼質量。

PHP的未來將通過適應新技術趨勢和引入創新特性來實現:1)適應云計算、容器化和微服務架構,支持Docker和Kubernetes;2)引入JIT編譯器和枚舉類型,提升性能和數據處理效率;3)持續優化性能和推廣最佳實踐。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SublimeText3 Linux新版
SublimeText3 Linux最新版