21.9 Creating a PHP Module


We've discussed some different features of PHP modules ”now it's time to cover the creation of a custom PHP module.

Technique

In this recipe, we discuss the curl module. For more information about curl , visit http://curl.haxx.se/.

The format of my explanation is different than that of the other recipes. Here, I will embed my explanations (in italic text) at critical points, rather than explaining it all in the end.

 /*    +----------------------------------------------------------------------+     PHP version 4.0                                                          +----------------------------------------------------------------------+     Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                       +----------------------------------------------------------------------+     This source file is subject to version 2.02 of the PHP license,           that is bundled with this package in the file LICENSE, and is             available at through the world-wide-web at                                http://www.php.net/license/2_02.txt.                                      If you did not receive a copy of the PHP license and are unable to        obtain it through the world-wide-web, please send a note to               license@php.net so we can mail you a copy immediately.                   +----------------------------------------------------------------------+     Author: Sterling Hughes <sterling@php.net>                               +----------------------------------------------------------------------+ */ 

I wrote this for PHP, therefore the preceding is the standard copyright. This is not required unless you are contributing your extension back to PHP.

 #include "php.h" #include "php_curl.h" 

Include all the necessary headers and API definitions.

 #if HAVE_CURL 

Test whether curl support has been enabled. This is defined by curl's config.m4 , which is discussed later.

 /* Standard Includes */ #include <stdio.h> #include <string.h> #include <sys/stat.h> #if HAVE_UNISTD_H #include <unistd.h> #endif #ifdef PHP_WIN32 #include <winsock.h> #include <sys/types.h> #define fstat(handle, buff) _fstat(handle, buff) #define stat _stat #endif 

Allow our module to compile on more than just one system, adding support for both UNIX and Windows NT.

 /* CURL Includes */ #include <curl/curl.h> #include <curl/easy.h> /* PHP Includes */ #include "ext/standard/info.h" ZEND_DECLARE_MODULE_GLOBALS(extname) static void _php_curl_close(php_curl *); #define SAVE_CURL_ERROR(__handle, __err) \     __handle->cerrno = (int)__err; 

_php_curl_close() is the list destructor for the curl handle (of type php_curl * ). The SAVE_CURL_ERROR() macro saves an error for later retrieval by the curl_errno() function.

 #ifdef PHP_WIN32 /* {{{ win32_cleanup()    Clean-up allocated socket data on win32 systems */ static void win32_cleanup() {     WSACleanup(); } /* }}} */ /* {{{ win32_init()    Initialize WSA stuff on Win32 systems */ static CURLcode win32_init() {     WORD wVersionRequested;     WSADATA wsaData;     int err;     wVersionRequested = MAKEWORD(1, 1);     err = WSAStartup(wVersionRequested, &wsaData);     if (err != 0) return CURLE_FAILED_INIT;     if (LOBYTE(wsaData.wVersion) != 1          HIBYTE(wsaData.wVersion) != 1) {         WSACleanup();         return CURLE_FAILED_INIT;     }     return CURLE_OK; } /* }}} */ #else static CURLcode win32_init(void) { return CURLE_OK; } #define win32_cleanup() #endif 

This is more "Win32-izing." The preceding code is cut and pasted from the source code of curl itself.

 function_entry curl_functions[] = {     PHP_FE(curl_init,     NULL)     PHP_FE(curl_version,  NULL)     PHP_FE(curl_setopt,   NULL)     PHP_FE(curl_exec,     NULL)     PHP_FE(curl_error,    NULL)     PHP_FE(curl_errno,    NULL)     PHP_FE(curl_close,    NULL)     {NULL, NULL, NULL} }; 

The preceding snippet of code declares all the functions that will be in the bzip2 module. The PHP_FE macro has the following syntax:

 PHP_FE(functionName,    argument_types) 

In general, the argument_types parameter should be NULL unless you want to accept references to variables . In that case

 unsigned char first_arg_force_ref[] = {BYREF_FORCE, BYREF_NONE, ..}; PHP_FE(functionName,    first_arg_force_ref) 

