Translation [PHP extension development and Embedded] 10th chapter-PHP4 Objects

Source: Internet
Author: User
Tags modifiers php class php source code zend
Objects of the PHP4

Once upon a time, in an earlier version, PHP did not support any object-oriented programming syntax. Introducing the Zend Engine (ZE1) in PhP4, several new features have emerged, including object data types.

Evolution of PHP Object types

The first object-oriented programming (OOP) support implements only the semantics of object affinity. In the words of a PHP kernel developer, the PhP4 object simply binds an array and some methods together. It's the PHP object you're going to study now.

The second major release of the Zend Engine (ZE2) is in PhP5, which introduces some new features in the OOP implementation of PHP. For example, properties and methods can use access modifiers to mark their visibility outside of your class definition, and overloads of functions can be used to define custom behavior for an internal language structure, which can be standardized using an interface implementation API between call chains of multiple classes. As you learn the 11th chapter "PHP5 Objects", you will build awareness of these by implementing these features in the PHP5 class definition.

Implementation class

Before entering the world of OOP, we need to travel light. So, restore your extension to the skeleton shape you just built in chapter 5th, "Your first extension."

To be independent of your original exercises, you can name this version sample2. Put the following three files into the Ext/sample2 directory of your PHP source code:

Config.m4

Php_arg_enable (Sample2,    [Whether to ENABLE the "sample2" extension],    [  enable-sample2       Enable " Sample2 "extension support])    if test $PHP _sample2! =" no "; then    Php_subst (sample2_shared_libadd)    Php_new_ EXTENSION (Sample2, sample2.c, $ext _shared)  fi

Php_saple2.h

#ifndef php_sample2_h/  * Prevent Double inclusion */  #define PHP_SAMPLE2_H/    * Define Extension Properties * /  #define PHP_SAMPLE2_EXTNAME    "Sample2"  #define Php_sample2_extver    "1.0"/    * Import Configure options when    building outside of    the PHP source tree */  #ifdef have_config_h  #include " Config.h "  #endif/    * Include PHP Standard Header */  #include" php.h "/    * Define the entry point symbol< c24/>* Zend would use if loading this module  */  extern zend_module_entry sample2_module_entry;  #define PHPEXT_SAMPLE2_PTR &sample2_module_entry    #endif/* php_sample2_h */

Sample2.c

#include "php_sample2.h"    static function_entry php_sample2_functions[] = {      {null, NULL, NULL}  };    Php_minit_function (Sample2)  {      return SUCCESS;  }    Zend_module_entry sample2_module_entry = {  #if zend_module_api_no >= 20010901      Standard_module_header,  #endif      Php_sample2_extname,      php_sample2_functions,      php_minit (sample2),      null,/* Mshutdown */      NULL,/* Rinit */      NULL,/* Rshutdown */      NULL,/* MINFO */  #if zend_module_api_no >= 20010901      php_sample2_ Extver,  #endif      standard_module_properties  };    #ifdef compile_dl_sample2  zend_get_module (sample2)  #endif

Now, as in the 5th chapter, you can perform phpize,./configure, make to build your sample2.so extension module.

Your previous config.w32 will work as well as the config.m4 you gave here.

Defining class Entries

In user space, define a class as follows:

<?php  class Sample2_firstclass {  }  ?>

There is no doubt that you will guess that it is still a bit difficult to implement it in an extension. First, you need to define an zend_class_entry pointer in your source code file, like the previous chapter defining an int le_sample_descriptor:

Zend_class_entry *php_sample2_firstclass_entry;

Now it is possible to initialize and register the class in the Minit function.

Php_minit_function (Sample2)  {      zend_class_entry ce;/* TEMP Variable */        * Registered class *      /Init_class_entry (CE, "Sample2 _firstclass ", NULL);      Php_sample2_firstclass_entry =              Zend_register_internal_class (&ce tsrmls_cc);        return SUCCESS;  }

Build this extension, Test get_declared_classes (), and you will see that Sample2_firstclass is now available in user space.

Defining the implementation of a method

At the moment, what you achieve is just a stdclass, of course it is available. But you actually want your class to be able to do something.

To achieve this, you need to go back to the other knowledge points learned in the 5th chapter. Replace the null parameter passed to Init_class_entry () with php_sample2_firstclass_functions and define the structure directly above the Minit function as follows:

Static Function_entry php_sample2_firstclass_functions[] = {      {null, NULL, NULL}  };

Do you look familiar? Of course. This is the same structure as the process function you originally defined. Even the way that this structure is set up is similar:

Php_named_fe (Method1, PHP_FN (SAMPLE2_FIRSTCLASS_METHOD1), NULL)

