php的扩展和嵌入--php内部变量
之前对于php的内部生命周期和Zend引擎的线程安全机制做了一个介绍,这里这篇文章则是主要介绍php的内部变量是如何实现的。
了解了这些实现的方法之后,对于写php,尤其是进行php扩展开发感觉相当有帮助。
php是一种类型比较松散的语言,与C相比不需要在使用变量前给出类型,直接用就可以。为了实现这一点,php必须在数据类型的定义上做一些工作。
数据类型:
最基本的类型被称为是zval或者说Zend Value,定义在Zend/zend.h头文件中。 typedef struct _zval_struct {zvalue_value value;
zend_uint refcount;
zend_uchar type;
zend_uchar is_ref;
} zval; 其中zvalue_value按照如下定义: typedef union _zvalue_value {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} zvalue_value; 这个是一个union,使用union的时候两种可能,一种是所有的变量共享一片内存空间,一种是要对其中的类型进行n选1的时候。
Zend定义了8种基本的数据类型,这八种基本上在别的语言中也都见过,所以只对比较特殊的类型进行说明: IS_NULL:non-valueIS_BOOL:IS_LONGIS_DOUBLEIS_STRING:分配的空间需要是长度+1IS_ARRAY: php的数组其实是hashtable,其中包括label和dataIS_OBJECT:在数组的基础上加上了 方法、访问修改符、有域的常量以及特殊的事件处理器IS_RESOURCE:比如文件的句柄或是mysql的句柄这些存储在zval的type中,并且与zvalue_value有着分别的对应关系 注意下面有两个类型判断函数的对比:
void describe_zval(zval *foo) { if (foo->type == IS_NULL) { php_printf("The variable is NULL"); } else { php_printf("The variable is of type %d", foo->type); } }
void describe_zval(zval *foo) { if (Z_TYPE_P(foo) == IS_NULL) { php_printf("The variable is NULL"); } else { php_printf("The variable is of type %d", Z_TYPE_P(foo)); } }
第一段代码中采用的是c的写法,第二段代码是带有php特色的写法。 注意到Zend头文件中提供了很多对zval处理的宏,最好用它们,这里就是用了Z_TYPE_P(foo)。同样还有Z_TYPE()和Z_TYPE_PP()分别对应zval和zval** php_printf()则是在printf的基础上做了些针对SAPI和php输出机制的优化。
数据值
通过一些宏可以获取不同类型的zval的值: BVAL(): BooleanLVAL(): longDVAL(): double 这个函数针对三种不同的zval类型,分别利用Z_TYPE进行了类型判断。然后利用相应的值提取的宏进行取值。void display_values(zval boolzv, zval *longpzv, zval **doubleppzv) { if (Z_TYPE(boolzv) == IS_BOOL) { php_printf("The value of the boolean is: %s\n", Z_BVAL(boolzv) ? "true" : "false"); } if (Z_TYPE_P(longpzv) == IS_LONG) { php_printf("The value of the long is: %ld\n", Z_LVAL_P(longpzv)); } if (Z_TYPE_PP(doubleppzv) == IS_DOUBLE) { php_printf("The value of the double is: %f\n", Z_DVAL_PP(doubleppzv)); } }
对于string的处理则要稍微特殊一些:需要两个宏Z_STRVA和Z_STRLEN分别读取值和长度,这个从string类型的定义中也可以看到,它是由字符和长度组成的。
void display_string(zval *zstr) { if (Z_TYPE_P(zstr) != IS_STRING) { php_printf("The wrong datatype was passed!\n"); return; } PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr)); }
对数组的访问使用的是ARRVAL系列:Z_ARRVAL(zv), Z_ARRVAL_P(pzv), Z_ARRVAL_PP(ppzv). 有一些版本的php源码中HASH_OF()等同于Z_ARRVAL_P,但是这个宏已经渐渐的用的少了.
对于Object: OBJ_HANDLE 返回对象句柄标识OBJ_HT 句柄表OBJCE 类定义OBJPROP 属性哈希表OBJ_HANDLER 在OBJ_HT中操作特定处理方法 对于资源Resource就直接用宏RESVAL
数据创建:
想要创造一个变量并分配空间的malloc(sizeof(zval))在php这里并不可行。应该使用MAKE_STD_ZVAL(pzv), 它对空间的分配进行了优化,并且会自动的初始化refCount(表示这个变量被引用的次数)和is_ref(是否是强制引用)这两个性质。注意它的输入是一个指针.ALLOC_INIT_ZVAL()也可以进行初始化,不同之处在于把zval*的值设为了NULL.
在设置不同类型的值的时候有很多形式,左边是比较简略的形式,右侧则是展开的形式: ZVAL_NULL(pvz); Z_TYPE_P(pzv) = IS_NULL;
ZVAL_BOOL(pzv, b); Z_TYPE_P(pzv) = IS_BOOL;
Z_BVAL_P(pzv) = b ? 1 : 0;
ZVAL_TRUE(pzv); ZVAL_BOOL(pzv, 1);
ZVAL_FALSE(pzv); ZVAL_BOOL(pzv, 0);
ZVAL_LONG(pzv, l); Z_TYPE_P(pzv) = IS_LONG;
Z_LVAL_P(pzv) = l;
ZVAL_DOUBLE(pzv, d); Z_TYPE_P(pzv) = IS_DOUBLE;
Z_DVAL_P(pzv) = d;
对于字符串的处理要特殊一些,提供了一个单独的参数dup. 这个参数决定了是否创建一个字符串的副本.举个例子 zval * pzva; ZVAL_STRING(pzval,"hello world",1); 由于“hello_world”是一个常量字符串,直接对它进行操作显然不合适,所以把dup设为1的话,会自动的给它创建一个副本,然后再赋给pzval. 这使得整个过程更加简洁。
ZVAL_STRINGL(pzv,str,len,dup); Z_TYPE_P(pzv) = IS_STRING;
Z_STRLEN_P(pzv) = len;
if (dup) {
Z_STRVAL_P(pzv) =
estrndup(str, len + 1);
} else {
Z_STRVAL_P(pzv) = str;
}
ZVAL_STRING(pzv, str, dup); ZVAL _STRINGL(pzv, str,
strlen(str), dup); 注意dup如果设为1的话就是申请新的空间并且拷贝内容,而不是一个shaddow copy。 ZVAL_RESOURCE(pzv, res); Z_TYPE_P(pzv) = IS_RESOURCE;
Z_RESVAL_P(pzv) = res;
数据的存储
数据的存储都在符号表中。 symbol table,每当创建一个新的变量的时候,Zend都保存这个值到这个内部的数组中去。 符号表在RINIT之前创建,在RSHUTDOWN之后销毁。当用户空间的函数或对象方法被调用的时候,会创建一个新的符号表,生命与函数执行时间相同。 在Zend/zend_gblobals.h中定义了两个元素:
struct _zend_executor_globals { ... HashTable symbol_table; HashTable *active_symbol_table; ... };
通过EG(symbol_table) 的方式可以访问符号表。感觉跟$GLOBALS似的。 注意到EG(symbol_table)这个宏返回的不是指针,必须加上&。 下面的这个对比非常的有趣: In PHP:
<?php $foo = 'bar'; ?>
In C:
{ zval *fooval; MAKE_STD_ZVAL(fooval); //首先分配空间,设置变量 ZVAL_STRING(fooval, "bar", 1); //然后赋值,创建一个copy,你不能直接操作常字符串 ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval); // 在符号表中注册,foo是一个label }
数据的获取: 在获取数据的时候,比较多的是使用zend_hash_find()函数:
{ zval **fooval; if (zend_hash_find(EG(active_symbol_table), "foo", sizeof("foo"), (void**)&fooval) == SUCCESS) { php_printf("Got the value of $foo!"); } else { php_printf("$foo is not defined."); } }
数据转换: 仅仅是说一下有这个功能,比如convert_to_string(zval *value)可以把zval转换为字符串。
以上就是php内部变量的一些介绍,为了能够区分不同的类型、设置获取变量值以及在符号表中增加和查找变量,这些知识必不可少。

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제









PHP용 SNMP 확장은 PHP가 SNMP 프로토콜을 통해 네트워크 장치와 통신할 수 있도록 하는 확장입니다. 이 확장 기능을 사용하면 CPU, 메모리, 네트워크 인터페이스 등 네트워크 장치의 구성 정보와 라우터, 스위치 등의 기타 정보를 쉽게 얻고 수정할 수 있습니다. 장치 포트 전환과 같은 제어 작업도 수행할 수 있습니다. 이 기사에서는 SNMP 프로토콜에 대한 기본 지식, PHP의 SNMP 확장 설치 방법, PHP에서 SNMP 확장을 사용하여 네트워크 장치를 모니터링하고 제어하는 방법을 소개합니다. 1. SN

PHP와 HTML의 조합은 웹 개발의 일반적인 기술입니다. PHP는 HTML 파일에 동적 콘텐츠를 포함하고 보조 기능을 구현할 수 있으므로 웹 사이트의 상호 작용성과 사용자 정의 가능성이 크게 향상됩니다. 이 기사에서는 코드 삽입을 위한 세 가지 기술을 소개하고 참조용 특정 코드 예제를 제공합니다. 1. PHP 태그를 사용하여 코드 삽입 가장 일반적인 방법은 PHP 태그()를 사용하여 PHP 코드를 HTML 파일에 삽입하여 동적 콘텐츠를 표시하는 것입니다. 예를 들어 PHP를 사용할 수 있습니다.

처음부터 끝까지: HTTP 요청에 PHP 확장 cURL을 사용하는 방법 소개: 웹 개발에서는 종종 타사 API 또는 기타 원격 서버와 통신해야 합니다. cURL을 사용하여 HTTP 요청을 하는 것은 일반적이고 강력한 방법입니다. 이 기사에서는 PHP를 사용하여 cURL을 확장하여 HTTP 요청을 수행하는 방법을 소개하고 몇 가지 실용적인 코드 예제를 제공합니다. 1. 준비 먼저 php에 cURL 확장이 설치되어 있는지 확인하세요. 명령줄에서 php-m|grepcurl을 실행하여 확인할 수 있습니다.

PHP 함수 기능을 확장하려면 확장 기능과 타사 모듈을 사용할 수 있습니다. 확장은 pecl 패키지 관리자를 통해 설치하고 활성화할 수 있는 추가 기능과 클래스를 제공합니다. 타사 모듈은 특정 기능을 제공하며 Composer 패키지 관리자를 통해 설치할 수 있습니다. 실제 예로는 확장 기능을 사용하여 복잡한 JSON 데이터를 구문 분석하고 모듈을 사용하여 데이터 유효성을 검사하는 것이 포함됩니다.

1.UncaughtError:Calltoundefinitivefunctionmb_strlen(); 위의 오류가 발생하면 mbstring 확장이 설치되지 않았음을 의미합니다. 2. PHP 설치 디렉터리 cd/temp001/php-7.1.0/ext/mbstring을 입력합니다. 3. phpize( /usr/local/bin /phpize 또는 /usr/local/php7-abel001/bin/phpize) PHP 확장 4를 설치하는 명령../configure--with-php-config=/usr/local/php7-abel

Aurora 푸시 확장을 사용하여 PHP 애플리케이션에서 일괄 메시지 푸시 기능을 구현하는 방법 모바일 애플리케이션 개발에서 메시지 푸시는 매우 중요한 기능입니다. Jiguang Push는 풍부한 기능과 인터페이스를 제공하는 일반적으로 사용되는 메시지 푸시 서비스입니다. 이 기사에서는 Aurora 푸시 확장을 사용하여 PHP 애플리케이션에서 일괄 메시지 푸시 기능을 구현하는 방법을 소개합니다. 1단계: Jiguang Push 계정 등록 및 API 키 획득 먼저 Jiguang Push 공식 웹사이트(https://www.jiguang.cn/push)에 등록해야 합니다.

PHP는 웹 애플리케이션을 개발하고 파일을 처리하는 데 사용할 수 있는 인기 있는 서버측 언어입니다. PHP용 ZipArchive 확장은 PHP에서 zip 파일을 조작하기 위한 강력한 도구입니다. 이 기사에서는 PHP의 ZipArchive 확장을 사용하여 zip 파일을 생성하고 읽고 수정하는 방법을 다룹니다. 1. ZipArchive 확장을 설치합니다. ZipArchive 확장을 사용하기 전에 확장이 설치되었는지 확인해야 합니다. 설치 방법은 다음과 같습니다. 1. 설치

PHP용 POSIX 확장은 PHP가 POSIX 호환 운영 체제와 상호 작용할 수 있게 해주는 함수 및 상수 세트입니다. POSIX(PortableOperatingSystemInterface)는 소프트웨어 개발자가 다양한 UNIX 또는 UNIX 유사 운영 체제에서 실행될 수 있는 응용 프로그램을 작성할 수 있도록 설계된 운영 체제 인터페이스 표준 세트입니다. 이 기사에서는 설치 및 사용을 포함하여 PHP용 POSIX 확장을 사용하는 방법을 소개합니다. 1. PHP의 POSIX 확장을 설치합니다.
