Implementing Classes

Table of contents:

As you start to explore the world of OOP, it's time to shake off some of the baggage you've collected in the chapters leading up to this point. To do that, "reset" back to the skeleton extension you started with in Chapter 5, "Your First Extension."

In order to compile it alongside your earlier incarnation, you can name this version sample2. Place the three files shown in Listings 10.1 through 10.3 in ext/sample2/ off of your PHP source tree.

Listing 10.1. Configuration File: 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

Listing 10.2. Header: php_sample2.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
 * Zend will use when loading this module
 */
extern zend_module_entry sample2_module_entry;
#define phpext_sample2_ptr &sample2_module_entry

#endif /* PHP_SAMPLE2_H */

Listing 10.3. Source Code: 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 you did in Chapter 5, you can issue phpize, ./configure, and make to build your sample2.so extension module.

Note

Like config.m4, your prior version of config.w32 will work here with nothing more than occurrences of sample replaced with sample2.

 

Declaring Class Entries

In userspace, the first step to defining a class is to declare it. For example:

As you can no doubt guess, this gets slightlybut only slightlyharder from within an extension. First, you'll need to define a zend_class_entry pointer within your source file similar to the le_sample_descriptor int you defined last chapter:

zend_class_entry *php_sample2_firstclass_entry;

Now, you can initialize and register the class within your MINIT method:

PHP_MINIT_FUNCTION(sample2)
{
 zend_class_entry ce; /* Temporary Variable */

 /* Register Class */
 INIT_CLASS_ENTRY(ce, "Sample2_FirstClass", NULL);
 php_sample2_firstclass_entry =
 zend_register_internal_class(&ce TSRMLS_CC);

 return SUCCESS;
}

Building this extension, and examining the output of get_declared_classes(), will show that Sample2_FirstClass is now available to userspace scripts.

Defining Method Implementations

At this point, you've only managed to implement stdClass, which is, of course, already available. You'll want your class to actually do something now.

To accomplish this, you'll fall back on another concept you picked up back in Chapter 5. Replace the NULL parameter to INIT_CLASS_ENTRY() with php_sample2_firstclass_functions and define that struct directly above the MINIT method as follows:

static function_entry php_sample2_firstclass_functions[] = {
 { NULL, NULL, NULL }
};

Look familiar? It should. This is the same structure you've been using to define ordinary procedural functions. You'll even populate this structure in nearly the same manner:

PHP_NAMED_FE(method1, PHP_FN(Sample2_FirstClass_method1), NULL)

Alternatively, you could have used PHP_FE(method1, NULL). However, as you'll recall from Chapter 5, this expects to find an implementation function named zif_method1, which might potentially conflict with another method1() implementation elsewhere. In order to namespace the function safely away from any procedural implementations, the class name gets prepended to the method name using drop cap-casing for the class name and camel-casing for the method name.

The PHP_FALIAS(method1, Sample2_FirstClass_method1, NULL) form is also acceptable; however, it may be slightly less intuitive when you come back later and wonder why there's no matching PHP_FE() line to go with it.

Now that you have a function list attached to your class definition, it's time to declare some methods. Create the following function above the php_sample2_firstclass_functions struct:

PHP_FUNCTION(Sample2_FirstClass_countProps)
{
 RETURN_LONG(zend_hash_num_elements(Z_OBJPROP_P(getThis())));
}

Now add a matching PHP_NAMED_FE() entry in the function list itself:

static function_entry php_sample2_firstclass_functions[] = {
 PHP_NAMED_FE(countprops,
 PHP_FN(Sample2_FirstClass_countProps), NULL)
 { NULL, NULL, NULL }
};

Note

Be sure to notice that the function is named for userspace in all lowercase. The case-folding operations meant to ensure case-insensitivity in method and function names require that internal functions be given all lowercase names.

The only new element here should be getThis() which, in all current PHP versions, is actually a macro that resolves to this_ptr. this_ptr, in turn, carries essentially the same meaning as $this within a userspace object method. If no object instance is available, such as when a method is called statically, getThis() will return NULL.

Just as the data return semantics in object methods is identical to procedural functions, so is the parameter acceptance and arg_info methodology:

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("!
You called an object method!
");
 RETURN_TRUE;
}

 

Constructors

Your class constructor can simply be implemented as any other ordinary class method, and the same rules will apply to internals as to userspace when it comes to nomenclature. Specifically, you'll want to name your constructor identically to the class name. The other ZE1 magic methods, __sleep() and __wakeup(), can be implemented in this manner as well.

Inheritance

Inheritance between internal objects in PHP4 is sketchy at best and should generally be avoided like dark alleys in a horror flick. If you absolutely must inherit from another object, you'll need to duplicate some ZE1 code:

void php_sample2_inherit_from_class(zend_class_entry *ce,
 zend_class_entry *parent_ce) {
 zend_hash_merge(&ce->function_table,
 &parent_ce->function_table, (void (*)(void *))function_add_ref,
 NULL, sizeof(zval*), 0);
 ce->parent = parent_ce;
 if (!ce->handle_property_get) {
 ce->handle_property_get =
 parent_ce->handle_property_get;
 }
 if (!ce->handle_property_set) {
 ce->handle_property_set =
 parent_ce->handle_property_set;
 }
 if (!ce->handle_function_call) {
 ce->handle_function_call =
 parent_ce->handle_function_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);
 }
 }
}

With this function defined, you can now place a call to it following zend_register_internal_class in your MINIT block:

INIT_CLASS_ENTRY(ce, "Sample2_FirstClass", NULL);
/* Assumes php_sample2_ancestor is an already
 * 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);

Caution

Although this approach to inheritance will work, it should generally be avoided as ZE1 simply wasn't designed to handle internal object inheritance properly. As with most OOP practices in PHP, the ZE2 (PHP5) and its revised object model is strongly encouraged for all but the most simple OOP-related tasks.



Working with Instances

The PHP Life Cycle

Variables from the Inside Out

Memory Management

Setting Up a Build Environment

Your First Extension

Returning Values

Accepting Parameters

Working with Arrays and HashTables

The Resource Data Type

PHP4 Objects

PHP5 Objects

Startup, Shutdown, and a Few Points in Between

INI Settings

Accessing Streams

Implementing Streams

Diverting the Stream

Configuration and Linking

Extension Generators

Setting Up a Host Environment

Advanced Embedding

Appendix A. A Zend API Reference

Appendix B. PHPAPI

Appendix C. Extending and Embedding Cookbook

Appendix D. Additional Resources



Extending and Embedding PHP
Extending and Embedding PHP
ISBN: 067232704X
EAN: 2147483647
Year: 2007
Pages: 175
Authors: Sara Golemon

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