Of course, you can also choose Php_fe (Method1, NULL). But looking back at Chapter 5th, this is expected to find the function implementation of the name ZIF_METHOD1, which may potentially be a bout of other method1 () implementation conflicts. For the namespace security of the function, we prefix the class name with the name of the method.

The format of Php_falias (Method1, SAMPLE2_FIRSTCLASS_METHOD1, NULL) is also possible; But it's a bit counterintuitive, and you might wonder later when you look back at the code, "Why didn't you use Php_fe ()?"

Now that you have attached a list of functions to the definition of the class, it is time to define some methods. Create the following function above the php_sample2_firstclass_functions structure:

Php_function (sample2_firstclass_countprops)  {      Return_long (zend_hash_num_elements (Z_objprop_p () )));  }

Accordingly, add a php_named_fe () entry in its list of functions:

Static Function_entry php_sample2_firstclass_functions[] = {      Php_named_fe (countprops,              php_fn (Sample2_ Firstclass_countprops), null)      {null, NULL, NULL}  };

Note that the function names exposed to user space here are all lowercase. To ensure that the method and function names are case insensitive, the internal function is required to give all lowercase names.

The only new element here is Getthis (), which, in all PHP versions, is parsed into a macro, and the expansion is this_ptr. This_ptr is essentially the same as the $this meaning in the user-space object method. Getthis () returns null if no object instance is available, such as when the method is called statically.

The data return semantics of the object methods are consistent with the procedure functions, and the parameter acceptance and Arg_info are the same set of things.

Php_function (Sample2_firstclass_sayhello)  {      char *name;      int Name_len;      if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "s",                          &name, &name_len) = = FAILURE) {          return_null ();      }      php_printf ("Hello");      Phpwrite (name, Name_len);      php_printf ("!\nyou called an object method!\n");      return_true;  }

Constructors

Your class constructor can be implemented just like any other ordinary class method, and its naming follows the same rules. What's special is that you need to name the constructor the class name. The other two ZE1 magic Methods __sleep () and __wakeup () can also be implemented in this way.

Inherited

In PhP4, inheritance between internal objects is imperfect and it is best to avoid them. If you do have to inherit other objects, you need to copy the following ZE1 code:

void Php_sample2_inherit_from_class (Zend_class_entry *ce, Zend_class_entry *parent_ce) {Zen D_hash_merge (&ce->function_table, &parent_ce->function_table, (Void (*) (void *)) function_add_re      F, NULL, sizeof (zval*), 0);      Ce->parent = Parent_ce; if (!ce->handle_property_get) {ce->handle_property_get = Parent_ce->handle_property_ge      T } if (!ce->handle_property_set) {ce->handle_property_set = Parent_ce->handle_prop      Erty_set; } if (!ce->handle_function_call) {Ce->handle_function_call = Parent_ce->handle_fu      Nction_call;          } if (!zend_hash_exists (&ce->function_table, Ce->name, Ce->name_length + 1)) {          Zend_function *fe; if (Zend_hash_find (&parent_ce->function_table, Parent_ce->name, Parent_ce->name_length + 1, (void**) fe) = = SUCCESS) {zend_hash_update (&ce->function_table, CE              ->name, Ce->name_length + 1, fe, sizeof (zend_function), NULL);          Function_add_ref (FE); }      }  }

Define such a function so that you can invoke it in Minit Zend_register_internal_class below:

Init_class_entry (CE, "sample2_firstclass", NULL);  /* Assume that Php_saple2_ancestor is a registered Zend_class_entry */  php_sample2_firstclass_entry =          zend_register_internal _class (&ce tsrmls_cc);  Php_sample2_inherit_from_class (Php_sample2_firstclass_entry                              , php_sample2_ancestor);

Although inheritance in this way works, you should avoid inheriting from ZE1 because it does not design inheritance processing for internal objects. For most OOP practices in PHP, ZE2 and its revised object model are robust, encouraging all OOP-related tasks to be handled directly using it.

Working with instances

Like other user-space variables, objects are stored in the Zval * container. In ZE1, Zval * contains a hashtable * for saving properties, and a zend_class_entry * pointer to the definition of the class. In ZE2, these values are replaced by a handle table, which increases the object ID of a numeric value, similar to the use of resource IDs.

Fortunately, these differences between ZE1 and ZE2 are hidden by the z_* () family macros introduced in the 2nd Chapter, "Variables in and out", so you don't need to worry about them in your extension. The two ZE1 macros are listed in table 10.1 below, consistent with the relevant macros for non-OOP, and they also have corresponding _p and _PP versions to handle first-or two-level visits.

