Press "Enter" to skip to content

Writing a Module That Provides Objects for PHP to Use

    It's still that PHP module for developing security signatures. Today I'm wrapping it into a PHP CLASS. As before, there's very little material online about this, so I'd like to write up my experience — an article on how to create an object accessible from PHP inside an Extension Module. Sharing it with you all.
First, let's reach a few shared assumptions:
1. My module is called getCookie. It's written in C++, the source file is getCookie.cc, and now I want to add to it an object that PHP can use;
2. DOC_ROOT is your extension development directory. For example, if my extension is called getCookie, then the directory is under /home/y/share/php/getCookie;
3. The class I want to add is called XCSecure. Why XC? Because it's the initials of my name 🙂
4. My XCSecure does one thing: when you construct the object, you pass in a string, and it returns a string that is the MD5 of the cookie using that string as the key;
5. I assume you're already familiar with the basics of developing a PHP Module under Linux. If not, you can look up "深入PHP内核" (Exploring the PHP Kernel) online — it's translated quite well;
Now that we have some shared assumptions, let's begin:
    In PHP, modules and PHP scripts exchange data through zvals. To use our own C++ class, we must register our C++ class as a resource; then each time we NEW it, we register a resource instance and record its handle in the zval.
Next, let's do it step by step.
1. First you define your own C++ Class in the Module. As follows:
XCSecure.h

#ifndef _XCSECURE_H_
#define _XCSECURE_H_
#include
using namespace std;
class XCSecure{
private:
string _sec;
public:
XCSecure(char * s);
~XCSecure();
char * genSec();
};
#endif

Then define the class functions in XCSecure.cc, and also include XCSecure.h in getCookie.cc;
2. Modify config.m4,
PHP_NEW_EXTENSION(my_module, 'my_module.cc' 'XCSecure.cc', $ext_shared);
Note here that 'my_module.cc' and 'XCSecure.cc' are separated by a space;
Then run phpize under DOC_ROOT to generate configure for us;
Then run ./configure under DOC_ROOT to generate the Makefile for us;
At this point our preliminary work is done. You can now run make ; make install too, but it will only generate an empty module. Next we can fill it out and complete it.
3. Modify getCookie.cc to do some work to accommodate XCSecure:
First, we define a global zend_class_entry * ;
static zend_class_entry * php_xcsecure_ptr;
    Then we define a global handle (INT type). We want to register our C++ class as a resource in PHP. In PHP, a resource is a very broad concept — for example, the handle for a MySQL connection, the handle for an opened file, and so on.
static int de_xcsecure;
4. Define the member functions of our XCSecure:

function_entry php5_secure_method[] = {
PHP_FALIAS(XCSecure, XCSecure_new, NULL)
PHP_FALIAS(genSec, XCSecure_genSec, NULL)
{NULL, NULL, NULL}
};

Of course, you also need to define these functions yourself;
5. I choose to initialize my class during the PHP_MINIT phase;

PHP_MINIT_FUNCTION(getCookie){
zend_class_entry php5_secure_entry;
de_php5_secure = zend_register_list_destructors_ex(_de_php5_secure, NULL, "Signature Generate Type", module_number); //create a destructor for our C++ class objects and get its resource type handle
INIT_CLASS_ENTRY(php5_secure_entry, "XCSecure", php5_secure_method);
php5_secure_entry_ptr = zend_register_internal_class(&php5_secure_entry);//register our class, so it can be used in PHP scripts.
REGISTER_LONG_CONSTANT("XCSECURE_LOAD", 1, CONST_CS|CONST_PERSISTENT);
return SUCCESS;
}

    First we created a zend_class_entry. Going by what the variable name expresses, this is zend's handle type for operating on an object.
    Then we registered our object's cleanup function. This is because when our variable is unset in a PHP script, the zend kernel must know how to clean up our object and free the memory we occupy;
Then we initialized our class declaration,
INIT_CLASS_ENTRY(php5_secure_entry, "XCSecure", php5_secure_method);
Parameter 2 is our type name — it's what shows up when you var_dump in PHP, e.g.:
object(XCSecure)#1 (0) { }
Parameter 3 is the member functions of our class that we've already defined;
Then we registered our class with the zend kernel,
and assigned the pointer it returns to our global variable (I have other uses for this myself)
zend_register_internal_class(&php5_secure_entry);
At this point, our object is registered and can already be used in scripts;
$xcSecure = new XCSecure('laruence');
6. When you new our object in a PHP script, Zend automatically calls our constructor. Next, let's complete our constructor:

PHP_FUNCTION(XCSecure_new){
XCSecure * instance;
char* s;
int len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &s, &len) == FAILURE) {
RETURN_FALSE;
}
instance = new XCSecure(s);
zval * res_id;
MAKE_STD_ZVAL(res_id);
int id = ZEND_REGISTER_RESOURCE(NULL, instance, le_xc_secure);
ZVAL_LONG(res_id, id);
zend_hash_update(Z_OBJPROP_P(getThis()), Hash_Key, sizeof(Hash_Key), &res_id, sizeof(res_id), NULL);
}

    First we get the argument from the user's new XCSecure($para) — it's a string — then we new a C++ object and register it as a resource instance. Note the last parameter of ZEND_REGISTER_RESOURCE: it's the resource type handle returned back when we defined our resource destructor.
Then we save the resource handle (id) returned after registering the resource instance into a Zval.
    When a user creates an instance of our class in PHP, Zend calls a zend_objects_new to create a standard Zend object and stores it in the zend_objects_store, which is an array of Buckets. Then it stores the index of our object in that array (called a handler in Zend) into a zend_object_value structure, then stores that zend_object_value into a zval structure. After that, zend calls our constructor, with the this pointer pointing at this zval. So in our constructor we can get this zval through getThis(); of course, we can also directly use this_ptr;
    After registering the resource, the constructor stores the resource handle it obtained (which is actually also an index into a list) into the properties attribute of the object pointed to by this (this is a hash table). Later, when the user calls a class function through our object, we can get this object through this, and then get our C++ object through the resource handle in the object's properties. For example:

PHP_FUNCTION(XCSecure_genSec){
zval ** rsc;
XCSecure * secure;
if(zend_hash_find(Z_OBJPROP_P(getThis()), Hash_Key, sizeof(Hash_Key), (void **)&rsc) == SUCCESS){
secure = (XCSecure *)zend_fetch_resource(NULL, Z_LVAL_PP(rsc), NULL, NULL, 1, le_xc_secure);
}
ZVAL_STRING(return_value, secure->genSec(), 1);
}

    First we get the this pointer through getThis(), then use the Z_OBJPROP_P macro to get the properties attribute of the object (an object) that this points to, then use zend_hash_find to get the resource handle saved during construction.
    After that, we use zend_fetch_resource to get the C++ object created by the constructor, and then you can use this C++ object freely, just as you would in C++

7. Finally, I defined an integer constant, which also has other uses; it can now be accessed in scripts too. The CONST_CS in it indicates that our constant is case-sensitive, and CONST_PERSISTENT is self-explanatory..
Well, by now our object has been added to our module. You can now simply make ; make install and then test it 🙂

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.