forces the first argument to be passed by reference, and

 unsigned char second_arg_force_ref[] = {BYREF_NONE, BYREF_FORCE, ..}; PHP_FE(functionName,    second_arg_force_ref) 

causes the second argument to be a reference, and so on.

 zend_module_entry curl_module_entry = {     "curl",     curl_functions,     PHP_MINIT(curl),     PHP_MSHUTDOWN(curl),     NULL,     NULL,     PHP_MINFO(curl),     STANDARD_MODULE_PROPERTIES }; 

The preceding code creates an entry for the module and registers it with zend. The type zend_module_entry is defined like so:

 typedef struct _zend_module_entry zend_module_entry struct _zend_module_entry {     char *name;     zend_function_entry *functions,     int (*module_startup_func)(INIT_FUNC_ARGS);     int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);     int (*request_startup_func)(INIT_FUNC_ARGS);     void (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);     void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);     int (*global_startup_func)(void);     int (*global_shutdown_func)(void);     int globals_id;     int module_started;     unsigned char type;     void *handle;     unsigned char zend_debug;     unsigned char zts;     unsigned int zend_api; } 

However, the only entries we need to be concerned about are the entries from char *name to (void *info_func) . The rest of the entries are taken care of by STANDARD_MODULE_PROPERTIES . For more information about the definition of STANDARD_MODULE_PROPERTIES , see modules.h in the Zend directory.

The first entry in the structure is name , which is a character string with the name of the module (in this case, bzip ). The second entry in the structure is the name of the function entry. This is what we defined as a function_entry earlier, and it contains all the functions to be exported to PHP.

The third entry in the zend_module_entry structure is the module startup function argument, which defines the function that is to be called when this module is first loaded. The fourth entry is the module shutdown function, which is called when the module is unloaded or execution is about to stop.

The fifth entry in the zend_module_entry structure is the request startup function, which is called when PHP starts processing a request. The sixth entry is the request shutdown function, which is called at the end of request processing. The final entry is the more information function. This is the name of the PHP_MINFO() function that is called by phpinfo() , and prints information about the module to the user .

 #ifdef COMPILE_DL_CURL ZEND_GET_MODULE (curl) #endif 