Create an instance

Most of the time, your extension doesn't need to create an instance yourself. Instead, the user space invokes the new keyword to create an instance and calls your class constructor.

But you may still need to create instances, such as in the Factory method, the OBJECT_INIT_EX (Zval *val, Zend_class_entry *ce) function in Zend_api can be used to initialize an object instance into a variable.

Note that the OBJECT_INIT_EX () function does not call the constructor. When an object is instantiated in an intrinsic function, the constructor must be called manually. The following procedure function repeats the functional logic of the NEW keyword:

Php_function (sample2_new) {int argc = Zend_num_args ();      Zval ***argv = safe_emalloc (sizeof (zval**), argc, 0);      Zend_class_entry *ce;          if (argc = = 0 | |          ZEND_GET_PARAMETERS_ARRAY_EX (argc, argv) = = FAILURE) {efree (argv);      Wrong_param_count;      }/* The first parameter is the name of the class */Separate_zval (argv[0]);      Convert_to_string (*argv[0]);      /* Class name is stored in lowercase */php_strtolower (Z_STRVAL_PP (argv[0]), Z_STRLEN_PP (argv[0])); if (Zend_hash_find (EG (class_table), z_strval_pp (Argv[0]), Z_STRLEN_PP (argv[0]) + 1, (void**) &amp              CE) = = FAILURE) {php_error_docref (NULL tsrmls_cc, e_warning, "Class%s does not exist."),          Z_STRVAL_PP (argv[0]));          Zval_ptr_dtor (Argv[0]);          Efree (argv);      Return_false;      } object_init_ex (Return_value, CE); /* If a constructor is called, additional arguments are passed to the constructor */if (zend_hash_exists (&ce->function_table, Z_STRVAL_PP (Argv[0]), Z_st RLEN_PP (Argv[0]) + 1)) {/* object has a constructor */Zval *ctor, *dummy = NULL;          /* constructor name is class name */Make_std_zval (ctor);          Array_init (ctor);          Zval_add_ref (Argv[0]);          Add_next_index_zval (ctor, *argv[0]);          Zval_add_ref (Argv[0]);          Add_next_index_zval (ctor, *argv[0]); if (CALL_USER_FUNCTION_EX (&ce->function_table, NULL, ctor, &dummy,/* Do not care about return values */argc-1, argv + 1,/* parameter */0, NULL tsrmls_cc) = = FAILURE) {php_error_          Docref (NULL tsrmls_cc, e_warning, "Unable to call constructor");          } if (dummy) {zval_ptr_dtor (&dummy);      } zval_ptr_dtor (&ctor);      } zval_ptr_dtor (Argv[0]);  Efree (argv); }

Don't forget to add a reference to the php_sample2_functions. It is the list of your extended procedure functions, not the class method. To use the Php_strtolower () function, you also need to add # include "Ext/standard/php_string.h".

This function is the most complex one you've implemented so far, with a couple of new features. The first is Separate_zval (), in fact its function you have implemented many times, the use of Zval_copy_ctor () assignment value to a temporary structure, to avoid modifying the original content. However, it is a macro version of the package.

Php_strtolower () is used to convert the class name to lowercase, in order to achieve a case-insensitive PHP class name and function name. This is just one of the many PHPAPI tool functions listed in Appendix B.

EG (class_table) is a global variable, and all Zend_class_entry definitions are registered to it. It is important to note that in ZE1 (PHP4), this hashtable stores a zend_class_entry * struct that is visited at one level. In ZE2 (PHP5), it is stored as a two-level visit. This should not be a problem, because direct access to this hashtable is not common, but it is always good to know.

CALL_USER_FUNCTION_EX () is part of the ZENDAPI call you will see in the 20th chapter, "Advanced Embedded." Here you will take away the first element of the Zval * * parameter stack received from ZEND_GET_PARAMETERS_EX () to pass the remaining arguments to the constructor intact.

The original code in the translator's environment (php-5.4.9) can not be run, you need to change the zend_class_entry *ce to two-level interview. Here is the code that the translator tests.

