Piotras' blog: Archive

2009-01-01 - 2009-01-31

ZEND_ARG_INFO reflections in midgard php bindings

Posted on 2009-01-16 12:42:20 UTC.

Yesterday I finished ZEND_ARG_INFO implementation support in PHP bindings for Midgard. I am happy as I closed another ticket, so this nice feature will be also available for Vinland. Yes, I must say this is nice feature. However, at first I was very unhappy how it's implemented in ZE2.

For simple class implmentation you may use ZEND_ARG macros, quite simply.

midgard_connection::open($name):

ZEND_BEGIN_ARG_INFO_EX(arginfo_midgard_connection_open, 0, 0, 1)
    ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()

But for dynamic classes, like Mgdschema ones, things are more complicated.

get_by_guid($guid) method:

{ NULL, 0, NULL, 0, 0, 0, 0, 0, 1 },
{ "guid", sizeof("guid")-1, NULL, 0, 0, 0, 0, 0, 0 },

Especially, if you take into account fact, that method's arguments are defined with the same structure as method itself. And of course (as usual), you can not read about this in ZE2 documentation. Just investigate and hack sources.

Anyway, what does it mean for Midgard? Better reflection and nice (almost perfect) way to introspect all Midgard core API via PHP classes.

Let's look at simple example (I used the same code, provided in PHP docs):

$method = new ReflectionMethod('midgard_user', 'password');
$p = $method->getParameters();

foreach ($method->getParameters() as $i => $param) 
{
    printf(
    "-- Parameter #%d: %s {\n".
    "   Class: %s\n".
    "   Allows NULL: %s\n".
    "   Passed to by reference: %s\n".
    "   Is optional?: %s\n".
    "}\n",
    $i, // $param->getPosition() can be used from PHP 5.2.3
    $param->getName(),
    var_export($param->getClass(), 1),
    var_export($param->allowsNull(), 1),
    var_export($param->isPassedByReference(), 1),
    $param->isOptional() ? 'yes' : 'no'
    );
}

And the output:

-- Parameter #0: username {
    Class: NULL
   Allows NULL: false
   Passed to by reference: false
   Is optional?: no
}
-- Parameter #1: password {
   Class: NULL
   Allows NULL: false
   Passed to by reference: false
   Is optional?: no
}
-- Parameter #2: hashtype {
   Class: NULL
   Allows NULL: false
   Passed to by reference: false
   Is optional?: yes
}

A real cool thing about such reflection is possibility to create one unified documentation system for code written purely in PHP and for that one written in C extension. If Zend introspection will allow this, we should have much better documentation with less amount of work. And this will be first cool thing provided by Zend :)

midgard_datetime

Posted on 2009-01-22 16:39:20 UTC.

I started to work on midgard_datetime class. Pure PHP class which should represent any property of Midgard MgdSchema object which holds datetime string. A pure, means that it focuses on PHP language only, and in fact, it's implemented in midgard extension.

Why midgard_datetime? And not just simple DateTime class, as suggested in related ticket? Answer is simple. PHP has limits. Imagine, every datetime property is propagated as DateTime object. And when you want to read it, you simply invoke format() method. But what to do when you need to change date or time? You invoke setDate() method and what next? Did underlying GObject's property value has been changed as well? No. There's no features like signal emission or simple event notification on PHP level. So, even if you write own callback, no single line of code will trigger it (As a decent framework Midgard supports events).

I wrote midgard_datetime class (DateTime derived) with own underlying C structure and assigned own hooks for it. All in all, at some point I need to know what is the object and what is the property a midgard_datetime holds value for. And this failed because DateTime objects hold own underlying C structures, so when all Zend OO related hooks return void type, you never know what you really get: DateTime structure, midgard_datetime one or maybe mather-in-law? Implementation simplified. But it doesn't mean it became more elegant. I had to register private properties. Yes, this is common for PHP code, but on C level you have armory of possibilities, and had to forget about it.

Examples

In Ragnaroek, you used to print datetime property:

echo $object->metadata->created;

and in vinland, what you wanted to print is an object, so you need to invoke its method:

echo $object->metadata->created->format("c");

midgard_datetime is automagically initialized with DateTimeZone object, so if you need timezone you invoke getTimezone method. Of course, the only one correct timezone for Midgard datetimes is UTC. And you can not change it.

For more flexibility, every DateTime (or derived one) class' object is converted to GValue which holds ISO 8601 formatted string, so ot only objects are "affected" by such conversion. You can pass such objects as Query Builder constraints' values:

$md = new midgard_datetime();
$md->setDate(2009, 01, 02);

$qb = new midgard_query_builder("midgard_article");
$qb->add_constraint("metadata.published", ">", $md);

Performance

I made two simple tests. Both have a loop (100 steps) and assign string datetime. First one uses:

$a = new midgard_article();
$b = $a->title;

and takes from 0,0060 to 0,0080 seconds. This test is almost the same as assigning datetime property the "old" way.

Second one uses midgard_datetime:

$a = new midgard_article();
$d = $a->metadata->created->format("c");

and takes from 0,0180 to 0,0230 seconds.

Looks like midgard_datetime implementation is up to 4 times slower. But as it extends DateTime, there's many features implemented "for free". And if you take into account, that first test requires additional function calls, like strtotime() and date() (to format output), midgard_datetime implementation will be up to 2 times slower. And I think it's not a big problem. midgard_datetime object is created on demand, only when you need to read property. And in practice, you do not need thousands of datetimes available at the same time.

Issues

MySQL doesn't work with constraint used in QB's example. It simply treats string verbatim, so SQL part looks like this:

WHERE metadata_published = '2019-01-01T00:00:00+00:00'

It works, if you remove timezone offset. So I believe fixing the issue is a matter of date formatting.

Also there's no possibility to convert datetime values (returned by collector for example) to midgard_datetime objects. Such values are initialized as GValue values of string type. And this should be fixed by wider midgard timestamp values usage in core.

You can install latest packages, if you would like to try those.

Back

Layout Copyright © 2006 Finnish Teleservice Center Ltd Oy - Site Powered by Midgard CMS