There and Back Again

ZActiveRecord can’t work?

In the webcast the following active record example was shown. This looks nice and slick but I don’t see how its possible, at least in php 5.1. There is no way to get the class name of the child in in the find all method.

<?php

< ?php 
 
class Person extends ZActiveRecord {}     
 
$people = Person::findAll(array('nameFirst' => 'Daniel')); 
 
foreach ($people as $person) 
{ 
    echo $person->nameFirst . "n"; 
} 
 
?>

?>

This example shows how get_class doesn’t help it always returns parentstatic

<?php

< ?php
class parentstatic {
    static function findAll()
    {
        echo get_class();
    }
}
class childstatic extends parentstatic {
}
echo parentstatic::findAll();
echo childstatic::findAll();
?>

?>

Anyone have a clue on how Zend is planning to get around this problem. From what I can tell its impossible and I haven’t seen anything on 5.1.2dev that would solve the problem but I could just be missing something and there is an easy way around the problem. To me it looks like PHP forces you to do a none static activerecord implmentation.

Update:
This also came up in the comments of Chris’s post about the webcast.

Update after comments from Mike Naberezny:
Webcast showed what Zend hopes will be the final syntax, they are working on a PHP engine level solution, but since they want to work with current php5 installs we will see some other solution as well. Of course the code with work with non-static usage, though thats not as nice Semantically.

