Using objects - obj_ptr, obj_rc
The zpp::obj_ptr wrapper class for a zend_object* does the expected. It has methods to call the object's methods, and read or write its properties.
Such an object may be a "special" callable object, such as an anonymous function, or object with an invoke method. Wcc\Services checks for this possibility on any named service. The obj_ptr callable methods exist for zero, one, or two arguments. They use the callable_fn method declared in fn_call.h, which can use any number of arguments.
// Wcc\\Services passes itself to callables as zend_object* first argument
val_rc Services::call_value(obj_ptr callme)
{
val_rc self(this);
return callme.callable(self);
}
// in Services::get
val_ptr value;
if (test.isCallable())
{
obj_ptr callme = test.zobject();
val_rc result = call_value(callme);
}
The obj_ptr call methods all take a str_ptr as first argument, the method name. Method overrides exist for up to four zval* arguments. Each uses a fn_call_args
Property values
Property values can be returned one argument to the property method, or set with two arguments. A return zval* argument can be used in property_get.
// zpp::obj_ptr get property methods
val_rc property(str_ptr key);
zval* property_get(str_ptr key, zval* ret);
zval* property_ptr(str_ptr key);
// set value
void property(str_ptr key, val_ptr value);
void property(str_ptr key, str_ptr value);
void unset_property(str_ptr name);
// exists?
bool has_property(str_ptr name);
//! property-values list
htab_rc properties();
make a self object
Like val_rc, obj_ptr has a constructor with a base_d* argument, which sets it to the zend_object* value held by the base_d* this->self_, which is the PHP $this. For convenience base_d class has a self() method.
obj_ptr self() const { return obj_ptr(self_); }
sharing efficient property access with C++
PHP byte code efficiency of objects depends on having known offsets for class properties. This is behind the move to deprecate use of dynamic properties in objects not specifically tagged as allowing them. Object property names are case sensitive. They are subject to typo errors. Error or warning exceptions occur when trying to set the value of a property name that doesn't exist in an object, when it is not configured for dynamic properties.
The PHP object class data creation C code can be generated from the .stub.php files, including all property names, types and qualifiers. Object instances should be able to get the zval pointer to these property values, and cache them as val_ptr as part of their C++ data, and so have the most efficient read and write to the property value. If the property is public or read-only, PHP also has efficient access to the same value.
Comparing different methods of named property access
Wcc PHP objects in C++ have an example of a dynamic property oriented class - Wcc\Config , and also Wcc\Hmap class, which internally uses a PHP array to store properties.
Accessing properties and array values by string keys is the mainstay of PHP coding. To compare the efficiency of various kinds of PHP property/value access by key name, I created a script that repeated a numeric sum and division ( a + b ) / a, and averaged the time taken over a large number of iterations. The following table shows the kind of entity, and results ratioed two of chosen methods, being just local variables, and object-declared property. Compared are local array with string keys, dynamic property objects, and object properties implemented as array storage, and a call to a function implemented as a method of Wcc\Pair, just for this purpose.
The ratio of averaged times to complete are formatted to 2 decimal places here.
| Implementation | X / Local variable (e) | X / fixed property access (a) |
|---|---|---|
| Local variables (e) | 1 | 0.6 |
| Wcc\Pair (C++) declared properties (a) | 1.66 | 1 |
| EmptyTest (PHP) declared properties (i) | 1.66 | 1 |
| Wcc\Pair call test_calc() (c) | 1.77 | 1.07 |
| Wcc\Pair (C++) methods get (b) | 3.36 | 2.03 |
| Wcc\Config dynamic properties | 1.78 | 1.08 |
| Use local array (g) | 2.14 | 1.29 |
| Extend stdClass (h) | 3.81 | 2.3 |
| Hmap property handler (f) | 2.92 | 1.76 |
| Hmap array handler (m) | 3.78 | 2.28 |
| ArrayObject as [array] (k) | 4.97 | 3 |
| ArrayObject ->Property (j) | 5.14 | 3.11 |
The results suggest the unsurprising find that using local variables is optimal, and object reference to declared properties take about 67% more time. For any none trivial calculation, a method call to compiled code (c) is good, for a trivial calculation, compared to calling a get method (b) 3 times while doing it locally.
Unknown is if PHP doing any optimisation for a common sub-expression used twice in the looped statements. All of short statement repeat processes are fast, since each of the 12 test functions timed a loop of 1000 iterations, and the whole sequence repeated 1000 times, which makes for 12 million iterations, and on a modern x86_64 processor, which took a few seconds.
Dynamic properties a little bit slower. Property handlers seem faster than array handlers. Except for the "ArrayObject" in its "ARRAY_AS_PROPS" setup, which lags the others, for reasons I cannot explain.
Wcc\Config and Wcc\Hmap are into the Wcc extension using the ZPP / base_d C++ classes, I can at least say for simple uses they are reasonably effective at basic get/set compared to ArrayObject.
For Wcc\Pairs , also in the Wcc extension, 3 function calls takes longer than
The script that produced this table is "tests/pairs.php" from the zphp2.git repository