PHP内核探索:变量存储与类型使用说明_PHP教程
先回答前面一节的那个问题吧。
$foo = 10;
$bar = 20;
function change() {
global $foo;
//echo '函数内部$foo = '.$foo.'
';
//如果不把$bar定义为global变量,函数体内是不能访问$bar的
$bar = 0;
$foo++;
}
change();
echo $foo, ' ', $bar;
?>
程序输出 11 20。原因是,方法内部无法访问$bar变量,所以它的值还是20。使用global之后,可以取得$foo的值,自增后$foo的值就是11。
Global的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的所有文件。
前言中提到变量的三个基本特性,其中的有一个特性为变量的类型,变量都有特定的类型, 如:字符串、数组、对象等等。编程语言的类型系统可以分为强类型和弱类型两种:
强类型语言是一旦某个变量被申明为某个类型的变量,则在程序运行过程中,该不能将该变量的类型以外的值赋予给它 (当然并不完全如此,这可能会涉及到类型的转换,后面的小节会有相应介绍),C/C++/Java等语言就属于这类。
PHP及Ruby,JavaScript等脚本语言属于弱类型语言:一个变量可以表示任意的数据类型。
PHP之所以成为一个简单而强大的语言,很大一部分的原因是它拥有弱类型的变量。 但是有些时候这也是一把双刃剑,使用不当也会带来一些问题。就像仪器一样,越是功能强大, 出现错误的可能性也就越大。
在官方的PHP实现内部,所有变量使用同一种数据结构(zval)来保存,而这个结构同时表示PHP中的各种数据类型。 它不仅仅包含变量的值,也包含变量的类型。这就是PHP弱类型的核心。
那zval结构具体是如何实现弱类型的呢,下面我们一起来揭开面纱。
变量存储结构
PHP在声明或使用变量的时候,并不需要显式指明其数据类型。
PHP是弱类型语言,这并不表示PHP没有类型,在PHP中,存在8种变量类型,可以分为三类
* 标量类型:boolean、integer、float(double)、string
* 复合类型: array、object
* 特殊类型: resource、NULL
官方PHP是用C实现的,而C是强类型的语言,那这是怎么实现PHP中的弱类型的呢?
变量的值存储到以下所示zval结构体中。 zval结构体定义在Zend/zend.h文件,其结构如下:
typedef struct _zval_struct zval;
...
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
PHP使用这个结构来存储变量的所有数据。和其他编译性静态语言不同, PHP在存储变量时将PHP用户空间的变量类型也保存在同一个结构体中。这样我们就能通过这些信息获取到变量的类型。
zval结构体中有四个字段,其含义分别为:
属性名 | 含义 | 默认值 |
refcount__gc | 表示引用计数 | 1 |
is_ref__gc | 表示是否为引用 | 0 |
value | 存储变量的值 | |
type | 变量具体的类型 |
在PHP5.3之后,引入了新的垃圾收集机制,引用计数和引用的字段名改为refcount__gc和is_ref__gc。在此之前为refcount和is__ref。
而变量的值则存储在另外一个结构体zvalue_value中。值存储见下面的介绍。
PHP用户空间指的在PHP语言这一层面,而本书中大部分地方都在探讨PHP的实现。 这些实现可以理解为内核空间。由于PHP使用C实现,而这个空间的范畴就会限制在C语言。 而PHP用户空间则会受限于PHP语法及功能提供的范畴之内。 例如有些PHP扩展会提供一些PHP函数或者类,这就是向PHP用户空间导出了方法或类。
变量类型
zval结构体的type字段就是实现弱类型最关键的字段了,type的值可以为: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一。 从字面上就很好理解,他们只是类型的唯一标示,根据类型的不同将不同的值存储到value字段。 除此之外,和他们定义在一起的类型还有IS_CONSTANT和IS_CONSTANT_ARRAY。
这和我们设计数据库时的做法类似,为了避免重复设计类似的表,使用一个标示字段来记录不同类型的数据。
变量的值存储
前面提到变量的值存储在zvalue_value联合体中,结构体定义如下:
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
这里使用联合体而不是用结构体是出于空间利用率的考虑,因为一个变量同时只能属于一种类型。 如果使用结构体的话将会不必要的浪费空间,而PHP中的所有逻辑都围绕变量来进行的,这样的话, 内存浪费将是十分大的。这种做法成本小但收益非常大。
各种类型的数据会使用不同的方法来进行变量值的存储,其对应赋值方式如下:
1. 一般类型
变量类型 | 宏 | ? |
boolean | ZVAL_BOOL | 布尔型/整型的变量值存储于(zval).value.lval中,其类型也会以相应的IS_*进行存储。 Z_TYPE_P(z)=IS_BOOL/LONG; Z_LVAL_P(z)=((b)!=0); |
integer | ZVAL_LONG | |
float | ZVAL_DOUBLE | |
null | ZVAL_NULL | NULL值的变量值不需要存储,只需要把(zval).type标为IS_NULL。 Z_TYPE_P(z)=IS_NULL; |
resource | ZVAL_RESOURCE | 资源类型的存储与其他一般变量无异,但其初始化及存取实现则不同。 Z_TYPE_P(z) = IS_RESOURCE; Z_LVAL_P(z) = l; |
2. 字符串Sting
字符串的类型标示和其他数据类型一样,不过在存储字符串时多了一个字符串长度的字段。
struct {
char *val;
int len;
} str;
C中字符串是以\0结尾的字符数组,这里多存储了字符串的长度,这和我们在设计数据库时增加的冗余字段异曲同工。 因为要实时获取到字符串的长度的时间复杂度是O(n),而字符串的操作在PHP中是非常频繁的,这样能避免重复计算字符串的长度, 这能节省大量的时间,是空间换时间的做法。 这么看在PHP中strlen()函数可以在常数时间内获取到字符串的长度。 计算机语言中字符串的操作都非常之多,所以大部分高级语言中都会存储字符串的长度。
3. 数组Array
数组是PHP中最常用,也是最强大变量类型,它可以存储其他类型的数据,而且提供各种内置操作函数。数组的存储相对于其他变量要复杂一些, 数组的值存储在zvalue_value.ht字段中,它是一个HashTable类型的数据。 PHP的数组使用哈希表来存储关联数据。哈希表是一种高效的键值对存储结构。PHP的哈希表实现中使用了两个数据结构HashTable和Bucket。 PHP所有的工作都由哈希表实现,在下节HashTable中将进行哈希表基本概念的介绍以及PHP的哈希表实现。
4. 对象Object
在面向对象语言中,我们能自己定义自己需要的数据类型,包括类的属性,方法等数据。而对象则是类的一个具体实现。 对象有自身的状态和所能完成的操作。
PHP的对象是一种复合型的数据,使用一种zend_object_value的结构体来存放。其定义如下:
typedef struct _zend_object_value {
zend_object_handle handle; // unsigned int类型,EG(objects_store).object_buckets的索引
zend_object_handlers *handlers;
} zend_object_value;
PHP的对象只有在运行时才会被创建,前面的章节介绍了EG宏,这是一个全局结构体用于保存在运行时的数据。 其中就包括了用来保存所有被创建的对象的对象池,EG(objects_store),而object对象值内容的zend_object_handle域就是当前 对象在对象池中所在的索引,handlers字段则是将对象进行操作时的处理函数保存起来。 这个结构体及对象相关的类的结构_zend_class_entry,后面会介绍到。
PHP的弱变量容器的实现方式是兼容并包的形式体现,针对每种类型的变量都有其对应的标记和存储空间。 使用强类型的语言在效率上通常会比弱类型高,因为很多信息能在运行之前就能确定,这也能帮助排除程序错误。 而这带来的问题是编写代码相对会受制约。
PHP主要的用途是作为Web开发语言,在普通的Web应用中瓶颈通常在业务和数据访问这一层。不过在大型应用下语言也会是一个关键因素。 facebook因此就使用了自己的php实现。将PHP编译为C++代码来提高性能。不过facebook的hiphop并不是完整的php实现, 由于它是直接将php编译为C++,有一些PHP的动态特性比如eval结构就无法实现。当然非要实现也是有方法的, hiphop不实现应该也是做了一个权衡。

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

