번역: 세상에 빠졌어요
원본 주소 : http://devzone.zend.com/303/extension-writing-part-i-introduction-to-php-and-zend/
블로그 주소: http://lg.uuhonghe . com/index/view?id=3
소개
확장이란
라이프사이클
Hello World
자신만의 확장 프로그램 만들기
ini 구성
전역 변수
ini를 전역 변수로 설정
무결성 검사
그럼?
소개
이 튜토리얼을 읽고 계시다면 PHP 언어로 확장 기능을 작성하는 데 관심이 있으실 것입니다. 그렇지 않다면. . . 아마도 이 책을 읽고 나면 이전에 알지 못했던 것에 관심이 있다는 것을 알게 될 것입니다.
이 글은 독자가 PHP 언어와 C로 작성된 PHP 인터프리터에 대한 기본적인 이해를 갖고 있다고 가정합니다.
먼저 PHP 확장을 작성하려는 이유를 확인해 보겠습니다.
1. 추상화로 인해 언어 코어 Depth를 사용하면 PHP를 사용하여 직접 수행할 수 없는 일부 라이브러리 및 시스템 호출을 사용할 수 있습니다.
2. PHP가 몇 가지 특이한 방식으로 자체 동작을 구현하기를 원합니다.
3. 많은 PHP 코드를 작성했지만 더 빠르게 실행될 수 있다는 것을 알고 있습니다.
4. 판매하고 싶은 특히 기발한 아이디어를 구현하는 코드가 있지만 더 중요한 것은 판매하려는 코드가 실행될 수 있어야 하지만 실행될 수는 없다는 것입니다. 소스 코드에서 볼 수 있습니다.
이는 모두 매우 타당한 이유이지만 확장 프로그램을 만들려면 먼저 확장 프로그램이 무엇인지 이해해야 합니다.
확장이란 무엇인가요?
PHP를 작성했다면 확장 기능을 사용했을 것입니다. 몇 가지 확장만 있으면 PHP의 모든 사용자 공간 기능은 이 확장 또는 저 확장의 기능 그룹에 있습니다. 이러한 기능 중 다수는 표준 확장의 일부이며 총 400개 이상입니다. PHP 소스 코드에는 86개의 확장 기능이 번들로 포함되어 있으며 각 확장 기능에는 평균 약 30개의 기능이 포함되어 있습니다. 계산해 보면 총 약 2500개의 기능이 있습니다. 충분하지 않다면 PECL 저장소에서 100개 이상의 추가 확장 기능을 사용할 수 있으며, 온라인의 다른 곳에서도 더 많은 확장 기능을 찾을 수 있습니다.
PHP의 핵심은 두 부분으로 구성됩니다. 하단에는 Zend Engine(줄여서 ZE)이 있습니다. ZE는 사람이 인식할 수 있는 스크립트를 기계가 인식할 수 있는 기호로 구문 분석하고 이러한 기호를 프로세스 공간에서 실행합니다. ZE는 메모리 관리, 변수 필드 및 함수 호출을 동시에 처리합니다. 이러한 차이점의 또 다른 부분은 PHP 코어입니다. PHP 커널은 통신, 연결 및 SAPI 계층(서버 응용 프로그래밍 인터페이스, Apache, IIS, CLI, CGI와 같은 호스트 환경을 참조하는 데 자주 사용됨)을 처리합니다.
등)은 파일 및 네트워크 I/O를 위한 사용자 공간 함수 fopen(), fread() 및 fwrite()와 관련된 스트리밍 계층뿐만 아니라 제어 계층에서 safe_mode 및 open_basedir의 통합 감지도 제공합니다.
라이프 사이클
SAPI가 시작될 때(예: /usr/local/apache/bin/apachectl) start
에 대한 응답으로 PHP는 커널 하위 시스템을 초기화하여 시작합니다. 이 부팅 프로세스가 끝나면 각 확장의 커널이 로드되고 해당 모듈 초기화 루틴(MINIT)이 호출됩니다. 이는 각 확장에 내부 변수를 초기화하고, 리소스를 할당하고, 리소스 핸들을 등록하고, 해당 함수를 ZE에 등록할 수 있는 기회를 제공하므로 스크립트가 이러한 함수를 호출할 때 ZE는 어느 것을 실행할지 알 수 있습니다. 코드 조각.
다음으로, PHP는 SAPI 레이어가 처리할 페이지를 요청할 때까지 기다립니다. CGI 또는 CLI SAPI의 경우 이는 직접적으로 한 번만 발생합니다. Apache, IIS 또는 기타 성숙한 웹 서버 SAPI에서 이는 원격 사용자가 요청할 때 발생하며 동시성으로 여러 번 발생할 수 있습니다. 요청이 어떻게 도착하는지에 관계없이 PHP는 ZE에게 스크립트를 실행할 환경을 설정하라고 지시한 후 각 확장의 요청 초기화(RINI
RINI는 확장 프로그램을 통해 고유한 특정 환경 변수를 설정하고, 특정 리소스에 대한 요청을 할당하거나, 감사와 같은 기타 작업을 수행할 수 있는 기회를 제공합니다. RINI함수 동작의 대표적인 예는 세션입니다. 확장에서 session.auto_start 항목이 활성화되면 RINI가 자동으로 사용자 공간 session_start() 함수를 실행하고 $_SESSION변수. 요청이 초기화되면 ZE는 PHP 스크립트를 토큰으로 변환하고 마지막으로 단일 단계 디버깅 및 실행이 가능한 opcode로 변환합니다.
opcode 중 하나에 포함된 확장 메소드가 호출되면 ZE는 메소드의 매개변수를 묶고 일시적으로 직접 메소드 완성에 제어권을 넘깁니다.
스크립트가 완료된 후 PHP는 각 확장 요청 종료(RSHUTDOWN) 함수를 호출하여 최종 정리 작업(예: 세션 변수를 확인하는 등)을 수행합니다. 디스크). 다음으로, ZE는 요청의 이전 부분에서 사용된 모든 변수에 대해 unset() 작업을 효과적으로 수행하는 정리 프로세스(가비지 수집이라고 함)를 실행합니다.
작업이 완료된 후 PHP는 SAPI가 다른 문서를 요청하거나 신호가 닫힐 때까지 기다립니다. CGI, CLI SAPI의 경우 "다음 요청"이 없으므로 SAPI가 직접 종료 프로세스를 시작합니다. 종료 프로세스 동안 PHP는 각 확장을 다시 거쳐 모듈 종료(MSHUTDOWN) 함수를 호출하고 마지막으로 자체 커널 하위 시스템을 종료합니다.
위의 내용이 겁나게 들릴 수도 있지만 작동하는 확장 프로그램을 개발하기 시작하면 일부 내용이 점차 명확해집니다.
메모리 관리
잘못 작성된 확장 메모리의 손실을 방지하기 위해, ZE는 지속성을 나타내는 추가 플래그를 사용하여 내부 메모리 관리자를 실행합니다. 메모리 할당이 페이지 요청보다 오래 지속되도록 하려면 지속적인 할당이 중요합니다. 반면, 비지속적 할당은 해제 함수 호출 여부에 관계없이 할당된 요청이 끝나면 해제됩니다. 예를 들어 사용자 공간 변수는 요청이 끝난 후 더 이상 사용되지 않으면 비영구적으로 할당됩니다.
그러나 아마도 확장은 이론적으로 페이지 요청이 끝날 때 비영구 메모리를 자동으로 해제하기 위해 ZE에 의존할 것입니다. 이는 권장되지 않습니다. 메모리를 할당하면 재활용 취소 기간이 길어지고, 메모리 관련 리소스가 적시에 닫힐 가능성이 낮으며, 정리 작업이 없으면 이 작업이 엉망이 됩니다. 나중에 보게 되겠지만, 할당된 데이터가 제 시간에 정리되는지 확인하는 것은 간단한 문제입니다. 전통적인 메모리 할당(외부 라이브러리를 사용할 때 사용해야 함)과 PHP/ZE의 지속성 및 비지속적 메모리 할당을 간단히 비교해 보겠습니다.
Traditional | Non-Persistent | Persistent |
---|---|---|
*
**
개발 환경 구축
이제 PHP와 Zend 엔진의 작동 방식에 대한 몇 가지 이론을 배웠으니, 시작하고 싶어 그의 기술이 발전하기 시작했습니다. 그런 다음 시작하기 전에 요구 사항을 충족하는 개발 환경을 설정하는 데 필요한 몇 가지 작업을 수집해야 합니다.
먼저 PHP 자체가 필요합니다. 이 일련의 개발 도구는 PHP와 분리될 수 없기 때문입니다. 소스 코드를 사용하여 PHP를 구축하는 데 익숙하지 않다면 먼저 이 기사를 읽어 보시기 바랍니다: http://www.php.net/install.unix.
(Windows를 사용하여 PHP 확장을 개발하는 방법에 대한 기사는 나중에 제공됩니다) . 안전하게 Linux 배포판에 포함된 바이너리 패키지를 사용하고 싶은 유혹이 있지만, 개발 중에 매우 편리한 두 가지 ./configure 옵션이 누락됩니다. 첫 번째는
완료되었습니다. 따라서 아래에서는 "Hello World" 문자열을 반환하는 함수 하나만 확장해보겠습니다. PHP 코드에서는 다음과 같이 작성할 수 있습니다.
이제 PHP 확장을 사용하여 이 코드를 구현합니다. 먼저 ext/ 디렉터리에 디렉터리를 만듭니다. PHP 소스 코드 hello
<?php function hello_word(){ return 'Hello World'; } ?>
메서드가 포함된 소스 파일, 확장 프로그램을 로드하기 위한 PHP에 대한 참조가 포함된 헤더 파일,
php_hello.h
PHP_ARG_ENABLE(hello, whether to enable Hello World support, [--enable-hello Enable Hello World support]) if test "$PHP_HELLO" = "yes"; then AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World]) PHP_NEW_EXTENSION(hello, hello.c, $ext_shared) fi
hello.c
#ifndef PHP_HELLO_H #define PHP_HELLO_H 1 #define PHP_HELLO_WORLD_VERSION "1.0" #define PHP_HELLO_WORLD_EXTNAME "hello" PHP_FUNCTION(hello_world); extern zend_module_entry hello_module_entry; #define phpext_hello_ptr &hello_module_entry #endif
위 확장 예제의 코드 대부분은 글루 프로토콜 언어이며, 이는 PHP에 확장을 소개하고 PHP에 대한 대화를 설정하는 데 사용됩니다. 그들이 의사소통하게 하세요. 코드의 마지막 4줄만 "실제 코드"라고 부를 수 있으며 사용자 공간 레이어 스크립트가 상호 작용할 수 있는 작업을 수행하는 데 사용됩니다. 실제로 이 수준의 코드는 이전에 살펴본 PHP 코드와 매우 유사해 보이며 이해하기 쉽습니다.
1、声明一个方法
2、让这个方法返回一个字符串:"Hello World"
3、。。呃。。。1?这个1几个意思?
回忆一下,ZE包含了一套复杂的内存管理层,能确保分配的资源在脚本退出时被释放。然而在内存管理的掌控下,释放同样的块两次是非常大的禁忌。这种行为通常称为"double freeing",是一个常见的段错误的原因,涉及到一个正在调用的程序访问一个不再属于它的内存块。同样的,你不希望允许ZE去释放一个存活于程序空间并且其数据块被其他进程占用的静态字符串buffer(例如我们示例中的"Hello World")。
PHP_FUNCTION(hello_world) { char *str; str = estrup("hello World"); RETURN_STRING(str, 0); }
在此版本中,我们手动地分配内存给"Hello World"这个串,并且最终传回调用脚本,然后把内存传给
编译你的扩展
本练习的最后一步就是将你的扩展编译为一个动态可加载模块。如果你把上面的示例原封不动的抄下来,那么只需要在ext/hello/目录下运行下面三步命令就行:
phpize ./configure --enable-hello (译者注:如果编译PHP的时候使用了 --prefix 参数,此处要加上 --with-php-config 选项, 如笔者编译PHP时使用的是 ./configure --prefix=/use/local/phpdev/ 此处命令应使用 ./configure --enable-hello --with-php-c/local/phpdev/bin/php-config) make
运行完上述三个命令之后,你应该在ext/hello/modules/下找到一个 hello.so 文件。(译者注:如果在make时报错: error: unknown type name 'function_entry' ,可以把 'function_entry' 改为 'zend_function_entry',参见:https://bugs.php.net/bug.php?id=61479
)。现在,就像其他PHP扩展一样,你可以把你的扩展拷贝到扩展目录(默认为,/usr/local/lib/php/extensions/,可以通过php.ini确认)然后在php.ini里加上extension=hello.so一行可以在以触发它在程序启动时被加载到了。对于CGI/CLI SAPIs 来说,启动就指下一次运行PHP;而对我web server SAPIs如Apache来说,启动指下次web server重启。让我们试下运行下面命令:
$ php -r 'echo hello_world();'
如果一切顺利,你现在应该能看到这段代码输出"Hello World"了,因为你的扩展里的"hello_world()"返回了一个字符串"Hello World",而echo命令会原封不动地显示传递给他的参数(此处即为该函数的返回值)。
其他标量也可用类似的方式返回,使用
static function_entry hello_functions[] = { PHP_FE(hello_world, NULL) PHP_FE(hello_long, NULL) PHP_FE(hello_double, NULL) PHP_FE(hello_bool, NULL) PHP_FE(hello_null, NULL) {NULL, NULL, NULL} }; PHP_FUNCTION(hello_long) { RETURN_LONG(42); } PHP_FUNCTION(hello_double) { RETURN_DOUBLE(3.1415926535); } PHP_FUNCTION(hello_bool) { RETURN_BOOL(1); } PHP_FUNCTION(hello_null) { RETURN_NULL(); }
你同样需要在头文件php_hello.h里
PHP_FUNCTION(hello_world); PHP_FUNCTION(hello_long); PHP_FUNCTION(hello_double); PHP_FUNCTION(hello_bool); PHP_FUNCTION(hello_null);
因为你没有修改config.m4文件,所以技术上这次跳过phpize和./configure这两个步骤直接make是安全的。然后,在本游戏的这个阶段,我还是要求你从头把三个步骤都执行一遍以确认活干得漂亮。另外,最后一步的时候,你应该执行make clean all而不是简单的执行make,来确保所有源文件重建。再次声明,现在的改动上述这些步骤是非必须的,但是会更安全更清晰。模块建好后,再拷贝到你的扩展目录下,替换旧版本。
这个时候你可以再次调用你的PHP解析器,运行一段简单的脚本来试试你刚刚添加的方法。事实上,为何不现在不试呢?我等着呢。。。
试完了?很好。如果你使用
PHP_FUNCTION(hello_bool){ RETURN_TRUE; }
注意这里没有使用括号哦。
你也许注意到上面这些代码样品我们都没有传0和1什来表示这些值是否需要被拷贝。这是因为没有额外的内存(变量容器之外——我们将在第2部分深入)需要被分配或释放,因为这些标量都很简单很小。
还有另外三种返回类型:
INI Settings
Zend 引擎提供了两个方式处理
现在我们想在php.ini中定义一个变量,"hello.greeting", 用来处理你在"hello_function()"函数里用来打招呼的变量。你需要在hello.c和php_hello.h中添另一些东西,并且
PHP_MINIT_FUNCTION(hello); PHP_MSHUTDOWN_FUNCTION(hello); PHP_FUNCTION(hello_world); PHP_FUNCTION(hello_long); PHP_FUNCTION(hello_double); PHP_FUNCTION(hello_bool); PHP_FUNCTION(hello_null);
现在到hello.c中用下面这串代码覆盖当前版本的
zend_module_entry hello_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_HELLO_WORLD_EXTNAME, hello_functions, PHP_MINIT(hello), PHP_MSHUTDOWN(hello), NULL, NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 PHP_HELLO_WORLD_VERSION, #endif STANDARD_MODULE_PROPERTIES }; PHP_INI_BEGIN() PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL) PHP_INI_END() PHP_MINIT_FUNCTION(hello) { REGISTER_INI_ENTRIES(); return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(hello) { UNREGISTER_INI_ENTRIES(); return SUCCESS; }
现在,你只需要在hello.c顶部剩下的
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h "#include "php_hello.h"
最后,我们修改
PHP_FUNCTION(hello_world) { RETURN_STRING(INI_STR("hello.greeting"), 1); }
注意,你复制了来自
首次修改的部分包含了两个你需要熟悉的方法:
在你的方法hellow_world()中使用"INI_STR()"来取回当前"hello.greeting"项的值作为一个字符串。那些其他的已有方法取值作为长整型,浮点型和布尔型,如下表所示,"ORIG"补充了其他方法,能提供从
Current Value | Original Value | Type |
signed long | ||
signed double | ||
전달
두 번째 매개변수는 초기값이며 일반적으로 숫자 여부에 관계없이 char* 문자열로 사용됩니다. 이는 주로 .ini 파일의 값이 실제로 텍스트로 기본 설정되어 있기 때문입니다. 텍스트 파일입니다. 스크립트에서
전달된 세 번째 매개변수는 액세스 모드 수정자입니다.
네 번째 매개변수를 건너뛰고 콜백 메서드에 전달할 값을 클릭하기만 하면 ini 구성이 수정될 때마다 호출됩니다.
전역 변수
通常,扩展需要在特定的请求里跟踪变量的值,使之独立于并发请求。在无线程的SAPI中这可能比较简单:只需要在源文件中声明一个全局变量,在需要时调用。然而麻烦在于PHP被设计运行于线程级的web服务器(如Apache 2 和 IIS),这就需要保证一个线程中的全局变量与其他线程中的分离。PHP通过使用TSRM(Thread Safe Resource Management)抽象层,大大地简化了这一操作,有时被称为ZTS(Zend Thread Safety)。实际 上,到现在你已经使用了一部分TSRM了,虽然你对其不甚了解。(不要急着去搜索,通过这一系列的进展,你会发现它无处不在。)
创建线程安全全局变量的第一步,跟创建其他全局变量一样,声明它。为了实现这个例子,你将声明一个以
#ifdef ZTS #include "TSRM.h" #endif ZEND_BEGIN_MODULE_GLOBALS(hello) login counter; ZEND_END_MODULE_GLOBALS(hello) #ifdef ZTS #define HELLO_G(v) TSRM(hello_globals_id, zend_hello_globals *, v) #else #define HELLO_G(v) (hello_globals.v) #endif
这次你还将用到
PHP_MINIT_FUNCTION(hello); PHP_MSHUTDOWN_FUNCTION(hello); PHP_RINIT_FUNCTION(hello);
现在 到hello.c中添加下面这段代码到include 模块后:
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "php_hello.h" ZEND_DECLARE_MODULE_GLOBALS(hello)
修改
zend_module_entry hello_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_HELLO_WORLD_EXTNAME, hello_functions, PHP_MINIT(hello), PHP_MSHUTDOWN(hello), PHP_RINIT(hello), NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 PHP_HELLO_WORLD_VERSION, #endif STANDARD_MODULE_PROPERTIES };
修改你的
static void php_hello_init_globals(zend_hello_globals *hello_globals) { } PHP_RINIT_FUNCTION(hello) { HELLO_G(counter) = 0; return SUCCESS; } PHP_MINIT_FUNCTION(hello) { ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL); REGISTER_INI_ENTRIES(); return SUCCESS; }
最后,修改你的
PHP_FUNCTION(hello_long) { HELLO_G(counter)++; RETURN_LONG(HELLO_G(counter)); }
在添加到php_hello.h的代码里,使用了一对宏
INI设置 vs 全局变量
如果你回观前文,一个在
ZEND_BEGIN_MODULE_GLOBAL(hello) login counter; zend_bool direction; ZEND_ENG_MODULE_GLOBALS(hello)
然后,通过修改
PHP_INI_BEGIN() PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL) STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals) PHP_INI_END()
现在在
static void php_hello_init_globals(zend_hello_globals *hello_globals) { hello_globals->direction = 1; }
最后,在hello_long()中使用这个配置的值来决定是自增还是自减:
PHP_FUNCTION(hello_long) { if (HELLO_G(direction)) { HELLO_G(counter)++; } else { HELLO_G(counter)--; } RETURN_LONG(HELLO_G(counter)); }
就是这样,我们在
完整性检查
到现在,我们的三个文件里的内容应该如下所列的(为了可阅读性,一些条目被移到了一起)。
config.m4
PHP_ARG_ENABLE(hello, whether to enable Hello World support, [ --enable-hello Enable Hello World support]) if test "$PHP_HELLO" = "yes"; then AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World]) PHP_NEW_EXTENSION(hello, hello.c, $ext_shared) fi
php_hello.h
#ifndef PHP_HELLO_H #define PHP_HELLO_H 1 #ifdef ZTS #include "TSRM.h" #endif ZEND_BEGIN_MODULE_GLOBALS(hello) long counter; zend_bool direction; ZEND_END_MODULE_GLOBALS(hello) #ifdef ZTS #define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v) #else #define HELLO_G(v) (hello_globals.v) #endif #define PHP_HELLO_WORLD_VERSION "1.0" #define PHP_HELLO_WORLD_EXTNAME "hello" PHP_MINIT_FUNCTION(hello); PHP_MSHUTDOWN_FUNCTION(hello); PHP_RINIT_FUNCTION(hello); PHP_FUNCTION(hello_world); PHP_FUNCTION(hello_long); PHP_FUNCTION(hello_double); PHP_FUNCTION(hello_bool); PHP_FUNCTION(hello_null); extern zend_module_entry hello_module_entry; #define phpext_hello_ptr &hello_module_entry #endif
hello.c
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "php_hello.h" ZEND_DECLARE_MODULE_GLOBALS(hello) static function_entry hello_functions[] = { PHP_FE(hello_world, NULL) PHP_FE(hello_long, NULL) PHP_FE(hello_double, NULL) PHP_FE(hello_bool, NULL) PHP_FE(hello_null, NULL) {NULL, NULL, NULL} }; zend_module_entry hello_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_HELLO_WORLD_EXTNAME, hello_functions, PHP_MINIT(hello), PHP_MSHUTDOWN(hello), PHP_RINIT(hello), NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 PHP_HELLO_WORLD_VERSION, #endif STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_HELLO ZEND_GET_MODULE(hello) #endif PHP_INI_BEGIN() PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL) STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals) PHP_INI_END() static void php_hello_init_globals(zend_hello_globals *hello_globals) { hello_globals->direction = 1; } PHP_RINIT_FUNCTION(hello) { HELLO_G(counter) = 0; return SUCCESS; } PHP_MINIT_FUNCTION(hello) { ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL); REGISTER_INI_ENTRIES(); return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(hello) { UNREGISTER_INI_ENTRIES(); return SUCCESS; } PHP_FUNCTION(hello_world) { RETURN_STRING("Hello World", 1); } PHP_FUNCTION(hello_long) { if (HELLO_G(direction)) { HELLO_G(counter)++; } else { HELLO_G(counter)--; } RETURN_LONG(HELLO_G(counter)); } PHP_FUNCTION(hello_double) { RETURN_DOUBLE(3.1415926535); } PHP_FUNCTION(hello_bool) { RETURN_BOOL(1); } PHP_FUNCTION(hello_null) { RETURN_NULL(); }
接下来做什么?
在这教程中我们开发了一个简单的PHP扩展,导出方法,返回值,声明了
下一章我们研究PHP变量的内核结构,以及变量如何存储、跟踪和在脚本环境中修改。当一个函数被调用时我们使用