Return the special dynamic loading “specific code, if the user has chosen to compile this extension as a dynamic loading extension.

 PHP_MINFO_FUNCTION(curl) {     php_info_print_table_start();     php_info_print_table_row(2, "CURL support", "enabled");     php_info_print_table_row(2, "CURL Information", curl_version());     php_info_print_table_end(); } PHP_MINIT_FUNCTION(curl) {     CURLLS_FETCH();     CURLG(le_curl) = register_list_destructors(_php_curl_close, NULL);     /* Constants for curl_setopt() */     REGISTER_LONG_CONSTANT("CURLOPT_PORT", CURLOPT_PORT, CONST_CS                             CONST_PERSISTENT);     REGISTER_LONG_CONSTANT("CURLOPT_FILE", CURLOPT_FILE, CONST_CS                             CONST_PERSISTENT);     REGISTER_LONG_CONSTANT("CURLOPT_INFILE", CURLOPT_INFILE, CONST_CS                             CONST_PERSISTENT);     /* *  The other 60-70 or so constants have been ommited for *  brevity.  If you want to see them you can always check out *  the source of curl in php4/ext/curl/curl.c      */     if (win32_init() != CURLE_OK) {         return FAILURE;     }     return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(curl) {     win32_cleanup(); } /* {{{ proto string curl_version(void)    Return the CURL version string. */ PHP_FUNCTION (curl_version) {     RETURN_STRING(curl_version(), 1); } /* }}} */ /* {{{ proto int curl_init([string url])    Initialize a CURL session */ PHP_FUNCTION(curl_init) {     zval **url;     php_curl *curl_handle = NULL;     int argc = ZEND_NUM_ARGS();     CURLLS_FETCH(); 

CURLLS_FETCH() fetches the module globals stored in our global structure. This allows our module to be threadsafe.

 if (argc < 0  argc > 1      zend_get_parameters_ex(argc, &url) == FAILURE) {     WRONG_PARAM_COUNT; } curl_handle = (php_curl *)emalloc(sizeof(php_curl)); if (!curl_handle) {     php_error(E_WARNING, "Couldn't allocate    a CURL Handle");     RETURN_FALSE; } memset(curl_handle, 0, sizeof(php_curl)); curl_handle->cp = curl_easy_init(); if (!curl_handle->cp) {     php_error(E_ERROR, "Cannot initialize CURL Handle");     RETURN_FALSE; } if (argc > 0) {     char *urlstr;     convert_to_string_ex(url);              urlstr = estrndup(Z_STRVAL_PP(url), Z_STRLEN_PP(url));     curl_easy_setopt(curl_handle->cp, CURLOPT_URL, urlstr); 

You should always duplicate strings unless the prototype for the function you are calling is a const char *.

 } curl_easy_setopt(curl_handle->cp, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(curl_handle->cp, CURLOPT_VERBOSE,    0); curl_easy_setopt(curl_handle->cp, CURLOPT_ERRORBUFFER, curl_handle->error); curl_handle->output_file = 0; curl_handle->php_stdout  = 1; ZEND_REGISTER_RESOURCE(return_value, curl_handle, CURLG(le_curl)); 

Register the resource and return the identifier. The CURLG() macro is for accessing members of our global structure.

 } /* }}} */ /* {{{ proto bool curl_setopt(int ch, string option, mixed value)    Set an option for a CURL transfer */ PHP_FUNCTION(curl_setopt) {     zval **curl_id,          **curl_option,          **curl_value;     php_curl *curl_handle;     CURLcode ret;     int option;     CURLLS_FETCH();     if (ZEND_NUM_ARGS() != 3          zend_get_parameters_ex(3, &curl_id, &curl_option, &curl_value)         == FAILURE) {         WRONG_PARAM_COUNT;     }     ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL    Handle",                         CURLG(le_curl)); 

Fetch the resource of type php_curl * into curl_handle with a resource identifier of curl_id and a list destructor having the identifier of CURLG(le_curl) .

 convert_to_long_ex(curl_option); option = Z_LVAL_PP(curl_option); switch (option) {     case CURLOPT_INFILESIZE:     case CURLOPT_VERBOSE:     case CURLOPT_HEADER:     case CURLOPT_NOPROGRESS:     case CURLOPT_NOBODY:     case CURLOPT_FAILONERROR:     case CURLOPT_UPLOAD:     case CURLOPT_POST:     case CURLOPT_FTPLISTONLY:     case CURLOPT_FTPAPPEND:     case CURLOPT_NETRC:     case CURLOPT_FOLLOWLOCATION:     case CURLOPT_PUT:     case CURLOPT_MUTE:     case CURLOPT_TIMEOUT:     case CURLOPT_LOW_SPEED_LIMIT:     case CURLOPT_SSLVERSION:     case CURLOPT_LOW_SPEED_TIME:     case CURLOPT_RESUME_FROM:     case CURLOPT_TIMEVALUE:     case CURLOPT_TIMECONDITION:     case CURLOPT_TRANSFERTEXT:         convert_to_long_ex(curl_value);         ret = curl_easy_setopt(curl_handle->cp, option,               Z_LVAL_PP(curl_value));         break;          case CURLOPT_URL:     case CURLOPT_PROXY:     case CURLOPT_USERPWD:     case CURLOPT_PROXYUSERPWD:     case CURLOPT_RANGE:     case CURLOPT_CUSTOMREQUEST:     case CURLOPT_USERAGENT:     case CURLOPT_FTPPORT:     case CURLOPT_COOKIE:     case CURLOPT_SSLCERT:     case CURLOPT_SSLCERTPASSWD:     case CURLOPT_COOKIEFILE:         {             char *copystr = NULL;                             convert_to_string_ex(curl_value);             copystr = estrndup(Z_STRVAL_PP(curl_value),                                Z_STRLEN_PP(curl_value));             ret = curl_easy_setopt(curl_handle->cp, option, copystr);         }         break;     case CURLOPT_FILE:     case CURLOPT_INFILE:     case CURLOPT_WRITEHEADER:     case CURLOPT_STDERR:         {             FILE *fp;             ZEND_FETCH_RESOURCE(fp, FILE *, curl_value, -1, "File-Handle",                                 php_file_le_fopen());             ret = curl_easy_setopt(curl_handle->cp, option, fp); 

In the preceding, we fetch a file pointer resource into fp . The php_file_le_fopen() function is an API function that allows PHP extensions to access file pointer ( FILE * ) resources.

 if (option & CURLOPT_FILE) {                     curl_handle->output_file = Z_LVAL_PP(curl_value);                     curl_handle->php_stdout  = 0;                 }             }             break;         case CURLOPT_RETURNTRANSFER:             convert_to_long_ex(curl_value);             curl_handle->return_transfer = Z_LVAL_PP(curl_value);             curl_handle->php_stdout      = !Z_LVAL_PP(curl_value);             break;         case CURLOPT_POSTFIELDS:             if (Z_TYPE_PP(curl_value) == IS_ARRAY                  Z_TYPE_PP(curl_value) == IS_OBJECT) {                 zval **current;                 HashTable *u_post = HASH_OF(*curl_value);                 struct HttpPost *first = NULL,                                 *last  = NULL;                 for (zend_hash_internal_pointer_reset(u_post);                      zend_hash_get_current_data(u_post, (void **)&current)                                                 == SUCCESS;                      zend_hash_move_forward(u_post)) {                     char *string_key = NULL,                          *str    = NULL,                          *val_str    = NULL;                     ulong num_key;                     SEPARATE_ZVAL(current);                     convert_to_string_ex(current);                    if (zend_hash_get_current_key(u_post, &string_key, &num_key)                        == HASH_KEY_IS_LONG) {                         php_error(E_WARNING, "Array passed to %s() must be an                         associative array", get_active_function_name());                         RETURN_FALSE;                     }                     val_str = estrndup(Z_STRVAL_PP(current),                                        Z_STRLEN_PP(current));                    str = emalloc(strlen(string_key) + strlen(val_str) + 1 + 2);                     if (!str) {                         php_error(E_WARNING, "Couldn't allocate a post field                         from %s()", get_active_function_name());                         RETURN_FALSE;                     }                     sprintf(str, "%s=%s", string_key, val_str);                     ret = curl_formparse(str, &first, &last);                     efree(string_key);                     efree(val_str);                 }                 if (ret != CURLE_OK) {                     SAVE_CURL_ERROR(curl_handle, ret);                     RETURN_FALSE;                 }               ret = curl_easy_setopt(curl_handle->cp, CURLOPT_HTTPPOST, first);             } else {                 char *post_str = NULL;                 convert_to_string_ex(curl_value);                 post_str = estrndup(Z_STRVAL_PP(curl_value),                                     Z_STRLEN_PP(curl_value));                 ret = curl_easy_setopt(curl_handle->cp, CURLOPT_POSTFIELDS,                                        post_str);                 if (ret != CURLE_OK) {                     SAVE_CURL_ERROR(curl_handle, ret);                     RETURN_FALSE;                 }                 ret = curl_easy_setopt(curl_handle->cp, CURLOPT_POSTFIELDSIZE,                                        Z_STRLEN_PP(curl_value));                 break;             }             break;         case CURLOPT_HTTPHEADER:             {                 zval **current;                 HashTable *headers = HASH_OF(*curl_value);                 struct curl_slist *header = NULL;                 header = (struct curl_slist *)                 emalloc(sizeof(struct curl_slist));                 if (!header) {                     php_error(E_WARNING,                     "Couldn't allocate    header list from %s()",                     get_active_function_name());                     RETURN_FALSE;                 }                 memset(header, 0, sizeof(struct curl_slist));                 for (zend_hash_internal_pointer_reset(headers);                      zend_hash_get_current_data(headers, (void **)&current)                                                 == SUCCESS;                      zend_hash_move_forward(headers)) {                     char *indiv_header = NULL;                     SEPARATE_ZVAL(current);                     convert_to_string_ex(current);                     indiv_header = estrndup(Z_STRVAL_PP(current),                                             Z_STRLEN_PP(current));                     header = curl_slist_append(header, indiv_header);                     if (!header) {                           php_error(E_WARNING, "Couldn't build header from                                     %s()", get_active_function_name());                         RETURN_FALSE;                     }                 }                                 ret = curl_easy_setopt(curl_handle->cp, CURLOPT_HTTPHEADER,                                        header);             }             break;     }         if (ret != CURLE_OK) {         SAVE_CURL_ERROR(curl_handle, ret);         RETURN_FALSE;     } else {         RETURN_TRUE;     } } /* }}} */ /* {{{ proto bool curl_exec(int ch)    Perform a CURL session */ PHP_FUNCTION(curl_exec) {     zval **curl_id;     php_curl *curl_handle;     CURLcode ret;     FILE *fp;     char buf[4096];     int b;     unsigned long pos = 0;     CURLLS_FETCH();     if (ZEND_NUM_ARGS() != 1          zend_get_parameters_ex(1, &curl_id) == FAILURE) {         WRONG_PARAM_COUNT;     }     ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle",                          CURLG(le_curl));     if ((curl_handle->return_transfer &&         !curl_handle->output_file)  curl_handle->php_stdout) {         if ((fp = tmpfile()) == NULL) {             php_error(E_WARNING, "Cannot initialize temporary file to save                       output from %s()",             get_active_function_name());             RETURN_FALSE;         }         curl_easy_setopt(curl_handle->cp, CURLOPT_FILE, fp);     } else if (curl_handle->return_transfer &&             curl_handle->output_file) {               ZEND_FETCH_RESOURCE(fp, FILE *, (zval **)NULL, curl_handle->output_file,                                "File-Handle", php_file_le_fopen());          }     ret = curl_easy_perform(curl_handle->cp);     if ((!curl_handle->return_transfer && !curl_handle->php_stdout)          (ret != CURLE_OK)) {         if (ret != CURLE_OK) {             SAVE_CURL_ERROR(curl_handle, ret);             RETURN_FALSE;         } else {             RETURN_TRUE;         }     }     fseek(fp, 0, SEEK_SET);     if (curl_handle->php_stdout) {         while ((b = fread(buf, 1, sizeof(buf), fp)) > 0) {             php_write(buf, b);         }     } else {         char *ret_data;         struct stat stat_sb;         if (fstat(fileno(fp), &stat_sb)) {             RETURN_FALSE;         }         ret_data = emalloc(stat_sb.st_size+1);         while ((b = fread(buf, 1, sizeof(buf), fp)) > 0) {             memcpy(ret_data + pos, buf, b);             pos += b;         }         ret_data[stat_sb.st_size - 1] = ' 
 if (option & CURLOPT_FILE) { curl_handle->output_file = Z_LVAL_PP(curl_value); curl_handle->php_stdout = 0; } } break; case CURLOPT_RETURNTRANSFER: convert_to_long_ex(curl_value); curl_handle->return_transfer = Z_LVAL_PP(curl_value); curl_handle->php_stdout = !Z_LVAL_PP(curl_value); break; case CURLOPT_POSTFIELDS: if (Z_TYPE_PP(curl_value) == IS_ARRAY  Z_TYPE_PP(curl_value) == IS_OBJECT) { zval **current; HashTable *u_post = HASH_OF(*curl_value); struct HttpPost *first = NULL, *last = NULL; for (zend_hash_internal_pointer_reset(u_post); zend_hash_get_current_data(u_post, (void **)&current) == SUCCESS; zend_hash_move_forward(u_post)) { char *string_key = NULL, *str = NULL, *val_str = NULL; ulong num_key; SEPARATE_ZVAL(current); convert_to_string_ex(current); if (zend_hash_get_current_key(u_post, &string_key, &num_key) == HASH_KEY_IS_LONG) { php_error(E_WARNING, "Array passed to %s() must be an associative array", get_active_function_name()); RETURN_FALSE; } val_str = estrndup(Z_STRVAL_PP(current), Z_STRLEN_PP(current)); str = emalloc(strlen(string_key) + strlen(val_str) + 1 + 2); if (!str) { php_error(E_WARNING, "Couldn't allocate a post field from %s()", get_active_function_name()); RETURN_FALSE; } sprintf(str, "%s=%s", string_key, val_str); ret = curl_formparse(str, &first, &last); efree(string_key); efree(val_str); } if (ret != CURLE_OK) { SAVE_CURL_ERROR(curl_handle, ret); RETURN_FALSE; } ret = curl_easy_setopt(curl_handle->cp, CURLOPT_HTTPPOST, first); } else { char *post_str = NULL; convert_to_string_ex(curl_value); post_str = estrndup(Z_STRVAL_PP(curl_value), Z_STRLEN_PP(curl_value)); ret = curl_easy_setopt(curl_handle->cp, CURLOPT_POSTFIELDS, post_str); if (ret != CURLE_OK) { SAVE_CURL_ERROR(curl_handle, ret); RETURN_FALSE; } ret = curl_easy_setopt(curl_handle->cp, CURLOPT_POSTFIELDSIZE, Z_STRLEN_PP(curl_value)); break; } break; case CURLOPT_HTTPHEADER: { zval **current; HashTable *headers = HASH_OF(*curl_value); struct curl_slist *header = NULL; header = (struct curl_slist *) emalloc(sizeof(struct curl_slist)); if (!header) { php_error(E_WARNING, "Couldn't allocate header list from %s()", get_active_function_name()); RETURN_FALSE; } memset(header, 0, sizeof(struct curl_slist)); for (zend_hash_internal_pointer_reset(headers); zend_hash_get_current_data(headers, (void **)&current) == SUCCESS; zend_hash_move_forward(headers)) { char *indiv_header = NULL; SEPARATE_ZVAL(current); convert_to_string_ex(current); indiv_header = estrndup(Z_STRVAL_PP(current), Z_STRLEN_PP(current)); header = curl_slist_append(header, indiv_header); if (!header) { php_error(E_WARNING, "Couldn't build header from %s()", get_active_function_name()); RETURN_FALSE; } } ret = curl_easy_setopt(curl_handle->cp, CURLOPT_HTTPHEADER, header); } break; } if (ret != CURLE_OK) { SAVE_CURL_ERROR(curl_handle, ret); RETURN_FALSE; } else { RETURN_TRUE; } } /* }}} */ /* {{{ proto bool curl_exec(int ch) Perform a CURL session */ PHP_FUNCTION(curl_exec) { zval **curl_id; php_curl *curl_handle; CURLcode ret; FILE *fp; char buf[4096]; int b; unsigned long pos = 0; CURLLS_FETCH(); if (ZEND_NUM_ARGS() != 1  zend_get_parameters_ex(1, &curl_id) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle", CURLG(le_curl)); if ((curl_handle->return_transfer && !curl_handle->output_file)  curl_handle->php_stdout) { if ((fp = tmpfile ()) == NULL) { php_error(E_WARNING, "Cannot initialize temporary file to save output from %s()", get_active_function_name()); RETURN_FALSE; } curl_easy_setopt(curl_handle->cp, CURLOPT_FILE, fp); } else if (curl_handle->return_transfer && curl_handle->output_file) { ZEND_FETCH_RESOURCE(fp, FILE *, (zval **)NULL, curl_handle->output_file, "File-Handle", php_file_le_fopen()); } ret = curl_easy_perform(curl_handle->cp); if ((!curl_handle->return_transfer && !curl_handle->php_stdout)  (ret != CURLE_OK)) { if (ret != CURLE_OK) { SAVE_CURL_ERROR(curl_handle, ret); RETURN_FALSE; } else { RETURN_TRUE; } } fseek(fp, 0, SEEK_SET); if (curl_handle->php_stdout) { while ((b = fread(buf, 1, sizeof(buf), fp)) > 0) { php_write(buf, b); } } else { char *ret_data; struct stat stat_sb; if (fstat(fileno(fp), &stat_sb)) { RETURN_FALSE; } ret_data = emalloc(stat_sb.st_size+1); while ((b = fread(buf, 1, sizeof(buf), fp)) > 0) { memcpy (ret_data + pos, buf, b); pos += b; } ret_data[stat_sb.st_size - 1] = '\0'; RETURN_STRINGL(ret_data, stat_sb.st_size, 0); } } /* }}} */ /* {{{ proto string curl_error(int ch) Return a string contain the last error for the current session */ PHP_FUNCTION(curl_error) { zval **curl_id; php_curl *curl_handle; CURLLS_FETCH(); if (ZEND_NUM_ARGS() != 1  zend_get_parameters_ex(1, &curl_id) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle", CURLG(le_curl)); RETURN_STRING(curl_handle->error, 1); } /* }}} */ /* {{{ proto int curl_errno(int ch) Return an integer containing the last error number */ PHP_FUNCTION(curl_errno) { zval **curl_id; php_curl *curl_handle; CURLLS_FETCH(); if (ZEND_NUM_ARGS() != 1  zend_get_parameters_ex(1, &curl_id) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle", CURLG(le_curl)); RETURN_LONG(curl_handle->cerrno); } /* }}} */ /* {{{ proto void curl_close(int ch) Close a CURL session */ PHP_FUNCTION (curl_close) { zval **curl_id; php_curl *curl_handle; CURLLS_FETCH(); if (ZEND_NUM_ARGS() != 1  zend_get_parameters_ex(1, &curl_id) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle", CURLG(le_curl)); zend_list_delete(Z_LVAL_PP(curl_id)); } /* }}} */ /* {{{ _php_curl_close() List destructor for curl handles */ static void _php_curl_close(php_curl *curl_handle) { curl_easy_cleanup(curl_handle->cp); efree(curl_handle); } /* }}} */ #endif FILE: php_bzip.h /* +----------------------------------------------------------------------+  PHP version 4.0  +----------------------------------------------------------------------+  Copyright (c) 1997, 1998, 1999, 2000 The PHP Group  +----------------------------------------------------------------------+  This source file is subject to version 2.02 of the PHP license,   that is bundled with this package in the file LICENSE, and is   available at through the world-wide-web at   http://www.php.net/license/2_02.txt.   If you did not receive a copy of the PHP license and are unable to   obtain it through the world-wide-web, please send a note to   license@php.net so we can mail you a copy immediately.  +----------------------------------------------------------------------+  Author: Sterling Hughes <sterling@php.net>  +----------------------------------------------------------------------+ */ # ifndef _PHP_CURL_H #define _PHP_CURL_H 
