Methods
If you did read the last chapter, you're probably starting to think, "It all looks pretty much the same so far", and so far, you're right. Now that it's time to declare some object methods. However, you'll start to see some very definite, and much welcome, differences.
PHP_METHOD(Sample3_SecondClass, helloWorld) { php_printf("Hello World "); }
The PHP_METHOD() macro, introduced with version 2 of the Zend Engine, wraps itself around the PHP_FUNCTION() macro to combine the classname with the method name just as you did manually for PHP4 method declarations. By using this macro, namespacing conventions are kept consistent between extensions and your code becomes easier to parse by other maintainers.
Declaration
Defining a method implementation, like any other function, is only useful if it's linked into userspace by way of the class entry's function table. As with the PHP_METHOD() macro used for implementation, there are also new macros for declaration within the function list:
- PHP_ME(classname, methodname, arg_info, flags)
PHP_ME() adds a classname portion to the PHP_FE() macro from Chapter 5, "Your First Extension," as well as a new parameter at the end that provides access control modifiers such as public, protected, private, static, abstract, and a few other options. To declare the helloWorld method you just defined, you might use an entry like:
PHP_ME(Sample3_SecondClass,helloWorld,NULL,ZEND_ACC_PUBLIC)
- PHP_MALIAS(classname, name, alias, arg_info, flags)
Just like the PHP_FALIAS() macro, this declaration allows you to assign a new namegiven in the name parameterto an existing method implementation from the same class, specified by alias. For example, to give a duplicate name to your helloWorld method you might use:
PHP_MALIAS(Sample3_SecondClass, sayHi, helloWorld, NULL, ZEND_ACC_PUBLIC)
- PHP_ABSTRACT_ME(classname, methodname, arg_info)
Abstract methods in internal classes are just like abstract userspace methods. They're used as placeholders for within ancestral classes that expect their descendants to provide true implementations according to a specific API. You will typically use this macro within Interfaces, which are a specialized form of class entry.
- PHP_ME_MAPPING(methodname, functionname, arg_info)
This last form of method declaration macro is aimed primarily at extensions that export a dual OOP/non-OOP interface such as the MySQLi extension where the mysqli_query() procedural function and MySQLi::query() method are both serviced by the same internal implementation. Assuming you already had a procedural function, such as the sample_hello_world() that you wrote in Chapter 5, you would use this declaration macro to alias it to a method in the following manner (note that mapped methods are always public, non-static, non-final):
PHP_ME_MAPPING(hello, sample_hello_world, NULL)
So far, all the method declarations you've seen have used ZEND_ACC_PUBLIC for their flags parameter. In practice, this value can be made up of any (or none) of the type flags listed in Table 11.1 Bitwise OR'd with exactly one of the visibility flags listed in Table 11.2, and optionally OR'd with one of the special method flags you'll encounter in the "Special Methods" section later in this chapter.
Type Flag |
Meaning |
---|---|
ZEND_ACC_STATIC |
Method will be called statically. In practice, this simply means that even if the method is called via an instance, $thisor more accurately: this_ptrwill not be populated with the instance's scope. |
ZEND_ACC_ABSTRACT |
Method is not a true implementation. The current method should be overridden by a child class before being called directly. |
ZEND_ACC_FINAL |
Method cannot be overridden by child classes. |
Visibility Flag |
Meaning |
---|---|
ZEND_ACC_PUBLIC |
Callable from any scope or even outside of an object. This is the same visibility shared by all PHP4 methods. |
ZEND_ACC_PROTECTED |
Only callable from the class it was defined in, or one of its children or ancestors. |
ZEND_ACC_PRIVATE |
Only callable from the exact class it was defined by. |
For example, because the Sample3_SecondClass::helloWorld() method you defined earlier has no need for an object instance, you could change its declaration from a simple ZEND_ACC_PUBLIC to ZEND_ACC_PUBLIC|ZEND_ACC_STATIC so the engine knows not to bother.
Special Methods
In addition to the ZE1 set of magic methods, ZE2 adds a large family of magic methods listed in Table 11.3 and found in the PHP online manual at http://www.php.net/language.oop5.magic.
Method |
Usage |
---|---|
__construct(...) |
An alternative to the automatically called object constructor (previously defined as the method who's name matches the classname). If method implementation exist for both __construct() and classname(), __construct() will receive priority and be called during instantiation. |
__destruct() |
When the instance falls completely out of scopeor the request as a whole shuts downall instances implicit call their __destruct() methods to handle any last minute cleanup such as shutting down file and network handles. |
__clone() |
By default, all instances are passed around in truereference sets. As of PHP5, however, an instance can be explicitly copied using the clone keyword. When clone is called on an object instance, the __clone() method is implicitly called to allow an object to duplicate any internal resources as needed. |
__toString() |
When expressing an instance as a textual object, such as when using the echo or print statements, the __toString() method is automatically called by the engine. Classes implementing this magic method should return a string containing a representation of the object's current state. |
__get($var) |
If a script requests a property from an object instance that either does not exist in the standard properties table or is declared as non-public, the __get() magic method is called with the name of the property passed as the only parameter. Implementations may use their own internal logic to determine the most sensible return value to provide. |
__set($var, $value) |
Like__get(), __set() provides the opportunity to handle variable assignment when the variable being assigned is not in the standard properties table or is declared non-public. __set() implementations may choose to implicitly create these variables within the standard properties table, set the values within other storage mechanisms, or simply throw an error and discard the value. |
__call($fname, $args) |
Calling an undefined method on an object may be handled gracefully through the use of a __call() magic method implementation. This method receives two arguments: The method name being called, and a numerically indexed array containing the arguments passed to that method. |
__isset($varname) |
As of PHP 5.1.0, the calls to isset($obj->prop) will not only check for the prop property within $obj, they will also call into any defined __isset() method within $obj to dynamically evaluate if attempts to read or write the property would succeed given the dynamic __get() and __set() methods. |
__unset($varname) |
Like __isset(), PHP 5.1.0 introduced a simple OOP interface to the unset() function for properties that, although they might not exist within an objects standard properties table, might have meaning within the __get() and __set() dynamic property space. |
Note
Extra magic method functionality is available through certain interfaces such as the ArrayAccess interface as well as several SPL interfaces.
Within an internal object implementation, each of these special "magic methods" can be implemented as any other method within your object by defining a PHP_ME() line with the right name and a PUBLIC access modifier. For __get(), __set(), __call(), __isset(), and __unset(), which require a precise number of arguments to be passed, you must define an appropriate arg_info struct that states that the method takes exactly 1 or 2 arguments. The following code snippets show arg_info structs and their corresponding PHP_ME() entries for each of the magic methods:
static ZEND_BEGIN_ARG_INFO_EX(php_sample3_one_arg, 0, 0, 1) ZEND_END_ARG_INFO() static ZEND_BEGIN_ARG_INFO_EX(php_sample3_two_args, 0, 0, 2) ZEND_END_ARG_INFO() static function_entry php_sample3_sc_functions[] = { PHP_ME(Sample3_SecondClass, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(Sample3_SecondClass, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR) PHP_ME(Sample3_SecondClass, __clone, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CLONE) PHP_ME(Sample3_SecondClass, __toString, NULL, ZEND_ACC_PUBLIC) PHP_ME(Sample3_SecondClass, __get, php_sample3_one_arg, ZEND_ACC_PUBLIC) PHP_ME(Sample3_SecondClass, __set, php_sample3_two_args, ZEND_ACC_PUBLIC) PHP_ME(Sample3_SecondClass, __call, php_sample3_two_args, ZEND_ACC_PUBLIC) PHP_ME(Sample3_SecondClass, __isset, php_sample3_one_arg, ZEND_ACC_PUBLIC) PHP_ME(Sample3_SecondClass, __unset, php_sample3_one_arg, ZEND_ACC_PUBLIC) { NULL, NULL, NULL } };
Notice that __construct, __destruct, and __clone were OR'd with additional constants. These three access modifiers are specific to the methods they're named for and should never be used anywhere else.