47 thoughts on “ZActiveRecord can’t work?

  1. Mike Naberezny

    Hi Joshua,

    This is a problem that we have been aware of for a while (long before the webcast) and have been working towards a solution. Semantically, the static usage is the most desirable. During the webcast, we wanted to present what we hoped would be the final end usage. With that said, some things have changed even since that time, e.g. we’ve simplified the usage of ZSearch quite a bit based on feedback from the webcast.

    Call stack introspection almost solves this problem. I say almost because it relies on your storing your classes on disk in a specific way and looking at the filenames on the stack. We played with this for a very short time but didn’t get too far into it before deciding we didn’t want to force anything like that on the user.

    We’ve been working on the solution to the problem with statics on the C front, so eventually it will be solved there. However, this conflicts with the goal of the Zend Framework that it has to work on the majority of existing PHP 5 installations, so it is true that for installations without the enhancement, the methods will have to be called on the instance. We will try to find the best compromise for everyone.

    I hope this answers your question. Comments and questions are always welcome at framework-feedback@zend.com.

    Regards,
    Mike

  2. Ren

    Given that they are apparently targetting 5.0 version of PHP, can’t see how it can be done.

    Unless..

    ;)

  3. Ren

    Ah bugger, code got stripped from my code..

    Try again.

    class ZActiveRecord
    {
    static function findAll()
    {
    $lines = file(__FILE__);
    $trace = debug_backtrace();
    $line = $lines[$trace[0]['line'] – 1];
    preg_match(‘@[a-z_][a-z0-9_]*(?=::findAll)@i’, $line, $matches);

    echo $matches[0];
    }
    }

    class Person extends ZActiveRecord
    {
    }

    Person::findAll();

  4. Ren

    Yes, his comment wasn’t up when I posted. I was not being entirely serious proposing it as a solution as it seems a very bad idea.

  5. Lukas

    s/php5/php6 :-)

    supposedly we will finally get something run-time evaluated “static::” in php6 .. a real pity that it did not happen earlier as I, and others, have noticed this severe limitation for inheritance and static methods ages ago when php5 was still not stable :-(

  6. Pingback: Dreaming of Dawn » Blog Archive » Annoying statics in php

  7. spajus

    They can simply do some __get / __set magic for this purpose, even though Andi stated it will be avoided.

  8. Marcus Baker

    Hi.

    Why on earth are the Zend developers so keen to run the thing on static calls? Are people having trouble leaving their procedural past behind? How the hell are you going to mock out persistence for testing, for example?

    yours, Marcus

  9. Troy K

    Static calls make it nice for things like FindAll because creating an instance just to return another instance is lame.

    Beyond that, I think Zend is making a mistake and falling into the RoR hype. PHP doesn’t need Yet Another Framework to achieve their goal of enhancing the PHP ecosystem. They should be focusing on fixing PHP’s quirks (such as the static bug) and creating components. ZMail, ZSearch, etc, are good ideas, but why in the hell wrap them in a framework?

    And doing a webcast showcasing the very things that people try in the real world (static factories) and end up hacking around isn’t the brightest move either.

  10. spajus

    it could be like that (pseudo example..):

    class ZActiveRecord
    {
    static function findAll($params = array())
    {
    $return = array();
    $data = ZDB::getArray(‘select * from ‘ . self::table . ‘ where ‘ . $generated_code, $params);
    foreach ($data as $entry)
    {
    $p = new $child_class();
    $p->absorb($entry);
    $return[] = clone $p;
    }
    return $p;
    }

    function hydrate($array)
    {
    $this->data = $array;
    }

    function __get($p)
    {
    return $this->data[$p];
    }

    function __set($p, $v)
    {
    $this->data[$p] = $v;
    }
    }
    class Person extends ZActiveRecord {}

    $people = Person::findAll(array(‘nameFirst’ => ‘Daniel’));

  11. Joshua Eichorn Post author

    Stenfano:
    I think your missing the actual problem:

    class myClass {
    function getClass(){
    $a=new self();
    return get_class($a);
    }
    var $var = ‘not interesting';
    }

    class childClass extends myClass {
    }
    echo myClass::getClass();
    echo “
    “;
    echo childClass::getClass();

    outputs:
    myClass
    myClass

  12. Marcus Baker

    Hi…

    Troy K Says “Static calls make it nice for things like FindAll because creating an instance just to return another instance is lame”.

    OK, I’ll try again – why is it lame? This code is as old as the hills…

    $finder = new PeopleFinder();
    $people = $finder->findAll();

    It’s actually the norm in enterprise apps, it’s called the Aggregate pattern (Domain Driven Design – Eric Evans). What makes this code more practical is that the finder and the people are likely to be instantiated in different parts of the application. Modern development techniques, such as dependency injection and mock objects, both work with this approach.

    Static methods are runts. They rob OO of it’s single most powerful feature – polymorphism. That really is lame.

    yours, Marcus

  13. spajus

    well, i don’t really get the point here guys. the fact that it is doable one way or another is obvious. ZActiveRecord is simply an ORM. let’s take http://propel.phpdb.org for instance. it does such stuff aswell. mindless a few differences – xml schemas for database, code generation and complex query builders. xml schemas will be eliminated with database introspection, code generation may be avoided aswell. i’ve done similar things myself in my own code even: http://www.audio-massive.com/claw/docs/reference/clawmodel

  14. Joshua Eichorn Post author

    Spajus:
    The point isn’t that an ActiveRecord can’t be just that Zend has oddly been showing impossible syntax in the few bits of code we’ve seen.

    inheiritance in statics in PHP is a bit odd, you can always use a PeopleFinder type solution, though it does make the standard use cases more complicated.

  15. Pingback: Building a culture of objects in PHP | Professional PHP

  16. Michal Przytulski

    Hi, i have this same problem on coding my active record implementation, first solution what i was use was, adding to all child classes method like this:
    public static function find(){
    $args = array_marge(array(__CLASS__), func_get_args());
    return call_user_func_array(array(parent, ‘find’), $args);
    }

    but anyone see that this isn’t got idea, I lookup on internet without any solutions of this problem, I start looking for some implementations of active record in php or other languages, I start looking how this is implemented in RoR (I start implementing AR when frend show mi this solution in RoR), now in 3th relise of my implementation AR i use a CLI script witch create two files a base object definition class, and extended object this look like this:

    getObjectType();
    return new $returnType();
    }

    }

    abstract class UserDescription implements ActiveRecordDescription {

    const TYPE = ‘User';

    public function getObjectType(){
    return self::TYPE;
    }

    }

    ?>

    this is simple example witout any security check’s or validation, I write it ad hoc, without chack that will works – I only show how I resolve this problem, right now.
    Description class i genereted with script ./acObject User author=”Michal Przytulski” this script is connecting to DB and gets any information abouth table – column’s list, relations, primary keys etc. all this information is used in futer use of this implementation, now i can make joins with oter tables (inner, outer), find by pk, or by any filter, join with relation many to many with join table by transparenty join’s, make update, create, delete (with autodetect virtual delete by setting deletedTs column), and many more, results.

  17. Douglas Livingstone

    > OK, I’ll try again – why is it lame?

    In this case, it is lame because it isn’t the way “hip” Rails does it. Except for the fact that you can do this in Ruby:

    finder = PeopleFinder
    people = finder.find :all

    which you can’t do in PHP. So yes, you’re right, trying to use static access is bad in PHP, but only because PHP isn’t Ruby.

    Personally, I’m all for making PHP more like Ruby.

    Douglas

  18. Douglas Livingstone

    Just to clarify the point above, when you say:

    > Static methods are runts. They rob OO of it’s
    > single most powerful feature – polymorphism.
    > That really is lame.

    That is only because PHP/Java do not allow polymorphic static access – I know you’ve been doing some Ruby coding, I’d expect you to notice the difference between an OO limitation and a language limitation when you see one!

    Regards,
    Douglas

  19. Jake Grimley

    Agreeing with comments above, I think that this is mostly being done to achieve a parity with Rails’ ActiveRecord implementation. One of the nice things about Rails ActiveRecord is that you have a set of static methods for doing simple things like counting the number of records in a table that match a criteria. Yes it’s a lot like procedural programming, but you often want to use these methods in an off-the-cuff manner.

    Personally I like using static methods to instantiate and test objects, and there’s a long history of doing this with PHP classes, going back to PEAR’s DB…

    Anyway, this isn’t quite so elegant, but you could always do:

    $people = ZActiveRecord->findAll( ‘Person’, array( ‘nameFirst’=>’Daniel’ ) );

    I would find this quite appealing as there just seems something WRONG about instantiating a ‘person’ in order to find all ‘people’.

  20. Jose Gandullia

    Actually, it is possible to implement as shown originally.
    In PHP4 I discovered that when you call a method statically from within another method, inside the static method, $this is available and points to the object that called the static method. This is undocumented & probably by accident and not intentional. My guess is it was done to pass $this to parent::whatever(). I haven’t tested to see whether the same still holds true in PHP 5.1, but my guess is that it does.

    f1()
    }
    }

    $a = new a();
    $a->f1();

  21. Jose Gandullia

    oops stripslashes removed most my code:

    class a
    {
    function f1()
    {
    b::f2();
    }
    }
    class b
    {
    function f2()
    {
    // $this points to $a if called from $a->f1
    }
    }

    $a = new a();
    $a->f1();

  22. Joshua Eichorn Post author

    Jose:
    Depending on how you code this bug still works in php5, but if a function is actually marked static then $this is no longer available.

    However thisn’t doesn’t solve the problem its just the context of the old class falling through, if $this doesn’t already exist its not automatically created.

    class ZActiveRecord
    {
    function findAll($params)
    {
    var_dump(get_class($this));
    }

    }

    class Person extends ZActiveRecord {
    }

    Person::findAll(array(‘nameFirst’=>’Josh’));

    Returns:
    bool(false)

  23. Jose Gandullia

    Oh, of course.

    When I coded my own DB Abstraction layer & class generator, I got around this by having singleton table objects in addition to row objects.

    Then any static methods on the table class could internally instantiate the table class if necessary.

    So it would come down to something like:
    Person_Table::findAll(array(‘nameFirst’=>’Josh’));

  24. Jose Gandullia

    or maybe:

    Person::getTable()->findAll(array(‘nameFirst’=>’Josh’));

    not all that pretty, but OOP wise seems decent.

  25. Jose Gandullia

    wait, this whole singleton table object could be behind the scenes, then back to the original syntax.

  26. Joshua Eichorn Post author

    Jose: the problem is you have no way of knowning which singleton class to use.

    As far as I can tell each static method has to be overridden to give it the class name. As far as i can tell the best option is:

    ZActiveRecord::finder(‘Person’)->findAll(‘nameFirst’=>’Josh’);

    Assuming you don’t want to have to define a PersonFinder and a Person class to make things work.

  27. Pingback: Mechanism Alley » Blog Archive » Zend Collaboration?

  28. Pingback: Digital Sandwich

  29. Pingback: Madeblog » Blog Archive » ActiveRecord Angst… ReActivated.

  30. Pingback: Madeblog » ActiveRecord Angst… ReActivated.

  31. Pingback: Made Media Ltd » ActiveRecord Angst… ReActivated.

  32. Pingback: chazmeyers.com/blog » Broken static methods in PHP make me sad.

  33. Pingback: Über ein neues, halbes Feature in PHP 5.3 im WEBTEAM Weblog

  34. Pingback: Josh on the Web » Blog Archive » Exploring PHP’s static scoping