'; RETURN_STRINGL(ret_data, stat_sb.st_size, 0); } } /* }}} */ /* {{{ proto string curl_error(int ch) Return a string contain the last error for the current session */ PHP_FUNCTION(curl_error) { zval **curl_id; php_curl *curl_handle; CURLLS_FETCH(); if (ZEND_NUM_ARGS() != 1 zend_get_parameters_ex(1, &curl_id) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle", CURLG(le_curl)); RETURN_STRING(curl_handle->error, 1); } /* }}} */ /* {{{ proto int curl_errno(int ch) Return an integer containing the last error number */ PHP_FUNCTION(curl_errno) { zval **curl_id; php_curl *curl_handle; CURLLS_FETCH(); if (ZEND_NUM_ARGS() != 1 zend_get_parameters_ex(1, &curl_id) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle", CURLG(le_curl)); RETURN_LONG(curl_handle->cerrno); } /* }}} */ /* {{{ proto void curl_close(int ch) Close a CURL session */ PHP_FUNCTION (curl_close) { zval **curl_id; php_curl *curl_handle; CURLLS_FETCH(); if (ZEND_NUM_ARGS() != 1 zend_get_parameters_ex(1, &curl_id) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(curl_handle, php_curl *, curl_id, -1, "CURL Handle", CURLG(le_curl)); zend_list_delete(Z_LVAL_PP(curl_id)); } /* }}} */ /* {{{ _php_curl_close() List destructor for curl handles */ static void _php_curl_close(php_curl *curl_handle) { curl_easy_cleanup(curl_handle->cp); efree(curl_handle); } /* }}} */ #endif FILE: php_bzip.h /* +----------------------------------------------------------------------+ PHP version 4.0 +----------------------------------------------------------------------+ Copyright (c) 1997, 1998, 1999, 2000 The PHP Group +----------------------------------------------------------------------+ This source file is subject to version 2.02 of the PHP license, that is bundled with this package in the file LICENSE, and is available at through the world-wide-web at http://www.php.net/license/2_02.txt. If you did not receive a copy of the PHP license and are unable to obtain it through the world-wide-web, please send a note to license@php.net so we can mail you a copy immediately. +----------------------------------------------------------------------+ Author: Sterling Hughes <sterling@php.net> +----------------------------------------------------------------------+ */ #ifndef _PHP_CURL_H #define _PHP_CURL_H

