SitePoint Sponsor

User Tag List

Page 5 of 5 FirstFirst 12345
Results 101 to 116 of 116
  1. #101
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Care to explain a bit?

  2. #102
    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)
    Quote Originally Posted by stereofrog
    Care to explain a bit?
    Sure.
    PHP Code:
    require_once('../simpletest/unit_tester.php');
    require_once(
    '../simpletest/reporter.php');
    error_reporting(E_ALL);

    function &
    single($klass) {
        static 
    $map = array();
        if(!isset(
    $map[$klass])) {
            
    $e=(($c=count($a=func_get_args()))<2)?''
                
    :'$a['.implode('],$a[',range(1,--$c)).']';
            eval(
    "\$map['$klass']=&new $klass($e);");
        }
        return 
    $map[$klass];
    }

    // playtoys
    // ids show that we have singletons
    class App {
        function 
    App(&$db, &$logger) {
            
    $this->id rand();
            
    $this->db = &$db;
            
    $this->logger = &$logger;
        }
        function 
    run() {
            
    var_dump($this);
        }
    }
    class 
    DbLogger {
        function 
    DbLogger(&$db) {
            
    $this->id rand();
            
    $this->db = &$db;
    }}
    class 
    DB {
        function 
    DB($dsn) {
            
    $this->id rand();
            
    $this->dsn $dsn;
    }}

    // Config is able to handle everything by itself.
    class Config {
        var 
    $dsn "mysql://user:pass@host/db";

        function &
    App() {
            return 
    single('App'$this->DB(), $this->Logger());
        }
        function &
    Logger() {
            
    $l = &single('DbLogger'$this->DB());
    //        $l = &single('DbLogger');
    //        $l->db = &$this->DB();
            
    return $l;
        }
        function &
    DB() {
            return 
    single('DB'$this->dsn);
        }
    }






    class 
    TestOfSingleton extends UnitTestCase
    {
        function 
    TestOfSingleton() {
            
    $this->UnitTestCase();
        }

        function 
    test_referential_integrity() {
            
    $conf =& new Config();
            
    $app =& $conf->App();
            
    $this->assertReference($conf->DB(), $app->db);
        }
    }

    $test = new TestOfSingleton();
    $test->run(new HtmlReporter()); 
    That's not an issue with php5 ofcourse.

  3. #103
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh that's ill. I'm sure someone will come with the fix though...

  4. #104
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    More in the spirit would be:

    PHP Code:
    DI::register(
        
    'gateway',
        
    '$gateway = new MyPaymentGateway($di->get("dbconn"));
         $gateway->setOrder($di->get("order"));
         return $gateway;'
    ,
         
    'app/gateways/MyPaymentGateway.php'); 
    But... I don't really like having all that code in quotes. In Ruby, great. In PHP, it feels half baked. In Ruby you have the auto-return, you don't need "return $gateway;" and "$gateway =" is optional.

    So, I'd say keep it simple by only doing constructor injection, then have this:

    PHP Code:
    $di->reg('gateway''MyPaymentGateway', array('dbconn'),
                        
    'app/gateways/MyPaymentGateway.php'); 
    With the last two params optional. I'd also have the third param auto wrapped in an array if there is only one dependency, so that this shorter version would be fine:

    PHP Code:
    $di->reg('gateway''MyPaymentGateway''dbconn'); 
    Douglas
    Are you going to post new code with these ideas incorporated. ghurtado, what do you think about Doug's simplified register? Do we need the full code block for register, or just a class and parameters? The latter assumes that if you had a code block, you would wrap it in a class. Is this acceptable?
    Christopher

  5. #105
    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 arborint
    Are you going to post new code with these ideas incorporated. ghurtado, what do you think about Doug's simplified register? Do we need the full code block for register, or just a class and parameters? The latter assumes that if you had a code block, you would wrap it in a class. Is this acceptable?
    I'm somewhat backtracking, the bulk of the code is back here on page two. Attached are the changes I've made with ideas from the thread. It even has something which pretends to be documentation.

    It could be simplified if you removed the ability to pass in paramaters to get. I've simplified my application so that I can use the class as a ServiceLoactor which happens to inject dependencies behind the scenes if need be, so I've renamed it Container instead of DI. It works fine as a plain registry too, which is helpful for me. As I'm not using the static access anymore, so a short name isn't as important as it was. The syntax looks more like this:

    PHP Code:
    $c =& Container::getInstance();
    $c->add('Checkout''MyCheckout', array('Store''Order'));
    $c->add('Store',    'MyStore',    array('Gateway'));
    $c->add('Order',    'MyOrder');
    $c->set('Gateway',  $test_payment_gateway); 
    The add method could be renamed reg or register, I didn't do it in the attached code because... I didn't have to. Still a shame about having to split add and set - silly PHP references. Any ideas on that?

    Edit: just added a simplified version without the null arguments support, and without autowrapping dependencies in an array, so you have to use an array even if you have only one dependency. It must be good, it has a KB less.

    Edit 2: removed unneeded reference, and added needed reference

    Douglas
    Attached Files Attached Files
    Last edited by DougBTX; Sep 14, 2005 at 02:38.
    Hello World

  6. #106
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As I recall, something like this will let you get rid of set():
    PHP Code:
    function register /* $name, $classname, $signature = array() */ ) {
       if (
    func_num_args() >= 2) {
           
    $args func_get_args();
            
    $this->registry[$arg[0]]['constructor'] = array(
                
    'classname' => $args[1],
                
    'signature' => $args[2]
            );
        }

    Christopher

  7. #107
    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)
    Quote Originally Posted by arborint
    As I recall, something like this will let you get rid of set():
    ...
    What ?

    PHP Code:
    function &call_constructor_array $type, &$array 
    The ampersand before $array isn't needed. Not that it matters, but I though I had to point it out.

  8. #108
    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 arborint
    As I recall, something like this will let you get rid of set()
    PHP Code:
    class Klass
    {
        var 
    $foo;
    }

    $klass = new Klass;
    $klass->foo 'bar';

    function 
    manipulate (  )
    {
        
    $args func_get_args();
        
    $klass =& $args[0];
        
    $klass->foo 'manipulated';
    }

    manipulate($klass);

    echo 
    $klass->foo
    Echos 'bar', doesn't seem to work with PHP4 references?

    Quote Originally Posted by kyberfabrikken
    The ampersand before $array isn't needed.
    Good point, thanks, removed (updating the attachments above...)

    Douglas
    Hello World

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

    Quote Originally Posted by Travis S
    You call register('KnownClass') and it looks up the interfaces it implements.
    Did you look up the Phemto code? It works like that too. Whilst you almost have to implement DI to understand it, watch out the thread doesn't just go around in circles.

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

  10. #110
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey Marcus,
    Quote Originally Posted by lastcraft
    Did you look up the Phemto code? It works like that too. Whilst you almost have to implement DI to understand it, watch out the thread doesn't just go around in circles.
    Yes... I did check out the Phemto code briefly as well as the PicoContainer. My problem is when I'm trying to get something that's new I'm better off implementing it myself at least once... It's that old habit of not wanting to use code that I can't fully explain I guess.

    And yes... My ObjectFactory does get a container passed into it to look up other dependencies which could get you in a segfault loop pretty quick. In my preview release, I don't worry about that as it's really up to the implementor to figure that out. I just want to give him plenty of rope to hang himself with

  11. #111
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by arborint
    Are you going to post new code with these ideas incorporated. ghurtado, what do you think about Doug's simplified register? Do we need the full code block for register, or just a class and parameters? The latter assumes that if you had a code block, you would wrap it in a class. Is this acceptable?
    So far it seems like the idea of also including the path to the class file has been pushed aside, which I think is best, since it seems to me it belongs in a different set of responsibilities.

    With Douglas' latest revision, it looks like we are back to an implementation which requires two separate registration methods: add and set. Personally I don't think that the difference between the two is that clear in the naming, but the issue could be solved by allowing add (or "register", I am still inclined to believe that name is more adequate to the purpose of the method) to receive objects as well as class names. After all, most of the time that you want to pass an object, I imagine you would call it as:
    PHP Code:
    $c->add('Checkout''MyCheckout', array('Store', new MyOrder())) 
    Since you are creating a new one, you wouldn't have a problem with references. Otherwise, if you plan to pass an object that was already preexisting, perhaps because you use the same one in another component registration call, then you would probably be better off by defining that third object as a component too anyway.

    The term "add" always makes me think of linear lists.

    PHP Code:
    $c->add('Checkout''MyCheckout1');
    $c->add('Checkout''MyCheckout2');
    $c->add('Checkout''MyCheckout3'); 
    If I were to see the above code in some foreign library I would probably think that three things just got added to some list.

    Another smaller issue that I see is that by going from create_function to eval(), now the "Eval'd" code is interpreted everytime that a new component is instantiated instead of only once during the registration call, though I am not sure what type of performance impact this may have.

    At the end of the day, I too prefer an API that allows you to work with parameters and arrays at registration time, instead of code blocks; I just wonder if for all the situations where you may need DI (many times dealing with 3rd party libraries which you would rather not rewrite) can be accounted for in Douglas' version. How would you do setter / property injection? I think this is an important point to allow the DI container to work with any preexisting Plain Old PHP Object.
    Garcia

  12. #112
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ghurtado
    So far it seems like the idea of also including the path to the class file has been pushed aside, which I think is best, since it seems to me it belongs in a different set of responsibilities.
    I think it is a trivial amount of additional code to make true Lazy Loading and not require some other mechanism such as __autoload(). But up to you guys.

    Quote Originally Posted by ghurtado
    With Douglas' latest revision, it looks like we are back to an implementation which requires two separate registration methods: add and set. Personally I don't think that the difference between the two is that clear in the naming, but the issue could be solved by allowing add (or "register", I am still inclined to believe that name is more adequate to the purpose of the method) to receive objects as well as class names.
    Why not just call it what it is
    PHP Code:
     $c->register('Checkout''MyCheckout', array('Store', new MyOrder());

    // and

     
    $c->registerObject('Checkout', new MyCheckout('Store', new MyOrder())) 
    Christopher

  13. #113
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    PHP Code:
    $c =& Container::getInstance();
    $c->add('Checkout''MyCheckout', array('Store''Order'));
    etc 
    Unfortunately, you lost all track of referential integrity.

    PHP Code:
    class {
        function 
    A(&$b) {
            
    $this->= &$b;
    }}
    class 
    {}

    $c = &Container::getInstance();
    $c->add('A''A', array('B'));
    $c->add('B''B');

    $aa = &$c->get('A');
    $b0 = &$aa->b;
    $b1 = &$c->get('B');

    $b0->99;
    assert($b1->== 99); // :(((( 
    It must be good, it has a KB less.
    As said, actually no scaffolding code is necessary (except not-that-tiny singleton wrapper)
    PHP Code:
    // carping permitted
    function &single($klass) {
        static 
    $map = array();
        if(!isset(
    $map[$klass strtolower($klass)])) {
            
    array_shift($a func_get_args());
            foreach(
    $a as $k => $v)
                if(
    is_object($v) && isset($map[$c strtolower(get_class($v))]))
                    
    $a[$k] = &$map[$c];
            
    $e '';
            if(
    $c count($a))
                
    $e '$a[' implode('],$a['range(0$c 1)) . ']';
            eval(
    "\$map['$klass']=&new $klass($e);");
        }
        return 
    $map[$klass];

    PHP Code:
    class Conf {
        function &
    A() {
            return 
    single('A'$this->B());
        }
        function &
    B() {
            return 
    single('B');
        }
    }

    // A and B see above

    $c = new Conf;

    $aa = &$c->A();
    $b0 = &$aa->b;
    $b1 = &$c->B();

    $b0->99;
    assert($b1->== 99); // :) 

  14. #114
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Simplification by changing the problem, can't complain too much about that (and I've taken your better implode code too!)

    Patch this:

    PHP Code:
    $object =& call_constructor_array($classname$dependencies);
    return 
    $object
    becomes:

    PHP Code:
    $sevice['object'] =& call_constructor_array($classname$dependencies);
    return 
    $sevice['object']; 
    Or you don't get the same objects back out again. Thanks for the testcase - got any more?

    Cheers,
    Douglas
    Attached Files Attached Files
    Hello World

  15. #115
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah, that works now, good job.
    Simplification by changing the problem
    Care to explain? What exactly "Container" code can and "Conf" code cannot?

  16. #116
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The main thing for me me is that the only things you can put in the "Conf" registry (== $map) are instances of objects which have had their dependencies injected, you can't have any objects in there that were constructed somewhere else.

    That means you can just have the static $map; in the same function as where you gather dependencies, shaving off a few lines of code, which is a good thing if you don't want to use $map as a normal registry.

    Keeping that $map tied up also makes it harder to have the clear() method, and you can't have multiple instances of the registry. Again, both of those let you shave off a few lines of code.

    The last thing that affects me is that it makes it harder to have multiple sets of "Conf" code. If you register something in one place, and then register something which depends on it later, you'll have to use different code to resolve those dependencies. It is scaffolding code that you've got to add each time you need it, rather than something built into the container.

    I consider the bits in red to be scaffolding I'd rather not have, though you do need to have some, so I'd like as little as possible:

    Code:
    class Conf { 
        function &A() { 
            return single('A', $this->B());
        } 
        function &B() { 
            return single('B'); 
        } 
    }
    Code:
    $c = &Container::getInstance(); 
    $c->add('A', 'A', array('B')); 
    $c->add('B', 'B');
    Code:
    DI::add('A', 'A', array('B')); 
    DI::add('B', 'B');
    Though only the second syntax lets me have multiple instances of the container.

    Douglas
    Hello World


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
  •