設定無線網路很常見,但選擇或變更網路類型可能會令人困惑,尤其是在您不知道後果的情況下。如果您正在尋找有關如何在Windows11中將網路類型從公用變更為私有或反之亦然的建議,請繼續閱讀以取得一些有用的資訊。 Windows11中有哪些不同的網路設定檔? Windows11附帶了許多網路設定文件,這些設定檔本質上是可用於配置各種網路連線的設定集。如果您在家中或辦公室有多個連接,這將非常有用,因此您不必每次連接到新網路時都進行所有設定。專用和公用網路設定檔是Windows11中的兩種常見類型,但通

隨著短影片平台的盛行,影片矩陣帳號行銷已成為一種新興行銷方式。透過在不同平台上建立和管理多個帳號,企業和個人能夠實現品牌推廣、粉絲成長和產品銷售等目標。本文將為您探討如何有效運用視訊矩陣帳號,並介紹不同類型的視訊矩陣帳號。一、視訊矩陣帳號怎麼做?要做好視訊矩陣帳號,需要遵循以下幾個步驟:首先要明確你的影片矩陣帳號的目標是什麼,是為了品牌傳播、粉絲成長還是產品銷售。明確目標有助於制定相應的策略。 2.選擇平台:根據你的目標受眾,選擇合適的短影片平台。目前主流的短視頻平台有抖音、快手、火山小影片等。