Make sure that this file is not included twice.

 #ifdef COMPILE_DL_CURL #undef HAVE_CURL #define HAVE_CURL 1 #endif #if HAVE_CURL #include <curl/curl.h> extern zend_module_entry curl_module_entry; #define curl_module_ptr &curl_module_entry #define CURLOPT_RETURNTRANSFER 19913 PHP_MINIT_FUNCTION(curl); PHP_MSHUTDOWN_FUNCTION(curl); PHP_MINFO_FUNCTION(curl); PHP_FUNCTION(curl_version); PHP_FUNCTION(curl_init); PHP_FUNCTION(curl_setopt); PHP_FUNCTION(curl_exec); PHP_FUNCTION(curl_error); PHP_FUNCTION(curl_errno); PHP_FUNCTION(curl_close); typedef struct {     int return_transfer;     int output_file;     int php_stdout;     int cerrno;     char error[CURL_ERROR_SIZE+1];     CURL *cp; } php_curl; 

A php_curl structure (a php_curl type, to be exact) is the structure we store in Zend's resource list (actually, it's a pointer to a php_curl structure).

 typedef struct {     int le_curl; } php_curl_globals; #ifdef ZTS #define CURLG(v) (curl_globals->v) #define CURLLS_FETCH() php_curl_globals *curl_globals = ts_resource(curl_globals_id) #else #define CURLG(v) (curl_globals.v) #define CURLLS_FETCH() #endif 

