首頁 > 後端開發 > php教程 > PHP內核-類別與物件導向的程式碼詳解

PHP內核-類別與物件導向的程式碼詳解

黄舟
發布: 2023-03-06 13:18:02
原創
1375 人瀏覽過

在最开始接触PHP的时候,都是面向过程的方法来自己做一些很简单的网站在玩,写PHP代码就是堆砌,拓展性与维护性太差改个逻辑极不方便。后来发现PHP是支持面向对象的,忽然觉得自己那是后还真是年轻,真是孤陋寡闻呀,毕竟PHP是用C来实现,也不足为奇。

前言:

从我们接触PHP开始,我们最先遇到的是函数:数组操作函数,字符串操作函数,文件操作函数等等。 这些函数是我们使用PHP的基础,也是PHP自出生就支持的面向过程编程。面向过程将一个个功能封装, 以一种模块化的思想解决问题。

从PHP4起开始支持面向对象编程。但PHP4的面向对象支持不太完善。 从PHP5起,PHP引入了新的对象模型(Object Model),增加了许多新特性,包括访问控制、 抽象类和final类、类方法、魔术方法、接口、对象克隆和类型提示等。并且在近期发布的PHP5.3版本中,针对面向对象编程增加了命名空间、延迟静态绑定以及增加了两个魔术方法__callStatic()和__invoke()。

那么,在PHP底层,其是怎么实现的呢,其结构如何?

一。类的结构

引用TIPI的一个事例:

class ParentClass {
}
 
interface Ifce {
        public function iMethod();
}
 
final class Tipi extends ParentClass implements Ifce {
        public static $sa = 'aaa';
        const CA = 'bbb';
 
        public function __constrct() {
        }
 
        public function iMethod() {
        }
 
        private function _access() {
        }
 
        public static function access() {
        }
}
登入後複製


这里定义了一个父类ParentClass,一个接口Ifce,一个子类Tipi。子类继承父类ParentClass, 实现接口Ifce,并且有一个静态变量$sa,一个类常量 CA,一个公用方法,一个私有方法和一个公用静态方法。 这些结构在Zend引擎内部是如何实现的?类的方法、成员变量是如何存储的?访问控制,静态成员是如何标记的?

首先,我们看看类的内部存储结构:


取上面這個結構的部分字段,我們分析文章最開始的那段PHP程式碼在核心中的表現。 如下所示:

#
欄位名稱欄位說明ParentClass類別Ifce介面Tipi類別
name類別名稱ParentClass#IfceTipi
type類別2(使用者自訂)2 (使用者自訂)2 (使用者自訂,1為系統內建類別)
parent父類別#ParentClass類別
refcount引用計數112
ce_flags類別的型別0144#524352
function_table#函數列表空白function_name=iMethod | type=2 | fn_flags=258function_name=__construct | type=2 | fn_flags=8448
function_name=iMethod | type= 2 | fn_flags=65800
function_name=_access | type=2 | fn_flags=66560
function_name=access | type=2 | fn_flags=257
#interfaces介面清單#Ifce介面介面數為1
filename存放檔案位址/tipi.php/tipi.php /ipi.php
#line_start#類別開始行數1518 22
line_end類別結束行數162038


二。变量与成员变量

PHP内核的存储机制(分离/改变)所介绍,

变量要么是定义在全局范围中,叫做全局变量,要么是定义在某个函数中, 叫做局部变量。

成员变量是定义在类里面,并和成员方法处于同一层次。如下一个简单的PHP代码示例,定义了一个类, 并且这个类有一个成员变量。

class Tipi { 
	public $var;
}
登入後複製

1.成员变量的访问:

访问这个成员变量当然是通过对象来访问。

2.成员变量的规则:

1.接口中不允许使用成员变量

2.成员变量不能拥有抽象属性

3.不能声明成员变量为final

4.不能重复声明属性

在声明类的时候初始化了类的成员变量所在的HashTable,之后如果有新的成员变量声明时,在编译时zend_do_declare_property。函数首先检查成员变量不允许的这4 条情况。

比如:.

class Tipi { 
	public final $var;
}
登入後複製

运行程序将报错,违反了第三条:Fatal error: Cannot declare property Tipi::$var final, the final modifier is allowed only for methods and classes in .. 这个错误由zend_do_declare_property函数抛出


三。函数与成员方法

成员方法从本质上来讲也是一种函数,所以其存储结构也和常规函数一样,存储在zend_function结构体中。



对于一个类的多个成员方法,它是以HashTable的数据结构存储了多个zend_function结构体。 和前面的成员变量一样,在类声明时成员方法也通过调用zend_initialize_class_data方法,初始化了整个方法列表所在的HashTable。 在类中我们如果要定义一个成员方法,格式如下:

class Tipi{ 
     public function t() {echo 1; }
}
登入後複製


除去访问控制关键字,一个成员方法和常规函数是一样的,从语法解析中调用的函数一样(都是zend_do_begin_function_declaration函数), 但是其调用的参数有一些不同,第三个参数is_method,成员方法的赋值为1,表示它作为成员方法的属性。 在这个函数中会有一系统的编译判断,比如在接口中不能声明私有的成员方法。 看这样一段代码:

interface Ifce { 
    private function method();
}
登入後複製


如果直接執行,程式會報錯:Fatal error: Access type for interface method Ifce::method() must be omitted in 這段程式碼對應到zend_do_begin_function_declaration函數中的程式碼。

四。方法(Function)與函數(Method)的異同


#在前面介紹了函數的實現,函數與方法的本質是比較相似的,都是將一系列的邏輯放到一個集合裡執行, 但二者在使用中也存在很多的不同,這裡我們討論一下二者的實現。 從實現的角度來看,二者內部代碼都被最終解釋為op_array,其執行是沒有區別的(除非使用了$this/self等對象特有的變法或方法), 而二者的不同體現在兩個面向:


1.是定義(註冊)的實作;


2.是呼叫的實作;

定義(註冊)方式的實作

函數和方法都是在編譯階段註冊到compiler_globals變數中的,二者都使用相同的核心處理函數zend_do_begin_function_declaration() 和zend_do_end_function_declaration()來完成這個過程。 二者的內部內容會被最終解釋並儲存為一個op_codes數組,但編譯後「掛載」的位置不同,如下圖:

 PHP中函數與方法的註冊位置

#呼叫方式的實作

#定義位置的不同,以及性質的不同,決定了方法比函數要進行更多的驗證工作, 方法的呼叫比函數的呼叫多一個名為ZEND_INIT_METHOD_CALL的OPCODE

, 

#其作用是把方法註冊到execute_data.fbc , 然後就可以使用與函數相同的處理函數 ZEND_DO_FCALL_BY_NAME

#進行處理。


##########

以上是PHP內核-類別與物件導向的程式碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板