Part1聊聊Python序列類型的本質在本部落格中,我們來聊聊探討Python的各種「序列」類,內建的三大常用資料結構-清單類別(list)、元組類(tuple)和字符串類(str)的本質。我不知道你發現沒有,這些類別都有一個很明顯的共通性,都可以用來保存多個資料元素,最主要的功能是:每個類別都支援下標(索引)存取該序列的元素,例如使用語法Seq[i]。其實上面每個類別都是使用陣列這種簡單的資料結構表示。但熟悉Python的讀者可能知道這3種資料結構又有些不同:例如元組和字串是不能修改的,列表可以

Go函數可以傳回多個不同類型的值,傳回值類型在函數簽章中指定,並透過return語句傳回。例如,函數可以傳回一個整數和一個字串:funcgetDetails()(int,string)。在實戰中,一個計算圓面積的函數可以回傳面積和一個可選錯誤:funccircleArea(radiusfloat64)(float64,error)。注意事項:如果函數簽章未指定類型,則傳回空值;建議使用明確類型宣告的return語句以提高可讀性。

使用動態語言一時爽,程式碼重構火葬場。相信你一定聽過這句話,和單元測試一樣,雖然寫程式碼的時候花費你少量的時間,但是從長遠來看,這是非常值得的。本文分享如何更好的理解和使用Python的類型提示。 1、類型提示僅在語法層面有效類型提示(自PEP3107開始引入)用於在變數、參數、函數參數以及它們的傳回值、類別屬性和方法中新增類型。 Python的變數類型是動態的,可以在運行時修改,為程式碼添加類型提示,僅在語法層面支持,對程式碼的運行沒有任何影響,Python解釋器在運行程式碼的時候會忽略類型提示。因此類型提

C++函式有以下型別:簡單函式、const函式、靜態函式、虛函式;特性包括:inline函式、預設參數、參考回傳、重載函式。例如,calculateArea函數使用π計算給定半徑圓的面積,並將其作為輸出傳回。

隨著網路的快速發展,自媒體成為了資訊傳播的重要管道。自媒體平台為個人和企業提供了一個展示自己、傳播訊息的舞台。目前,市場上主要的自媒體平台有微信公眾號、今日頭條、一點資訊、企鵝媒體平台等。這些平台各有特色,為廣大自媒體從業人員提供了豐富的創作空間。接下來,我們將對這些平台進行詳細介紹,並探討自媒體平台的類型。一、主要的自媒體平台有哪些?微信公眾號是騰訊推出的自媒體平台,為個人和企業用戶提供資訊發布和傳播服務。它分為服務號碼和訂閱號碼兩種類型,服務號主要為企業提供服務,而訂閱號則側重於資訊傳播。由

標題:深入探討Golang變數的儲存位置和機制隨著Go語言(Golang)在雲端運算、大數據和人工智慧領域的應用逐漸增多,深入了解Golang變數的儲存位置和機制變得尤為重要。在本文中,我們將詳細探討Golang中變數的記憶體分配、儲存位置以及相關的機制。透過具體程式碼範例,幫助讀者更好地理解Golang變數在記憶體中是如何儲存和管理的。 1.Golang變數的內存