The preceding is the thread safety code; it defines the access of this module's global variables (this is used to access le_curl ).

 #else #define curl_module_ptr NULL #endif /* HAVE_CURL */ #define phpext_curl_ptr curl_module_ptr #endif  /* _PHP_CURL_H */ 

Comments

The preceding is the full source of the curl module, minus the configuration file (config.m4) and the makefile (Makefile.in), which are discussed in full during the next recipe.

Although the source is quite massive, try to break it into smaller pieces and you'll see that the essential elements are quite simple. You have a function entry, a module entry, and then all the surrounding functions and code for your module. If you know the Zend API, you can simply create your basic module.

In PHP 4, you can use the ext_skel tool to generate an extension skeleton, meaning that it has all the essentials: macros and definitions for thread safety, a function entry, a fully filled-out module entry, and other important definitions.

To use ext_skel (only on *nix-based systems), go to the ext directory of your PHP distribution and run ext_skel :

 % ./ext_skel --extname=yourmod 

ext_skel will create the necessary directories and files, with the necessary elements.

When you run the ext_skel script, it will generate extra "help" comments, explaining the different parts of the module. If you want to have ext_skel omit this extra stuff, add the --no-help option.

 % ./ext_skel extname=yourmod --no-help 


PHP Developer's Cookbook
PHP Developers Cookbook (2nd Edition)
ISBN: 0672323257
EAN: 2147483647
Year: 2000
Pages: 351

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net