SitePoint Sponsor

User Tag List

Results 1 to 17 of 17
  1. #1
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    Mad idea - overload as mini domain language

    Hi.

    This comes out of an idea to use the overload capability in PHP5 for writing terser, declarative statements. In particular I have been rethinking the mock objects interface to Simpletest. For quite a while I have wanted to define a mock like so...
    PHP Code:
    interface Doubler {
        function 
    double();
    }

    class 
    TestOfStuff extends UnitTestCase {
        function 
    testStuffUsingDoubler() {
            
    $this->MockDoubler->double->expect(13)->give(26);

            
    $stuff_with_doubler = new Stuff(new MockDoubler());
            ...
        }

    This should be possible in PHP4, but as soon as I use the overload() within SimpleTest everything breaks. Thus it would have to be introduced in a PHP5 only version .

    The main point is that a lot of magic can happen in that mock declaration. This really will be all of the syntax. Compare with the situation now where a generate call has to be made, then the mock created and then an expectation set on it and finally thereturn value is set. I've gone from five lines to two. This way works with dependency injection too.

    It can be extended to multiple instances and multiple method calls like so...
    PHP Code:
    $this->MockDoubler(1)->double->at(1)->expect(13)->give(26); 
    Basically it has become a declarative language. Within the __get() method there will likely be a bunch of context objects flying around, but that's just implementation. Probably a not too difficult one at that. I've done some experiments and all seems to be well. So much so I'd liketo explore it further.

    How about an ActiveRecord subclass like this...
    PHP Code:
    class Person extends ActiveRecord {
        function 
    __construct($transfer false) {
            
    $this->varchar(255)->given_name->not_null;
            
    $this->varchar(255)->family_name->not_null;
            
    $this->varchar(2)->iso_country('uk');
            
    $this->ContactEmail->emails;
            
    $this->key('given_name''family_name')->unique;
            
    parent::__construct($transfer);
        }
    }

    class 
    ContactEmail extends ActiveRecord { ... } 
    Now I'm using the domain language to specify types in a database. The $transfer object is the incoming data when the object is instantiated from a finder. The values in brackets are the defaults, the "uk" here. If a class type is given, then it will be a collection of other records.

    To set up a new one...
    PHP Code:
    $transaction = new MysqlTransaction();

    $marcus = new Person();
    $marcus->first_name 'Marcus';
    $marcus->family_name 'Baker';
    $marcus->emails->add->address 'marcus@lastcraft.com';
    $marcus->save($transaction);

    $transaction->commit(); 
    The actual data types are negotiated on the save() call. The MysqlTransaction can be an abstract factory of type mappers as well as holding the connection information.

    Implementation aside, I think it has a lot in common with the ruby style small domain languages.

    I bet there are other uses too.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  2. #2
    SitePoint Zealot
    Join Date
    Oct 2004
    Location
    naperville
    Posts
    189
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would personally kill for a version of ST that supports mock call that way. It would make tests "look" a lot cleaner....

  3. #3
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    PHP Code:
    $transaction = new MysqlTransaction();

    $marcus = new Person();
    $marcus->first_name 'Marcus';
    $marcus->family_name 'Baker';
    $marcus->emails->add->address 'marcus@lastcraft.com';
    $marcus->save($transaction);

    $transaction->commit(); 
    I bet there are other uses too.

    yours, Marcus
    Can't help myself:
    PHP Code:
    $sql_statement->select('*')->from('Person')->where('first_name')->equals('Marcus'); 
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  4. #4
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

  5. #5
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dagfinn
    Can't help myself:
    PHP Code:
      $sql_statement->select('*')->from('Person')->where('first_name')->equals('Marcus'); 
    Like the eZ systems guys are doing here

  6. #6
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi.

    The languages could be generated automatically. You take a BNF syntax, add some handlers and place the resulting langauge into a base class. Subclasses then have the domain language available.

    Note that I am talking here not about method chaining, as that is pretty static. I am talking about the langauge being extended dynamically. Adding new methods/variables on the fly in effect. You get something more like a human readable programming language.

    You could probably do something like rake (uby make) this way.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  7. #7
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sounds like you want to break out classkit ?

    http://uk.php.net/classkit


    Er, runkit even http://uk.php.net/manual/en/ref.runkit.php

  8. #8
    SitePoint Guru BerislavLopac's Avatar
    Join Date
    Sep 2004
    Location
    Zagreb, Croatia
    Posts
    830
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I knew I was somewhere the ability to create custom superglobals! Thanks for reminding me.

    Does that mean that runkit is installed by default? If so, I could actually make the $_APPLICATION level persistence I had in mind for PHP5 (using sqlite, of course, and easily replaceable by any other sql db).

  9. #9
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That's a seriously cool idea.
    Quote Originally Posted by BerislavLopac
    Does that mean that runkit is installed by default?
    IIRC this is no longer the case with PHP5.

  10. #10
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by BerislavLopac
    I knew I was somewhere the ability to create custom superglobals! Thanks for reminding me.

    Does that mean that runkit is installed by default? If so, I could actually make the $_APPLICATION level persistence I had in mind for PHP5 (using sqlite, of course, and easily replaceable by any other sql db).
    There was some discussion on php internals about doing something like this. Was a thread regarding PHP6 feature requests, and $_APPLICATION came up,
    with people suggesting runkit, shm/apc/...

    Anyways back on topic..

    Finally gotten my head around the orginal idea, so what a sort of automaton/state machine, with __get() calls feeding the tokens, the machine performing actions, and shifting to new state?

  11. #11
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Without __get support, and you need to be polite!

    PHP Code:
    class HashCall {
        
        protected 
    $caller;
        protected 
    $buffer = array();
        
        function 
    __construct($caller$method$arguments) {
            
    $this->caller $caller;
            
    $this->__call($method$arguments);
        }
        
        function 
    __call($method$arguments) {
            
    $this->buffer[$method] = $arguments[0];
            return 
    $this;
        }
        
        
    /* Be polite, or your method won't get called! */
        
    function please() {
            
    reset($this->buffer);
            return 
    call_user_method('_'.key($this->buffer), $this->caller$this->buffer);
        }
        
    }

    class 
    SQLObject {
        
        function 
    _select($arguments) {
            
    $expected = array('select' => '*''from' => 'all');
            echo (
    $arguments == $expected) ? "Pass" "Fail";
        }
        
        function 
    __call($method$arguments) {
            return new 
    HashCall($this$method$arguments);
        }
        
    }

    $sql = new SQLObject;
    $sql->select('*')->from('all')->please(); 
    Last edited by DougBTX; Dec 3, 2005 at 23:22.
    Hello World

  12. #12
    SitePoint Zealot
    Join Date
    Oct 2004
    Location
    naperville
    Posts
    189
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This simple, or am I missing somethin?

    PHP Code:
    class Request {     
        function 
    __call($method$args)
        {
            echo 
    '<hr />' $method '<br />';;
            
    print_r($args);
            return 
    $this;
        }
    }

    $R = new Request;
    $R->filter('var')->from('get')->is('int')->required(false)->match('#pattern#'); 
    Some sort of filter buffer to keep track of the current variable.

  13. #13
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Super Phil
    This simple, or am I missing somethin?
    Some sort of filter buffer to keep track of the current variable.
    Hello World

  14. #14
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PHP Code:
    $R->filter('var')->from('get')->is('int')->required(false)->match('#pattern#'); 
    I'm sorry, but that looks completely and utterly unreadable. It's unbearable in fact

    I'm proberly going to get lamped for this, but the whole idea is bordering on stupidity...

  15. #15
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    PHP Code:
    $R->filter('var')->from('get')->is('int')->required(false)->match('#pattern#'); 
    I'm sorry, but that looks completely and utterly unreadable. It's unbearable in fact
    I agree.

    I'm proberly going to get lamped for this, but the whole idea is bordering on stupidity...
    The idea is great. That doesn't mean it will be great if the execution is sub-par. While the above example reads poorly, this I find nice:
    PHP Code:
    $SomeMock->someMethod->expect('foo')->give('bar'); 

  16. #16
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Martin Fowler writes about "fluent interfaces":

    http://martinfowler.com/bliki/FluentInterface.html
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  17. #17
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dagfinn
    PHP Code:
    $sql_statement->select('*')->from('Person')->where('first_name')->equals('Marcus'); 
    I was so sure I was kidding when I wrote this. Now it turns out that ezComponents does almost exactly the same thing. See:
    http://www.schlitt.info/applications...interface.html
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •