Section 6. Express C shadow to PHP
table of Contents
Type Myclass
Macro, function and other
Encapsulation code
The class of PHP 5 supports many new features. Such as: Protected, Public, Private, Exceptions, Interfaces, and more. In this simple introduction, we only do the most basic thing: make PHPs can be imaged into C classes. This allows you to use your class in PHP, and then things will become very simple. Before you look at the introduction below, you can refer to the code of SQLITE, Simplexml, and CryptOpp-PHP modules.
Here you will introduce it. We have to use a C class as an example, just called Myclass. In PHP terminology, it is called a resource (resource). PHP often uses this type of things, such as database connection is resource, which may also be a Struct (structure) pointing to actual resource. In C , the class is actually a synonym of Struct (Struct defaults to public, Classe defaults to private - only this difference).
In a structured interface, we will use the following PHP code to use resources:
$ m = myclass_new ();
Myclass_set_string ($ m, 'Hello World!');
Var_dump (myclass_get_string ($ m));
?>
In an object-oriented interface, PHP Resources can be used, but it has been packaged in a PHP object, such as:
$ m = new myclass;
$ m-> setstring ('Hello World!');
VAR_DUMP ($ m-> getString ());
?>
We don't need to care about what is done by the actual code of the package. When we have a C class called MyClass. We can use this C class as a resources and encapsulate the methods in the C class into a PHP function. Then we can also package it into a PHP object such that it can be used like a general C .
When reading this article, remember that our purpose is to encapsulate the C class into a structured function or objective class that PHP can use. Maybe something in this will make you confused, but you will slowly understand it after reading it. There will be a lot of macro definition in the middle, but when you understand, you will feel that everything is very clear.
Class 1 Myclass
First we need a class. Low is a simple class with only one private property and several public methods.
Here is the statement of this class Myclass.h:
#ifndef __myclass_h__
#define __myclass_h__
#include
Using namespace std;
Class myclass {
Private:
String itsstring;
PUBLIC:
"String s =" default ");
~ Myclass ();
String getstring () const;
Bool setString (const string s);
}
#ENDIF
The following is defined code myclass.cpp:
#include "myclass.h"
Myclass :: MyClass (String S)
{
ITSString = S;
}
Myclass :: ~ myclass ()
{
}
String myclass :: getString () const {
Return itString;
}
Bool myclass :: setstring (const string s)
{
ITSString = S;
Return True;
}
This is just a class as an example.
Then we want the build system to know and compile these files. Do the config.m4 files:
PHP_NEW_EXTENSION (php5cpp, php5cpp.cpp, $ ext_shared)
Becomes ...
PHP_NEW_EXTENSION (php5cpp, php5cpp.cpp myclass.cpp, $ ext_shared)
Add #include "myclass.h" in a php5cpp.cpp file:
Extern "C" {
#include "php.h"
#include "php_ini.h"
#include "ext / standard / info.h"
}
#include "php_php5cpp.h"
#include "myclass.h"
Do not place the statement of include php_php5cpp.h and myclass.h in Extern "C", otherwise an error will occur.
2 macro, function and other
In order to make this module can be used in PHP 4 and PHP 5, we need to use some macro declarations depending on PHP 4 or PHP 5. Because the execution files of PHP 4 and PHP 5 are not compatible, you need to compile different versions for these two PHP versions.
Usually I will put these macros in a separate file. In this example, put it in Objects.h.
Objects.h wants to write some of the functions required by PHP 5, such as:
#ifndef __php5cpp_Objects_h__
#define __php5cpp_Objects_h__
#if php_major_version == 5
Zend_Class_ENTRY * PHP5CPP_CE_MYCLASS;
Static Zend_Object_Handlers PHP5CPP_Object_Handlers_myclass;
FUNCTION_ENTRY PHP5CPP_MYCLASS_METHODS [] = {
Zend_me_mapping (MyClass, Myclass_New, Null)
Zend_me_mapping (setstring, myclass_set_string, null)
Zend_me_mapping (getString, Myclass_Get_String, Null)
{Null, null, null}
}
Typedef enum {
IS_MYCLASS
PHP5CP_OBJ_TYPE;
Struct php5cpp_obj {
Zend_Object STD;
INT RSRC_ID;
PHP5CP_OBJ_TYPE TYPE;
Union {
Myclass * myclass;
} u;
}
You can find that in order to keep the same, each statement has a PHP5CPP_ prefix. This is a conventional convention.
In addition, # if php_major_version == 5 indicates that the macro, function, etc. below the next one is just in the PHP 5, which will be ignored by the pretreatment when we compile in PHP 4.
PHP5CPP_CE_MYCLASS is the entry of class Myclass. PHP5CPP_OBJECT_HANDLERS_MYCLASS is the internal processing of the class Handler (handle).
PHP5CPP_MYCLASS_METHODS [] Transfers the function in MyClass into standard functions that can be used in PHP. This way we can use myclass_new, myclass_get_string, etc. in PHP, etc. to perform these functions. You will find that the Myclass_Destroy function is not defined here, because when you use unset () to a class instance, the system will automatically call its interpretation function. The enumerated variable php5cpp_obj_type in the structural php5cpp_obj declares the type of object. If you want to join a class in the extension, such as: anotherclass, you need to add one, such as: is_anotherclass.
In the PHP5CPP_OBJ structure, some basic information used in PHP, including:
A resource ID: RSRC_ID, pointing to a resource (resource) that records the C object inside the PHP. In fact, our operations in PHP are more like a php resource, which has its own mechanism for garbage recycling and reference counting.
A zend_object to declare our PHP class, so that it can be like a class of PHP. When using this PHP class, we will actually call our C class to process. (Remember: You need to create it like using other PHP classes. (Translated: Using New))
TYPE declares the objects that are now processed. Here, it has only one: is_myclass. But as I said in front, you can add other classes.
The joint type variable U holds a pointer to the C object instance. If you have other CLASS in this extension, you need to add additional pointers here, such as: anotherclass * anotherclass.
Static void php5cpp_object_free_storage (Zend_Object * Object Tsrmls_dc)
{
PHP5CPP_OBJ * OBJ = (php5cpp_obj *) Object;
Zend_hash_DESTROY (OBJ-> std.properties);
Free_hashtable (Obj-> std.properties);
IF (Obj-> u.myclass) {
Zend_List_Delete (Obj-> RSRC_ID);
}
efree (obj);
}
Static void php5cpp_Object_new (Zend_Class_Entry * Class_Type, Zend_Object_Handlers * Handlers, Zend_Object_Value * RetVal Tsrmls_dc)
{
ZVAL * TMP;
PHP5CPP_OBJ * OBJ = (php5cpp_obj *) Emalloc (SIZEOF (PHP5CPP_OBJ));
MEMSET (Obj, 0, sizeof (php5cpp_obj);
Obj-> std.ce = Class_Type;
Alloc_hashtable (Obj-> std.properties);
Zend_hash_init (Obj-> std.properties, 0, null, zval_ptr_dtor, 0);
Zend_hash_copy (Obj-> std.properties, & class_type-> default_properties, (copy_ctor_func_t) zval_add_ref, (void *) & tmp, sizeof (zval *));
RetVal-> Handle = Zend_Objects_Store_put (Obj, Null, PHP5CP_Object_Free_Storage, Null Tsrmls_cc); RetVal-> Handlers = Handlers;
}
PHP5CPP_Object_free_storage () can be seen as an object is a dummy function of php5cpp_obj, because it is to do to release myclass objects to release the object of MyClass
PHP5CPP_OBJECT_NEW () basically is a constructor, which is responsible for allocating memory space, assigns a Zend_Object structure, and initializing the Handler (handle), etc. It is responsible for the construction of all classes in the extension, whether it is an object MyClass or anotherclass.
PHP5CPP_OBJECT_NEW_MYCLASS () Creates an instance of PHP myclss by calling php5cpp_object_new (). If there are several classes in the extension, you have to write a php5cpp_object_new _ * () function for each class.
// register the class entry ..
#define php5cpp_register_class (name, obj_name) /
{/
Zend_Class_ENTRY CE; /
INIT_CLASS_ENTRY (CE, OBJ_NAME, PHP5CPP_ ## Name ## _Methods); /
CE.CREATE_Object = php5cpp_object_new_ ## Name; /
PHP5CPP_CE_ ## name = zend_register_internal_class_ex (& CE, NULL, NULL TSRMLS_CC); /
Memcpy (& php5cpp_Object_handlers_ ## name, zend_get_std_object_handlers (), sizeof (Zend_Object_Handlers); /
PHP5CPP_OBJECT_HANDLERS_ ## Name.clone_obj = NULL; /
PHP5CPP_CE_ ## Name-> CE_FLAGS | = Zend_Acc_Final_Class; /
}
// register resources. If we're using an object, put it into the object store.
#define php5cpp_register_resource (Obj_type, return_value, res, le) /
{/
INT RSRC_ID = Zend_Register_Resource (Object? Null: return_value, res, le); /
IF (Object) {/
PHP5CPP_OBJ * OBJ = (php5cpp_obj *) Zend_Object_Store_Get_Object (Object Tsrmls_cc); /
Obj-> u.obj_type = res; /
Obj-> rsrc_id = rsrc_id; /
Obj-> type = IS_ ## Obj_type; /
} /
}
#define php5cpp_register_myclass_resource (return_value, res, le) /
PHP5CPP_REGISTER_RESOURCE (Myclass, Return_Value, Res, Le)
Then we have to add the above macros in php5cpp.cpp. Although it looks similar, don't mess, actually PHP5CPP_REGISTER_CLASS () and php5cpp_register_resource () processing is different. PHP5CPP_REGISTER_CLASS () registers a program of the actual processing of a class. Introduce the module initialization function (PHP_MINIT_FUNCTION ()) later.
PHP5CPP_REGISTER_RESOURCE () is responsible for getting Resource ID when registering a Resource. In fact, Resource is an example of our C object. When we handle an object, it creates a PHP object, saving the actual object in the PHP object reservoir, and record the Resource ID in the PHP object.
PHP5CPP_REGISTER_MYCLASS_RESOURCE () is just convenient to use PHP5CPP_REGISTER_RESOURCE ().
// There Are for Parsing Parameters and getting the actual object from the number.
#define php5cpp_get_this () /
ZVAL * Object = GetThis ();
#define php5cpp_set_obj (Type) /
PHP5CPP_OBJ * OBJ = (php5cpp_obj *) Zend_Object_Store_Get_Object (Object Tsrmls_cc); /
TYPE ## _INSTANCE = Obj-> u.Type;
#define php5cpp_obj_params (Type, Params) /
PHP5CPP_GET_THIS (); /
IF (Object) {/
IF (params == failure) {/
RETURN_FALSE; /
} /
PHP5CPP_SET_OBJ (TYPE) /
} /
Else
#define php5cpp_obj_no_params (Type) /
PHP5CPP_GET_THIS (); /
IF (Object) {/
IF (zend_num_args ()! = 0) {/
PHP_ERROR (E_WARNING, "Didn't Expect Any Arguments IN% S ()", GET_ACTIVE_FUNCTION_NAME (TSRMLS_C)); /
} /
PHP5CPP_SET_OBJ (TYPE) /
} /
Else
#define php5cpp_myclass_obj_params (params) php5cpp_obj_params (MyClass, params)
#define php5cpp_myclass_obj_no_params () php5cpp_obj_no_params (MyClass)
PHP5CPP_GET_THIS () will return the pointer to the currently used object. If the current use is a structure, getTHIS () will return null; if you are using an object, getThis () returns a pointer to the current PHP object.
PHP5CPP_SET_OBJ () will acquire the C object instance actually used by the PHP object from the object reservoir, and then can be used to do other processing. When using the PHP function / method, the object will store the type similar to "Type ##_instance", such as: in our example is MyClass_instance, ie myclass * type.
PHP5CPP _ * _ obj_params () and php5cpp _ * _ no_obj_params () will be used when calling our function / methods, which process parameters from the PHP party. In the package functions and statements, these parameters can be analyzed by Zend_Parse_Parameters (). You can notice that the macro php5cpp _ * _ params () is the end of ELSE. In this case, when the processed is not an object, it tries to process the structured manner. These macros can be found underout.
The part of the class handled for PHP 5 has been written, and the bottom below is very simple and easy to understand for PHP 4. If we use PHP 4, the part of the code will be inexplicably righteous, and only the following code is processed. If we use PHP 5, it is vice versa.
#ELSE // end of php5-specific macros
// this stuff is for php 4. They're Empty on Purpose, Obviously.
#define php5cpp_get_this ()
#define php5cpp_myclass_obj_params (params)
#define php5cpp_myclass_obj_no_params ()
#define php5cpp_register_class (name, obj_name)
#define php5cpp_register_myclass_resource (return_value, res, le) /
Zend_Register_Resource (Return_Value, Res, le);
#ENDIF / / END OF PHP4-Specific Macros
Very simple, other macros except pHP5cpp_register_myclass_resource () are empty. PHP5CPP_REGISTER_MYCLASS_RESOURCE () will still register a resource, but will not do anything for objects. Ok, after doing these, you can compile the code in PHP 4 and PHP 5.
Some macros below are in PHP 4 and PHP 5, they are processing structures and non-faced objects.
// There Are for Both PHP 4 and 5
#define php5cpp_myclass_rsrc_params (params) /
IF (params == failure) {/
RETURN_FALSE; /
} /
Else {/
Zend_Fetch_Resource (MyClass_Instance, Myclass *, & Resource, -1, "Myclass", Le_MYCLASS; /
}
#define php5cpp_myclass_rsrc_no_params () /
IF (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "R", & Resource) == Failure) {/
RETURN_FALSE; /
} /
Else {/
Zend_Fetch_Resource (MyClass_Instance, Myclass *, & Resource, -1, "Myclass", Le_MYCLASS; /
}
Static Zend_rsrc_dtor_func (Destroy_MyClass)
{
IF (RSRC-> PTR) {
Delete (myclass *) RSRC-> PTR;
RSRC-> PTR = NULL;
}
}
#ENDIF
They are also very simple: try to analyze the parameters sent in the PHP interface, and obtain the resource to be processed. DESTROY_MYCLASS is responsible for resource recycling. When the garbage collection mechanism is triggered, or when Destroy object / resource, it will pass the memory cleaning / release of the c object instance.
We will use these macros like this in php5cpp.cpp:
PHP5CPP_MYCLASS_OBJ_PARAMS (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "S", & S, & LEN)
PHP5CP_MYCLASS_RSRC_PARAMS (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "RS", & Resource, & S, & Len)
In PHP 5, it will become like this after pre-treatment:
ZVAL * Object = GetThis ();
IF (Object) {
IF (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "S", & S, & LEN) == Failure) {
Return_false;
}
PHP5CP_OBJ * OBJ = (php5cpp_obj *) Zend_Object_Store_GET_Object (Object Tsrmls_cc);
Myclass_instance = Obj-> u.myclass;
}
Else IF (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "RS", & Resource, & S, & Len) == Failure) {
Return_false;
}
Else {
Zend_Fetch_Resource (MyClass_Instance, Myclass *, & Resource, -1, "Myclass", Le_MYCLASS; /
}
It can be seen that in object-oriented mode, it will try to get objects, analyze parameters, and acquire the actual Myclass object. If in the non-dominant mode mode, this code will try to analyze the parameters in a structure, and obtain Resource, etc..
In PHP 4, it will become the following after pre-treatment:
IF (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "RS", & Resource, & S, & Len) == Failure) {
Return_false;
}
Else {
Zend_Fetch_Resource (MyClass_Instance, Myclass *, & Resource, -1, "Myclass", Le_MYCLASS; /
}
Here (PHP 4), the object-oriented code will be ignored, leaving only the code of the structured program.
For the meaning of we do, now we have to be a simplest part, write our packaging code, and make our Class in PHP.
3 package code
We have to add some of the package functions and methods in PHP_PHP5CPP.H:
PHP_Function (myclass_new);
PHP_Function (myclass_destroy);
PHP_FUNCTION (MyClass_set_string);
PHP_FUNCTION (MyClass_get_string); simple. Ok, then we write the execution code of the package function in php5cpp.cpp:
PHP_Function (myclass_new)
{
Myclass * myclass_instance;
Char * S;
INT LEN = 0;
PHP5CPP_GET_THIS ();
IF (Zend_PARSE_PARAMETERS (Zend_Num_args () TsRMLS_CC, "| S", & S, & LEN) == Failure) {
Return_false;
}
IF (len) {
Myclass_instance = new myclass (String (S, LEN));
}
Else {
Myclass_instance = new myclass;
}
IF (myclass_instance! = NULL) {
PHP5CPP_REGISTER_MYCLASS_RESOURCE (Return_Value, Myclass_Instance, Le_MYCLASS);
}
}
PHP_FUNCTION (MyClass_Destroy)
{
ZVAL * resource;
IF (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "R", & Resource) == Failure) {
Return_false;
}
Zend_List_Delete (z_resval_p (resource));
}
PHP_FUNCTION (MyClass_ST_String)
{
ZVAL * resource;
Myclass * myclass_instance;
INT LEN = -1;
Char * S;
PHP5CPP_MYCLASS_OBJ_PARAMS (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "S", & S, & LEN)
PHP5CP_MYCLASS_RSRC_PARAMS (Zend_Parse_Parameters (Zend_Num_args () TsRMLS_CC, "RS", & Resource, & S, & Len)
IF (MyClass_Instance == NULL) {
PHP_ERROR (E_WARNING, "Can't set string on null resource in% s ()", get_active_function_name (tsrmls_c));
Return_false;
}
Else {
Myclass_instance-> setstring (String (S, LEN));
Return_True;
}
}
PHP_FUNCTION (MyClass_Get_String)
{
ZVAL * resource;
Myclass * myclass_instance = null;
String RetVal;
PHP5CPP_MYCLASS_OBJ_NO_PARAMS ()
PHP5CPP_MYCLASS_RSRC_NO_PARAMS ()
IF (MyClass_Instance == NULL) {
PHP_ERROR (E_WARNING, "Can't get string from null resource in% s ()", get_active_function_name (tsrmls_c));
Return_false;
}
Else {
Retval = myclass_instance-> getString (); retval_stringl ((char *) RetVal.Data (), retval.length (), 1);
}
}
There is nothing is too difficult here, so I don't want each function to explain once. Now just look at myclass_new () and myclass_set_string () as an example.
In myclass_new (), you first try to get an object. Once again, it will only take effect only in the object-oriented mode, and it will be ignored by the preparation of PHP 4.
Because the C class constructor has the default parameters, we have to see if the PHP has a string type parameter. After analyzing the parameters that come in, is to allocate myclass_instance memory space, then store the resulting resource with php5cpp_register_myclass_resource. In the OO mode of PHP 5, php5cpp_register_myclass_resources will use the resource as an object, which is only simply created a resource under the structured module of PHP 4.
Then say myclass_set_string () ...
First, first define a ZVAL structure to process the parameters that the PHP is used in structured interface. MyClass_Instance will be used to point to actual C objects. LEN and * S are used to save the length of the string and strings from the PHP party.
In PHP 4, php5cpp_myclass_obj_params () will be ignored by pre-processing. In PHP 5 it will try to obtain objects, if the object fails, it will return the step of using the structured interface.
After obtaining myclass_instance, you can use the C MyClass class like the class is usually used. First adjust myclass's setString () method. The obtained C standard String class object is then transferred to a CHAR pointer similar to the C method and passed back to the PHP party.
Finally, let PHP know this new Class, it is very simple, add: in php_minit_function:
PHP5CPP_REGISTER_CLASS (MyClass, "MyClass");
Le_MYCLASS = Zend_Register_List_Destructors_ex (Destroy_myclass, Null, "MyClass", Module_Number;
Ok, the code here has been written. : P
Section 7. then...
Then you can compile the extension module in the way you need. You may need to add some additional code, like Using Namespace STD, etc. A few pages of this introduction will contain a complete operational example. I have been tested on the Gentoo system, including current (2004-03-19) on CVS, is new PHP 5 and PHP_4_3 environments. (Translation: This translation only provides links to the code, no including these code)
Section 8. Example source code
There is no complete in an example source code in the translation, you go through the link below:
Http://bugs.tutorbuddy.com/php5cpp/php5cpp/example_code.html
Section 9. License
This article is authorized under GNU Free Documentation License. Here are the content link of licensed license ...
Http://bugs.tutorbuddy.com/php5cpp/php5cpp/license.html
Section 10. Modify history
JUN 2004 fishchen translated.
6 MAY 2004 Version 0.1.0.
29 Mar 2004 First Version.