Php_function (sample_new) {int argc = Zend_num_args ();       Zval ***argv = safe_emalloc (sizeof (zval * *), argc, 0);       Zend_class_entry **ce;                */*: Here in the translator's environment (php-5.4.9) is a two-level visit////* Array to read all incoming parameters */if (argc = = 0 | |          ZEND_GET_PARAMETERS_ARRAY_EX (argc, argv) = = FAILURE) {efree (argv);      Wrong_param_count;      }/* Isolate the first parameter (quarantine in order to make the following type conversions do not affect the original data) */Separate_zval (argv[0]);      /* Convert the first argument to a string type and lowercase (because the class name of PHP is case-insensitive) */convert_to_string (*argv[0]);      Php_strtolower (Z_STRVAL_PP (argv[0]), Z_STRLEN_PP (argv[0])); /* Find out if the provided class exists in the class's hashtable, and if so, the corresponding zend_class_entry * */if (Zend_hash_find (EG class_table), z_strval_pp (AR Gv[0]), Z_STRLEN_PP (argv[0]) + 1, (void * *) &ce) = = FAILURE) {php_error_docref (NULL tsrmls_cc, e_warning, "          Class%s does not exist. ", Z_strval_pp (Argv[0]));          Zval_ptr_dtor (Argv[0]);          Efree (argv); Return_FALSE;      }/* Initializes the return value to the object of the Found class */OBJECT_INIT_EX (return_value, *ce); /* Check if the class has a constructor */if (Zend_hash_exists (& (*ce)->function_table, z_strval_pp (Argv[0]), Z_STRLEN_PP (argv[0]) + 1)            ) {Zval *ctor, *dummy = NULL; /* Constructs ctor as an array, corresponding to the user space as: Array (argv[0], argv[0]), * actually corresponds to the static method of the user space call class $funcname Parameter form: * Array (class name,          Method name) */Make_std_zval (ctor);          Array_init (ctor);          Zval_add_ref (Argv[0]);          Add_next_index_zval (ctor, *argv[0]);          Zval_add_ref (Argv[0]);          Add_next_index_zval (ctor, *argv[0]); /* Call function */if (CALL_USER_FUNCTION_EX (& *ce)->function_table, NULL, ctor, &dummy, Argc-1, argv + 1,           0, null tsrmls_cc) = = FAILURE) {php_error_docref (null TSRMLS_CC, e_warning, "Unable to call constructor");          }/* If there is a return value direct destructor Discard */if (dummy) {zval_ptr_dtor (&dummy);         } /* Deconstruct an array of temporary use (used to describe the called method name) */Zval_ptr_dtor (&ctor);      }/* destructor the first parameter (class name) that was temporarily isolated */Zval_ptr_dtor (argv[0]);  /* Release argument list space */Efree (argv); }

Accepting instances

Sometimes your function or method needs to accept object parameters for user space. For this purpose, Zend_parse_parameters () provides two-format modifiers. The first is O (lowercase o), which verifies whether the passed argument is an object and sets it to the passed Zval * *. Here is a simple example of a user-space function in this way that returns the class name of the passed-in object.

Php_function (sample2_class_getname)  {      zval *objvar;      Zend_class_entry *objce;      if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "O",                                  &objvar) = = FAILURE) {          return_null ();      }      Objce = Z_objce_p (Objvar);      Return_stringl (Objce->name, objce->name_length, 1);  }

The second modifier is O (capital letter O), which not only allows zend_parse_parameters () to validate the type of Zval *, but also validates the class of the passed object. To do this, you need to pass a zval * * easy and a zend_class_entry * for validation, such as the following implementations are expected to pass in an instance of the Sample2_firstclass class:

Php_function (sample2_reload)  {      zval *objvar;      if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "O",          &objvar, php_sample2_firstclass_entry) = = FAILURE) {          return_null ();      }      /* Invoke the imaginary "reload" function *      /Return_bool (Php_sample2_fc_reload (Objvar tsrmls_cc));  }

Accessing properties

As you've seen, the class method can get to the current object instance through Getthis (). By combining the result of this macro or other Zval * with the z_objprop_p () macro that contains the object instance, the resulting Hashtable * contains all the properties of the object.

The property list for an object is a hashtable * that contains zval *, which is just another list of user space variables placed in a special location. and using Zend_hash_find (EG (active_symbol_table), ...) Taking variables from the current scope, you can also use the 8th chapter "Working on arrays and Hashtable" to get or set the properties of an object Zend_hash-api.

For example, suppose that the variable rcvdclass this zval * contains an instance of Sample2_firstclass, and the following code block can take the property Foo from its Standard property hashtable.

Zval **fooval;  if (Zend_hash_find (Z_objprop_p (rcvdclass),          "foo", sizeof ("foo"), (void**) &fooval) = = FAILURE) {/      * $ Rcvdclass->foo doesn ' t exist */      return;  }

To add elements to the attribute table, this is the reverse process of the process, call Zend_hash_add () to add elements, or you can replace the assoc of the add_assoc_* () family function introduced in the 8th chapter of the array with the property to handle the object.

The following constructor function provides some pre-set default properties for an instance of Sample2_firstclass:

Php_named_function (Php_sample2_fc_ctor)  {/      * for brevity, while demonstrating that the function name can be arbitrary, the function name implemented here is not the class name */      zval *objvar = getthis ();        if (!objvar) {          php_error_docref (NULL tsrmls_cc, e_warning,                          "Constructor called statically!");          Return_false;      }        Add_property_long (Objvar, "life");      Add_property_double (Objvar, "PI", 3.1415926535);      /* The return value of the constructor is ignored (please review the example of the previous constructor) */  }

It is now possible to connect it to the object's constructor through the Php_sample2_firstclass_functions list:

Php_named_fe (Sample2_firstclass, Php_sample2_fc_ctor, NULL)

Because the previous sample_new () factory function used the call format of a static method when calling the constructor in CALL_USER_FUNCTION_EX (), the Getthis () would not have the desired result if it was a constructor call that was fired using this factory function. Therefore, the translator has made the corresponding changes to the example, if the reader in this block encountered problems can refer to the translator's code.

Php_function (sample_new) {int argc = Zend_num_args ();       Zval ***argv = safe_emalloc (sizeof (zval * *), argc, 0);       Zend_class_entry **ce;                */*: Here in the translator's environment (php-5.4.9) is a two-level visit////* Array to read all incoming parameters */if (argc = = 0 | |          ZEND_GET_PARAMETERS_ARRAY_EX (argc, argv) = = FAILURE) {efree (argv);      Wrong_param_count;      }/* Isolate the first parameter (quarantine in order to make the following type conversions do not affect the original data) */Separate_zval (argv[0]);      /* Convert the first argument to a string type and lowercase (because the class name of PHP is case-insensitive) */convert_to_string (*argv[0]);      Php_strtolower (Z_STRVAL_PP (argv[0]), Z_STRLEN_PP (argv[0])); /* Find out if the provided class exists in the class's hashtable, and if so, the corresponding zend_class_entry * */if (Zend_hash_find (EG class_table), z_strval_pp (AR Gv[0]), Z_STRLEN_PP (argv[0]) + 1, (void * *) &ce) = = FAILURE) {php_error_docref (NULL tsrmls_cc, e_warning, "          Class%s does not exist. ", Z_strval_pp (Argv[0]));          Zval_ptr_dtor (Argv[0]);          Efree (argv); Return_FALSE;      }/* Initializes the return value to the object of the Found class */OBJECT_INIT_EX (return_value, *ce); /* Check if the class has a constructor */if (Zend_hash_exists (& (*ce)->function_table, z_strval_pp (Argv[0]), Z_STRLEN_PP (argv[0]) + 1)  ) {#define Dynamic_constructor #ifndef dynamic_constructor zval *ctor;    #endif zval *dummy = NULL; #ifndef dynamic_constructor/* Constructs the ctor as an array, corresponding to the user space in the form of array (argv[0], argv[0]), which actually corresponds to the static method of the user space call class          $funcname Parameter form: * Array (class name, method name) */Make_std_zval (ctor);          Array_init (ctor);          Zval_add_ref (Argv[0]);          Add_next_index_zval (ctor, *argv[0]);          Zval_add_ref (Argv[0]);  Add_next_index_zval (ctor, *argv[0]);                   #endif/* Call function */if (CALL_USER_FUNCTION_EX (& *ce)->function_table, #ifndef dynamic_constructor NULL, ctor, #else &return_value, *argv[0], #endif &dummy, AR Gc-1, argv + 1, 0, NULL tsrmls_cc) = = FAILURE) {php_error_docref (NULL tsrmls_cc, e_warning, "Unable to call constructor");          }/* If there is a return value direct destructor Discard */if (dummy) {zval_ptr_dtor (&dummy);  } #ifndef dynamic_constructor/* To deconstruct an array of temporary use (used to describe the called method name)/Zval_ptr_dtor (&ctor);      #endif}/* destructor the first parameter (class name) that was temporarily isolated */Zval_ptr_dtor (argv[0]);  /* Release argument list space */Efree (argv); }

Now, you can use the function to define whether DYNAMIC_CONSTRUCTOR this macro to switch the constructor calls, to facilitate the reader to understand.

Summary

Although the class functionality provided by ZE1/PHP4 is best used sparingly, it is beneficial to do so because the current PHP4 is widely used in the production environment. The techniques covered in this chapter allow you to flexibly write code for various functions, which can now be compiled and run, and will continue to work in the future.

In the next chapter, you'll see the real object-oriented in php5, and if you want OOP, you'll get the reason for the upgrade, and you'll never be able to look back after the upgrade.

The above is [translation][php extension development and Embedded] 10th chapter-PHP4 Object content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!

  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.