SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 52

Hybrid View

  1. #1
    SitePoint Wizard
    Join Date
    Jan 2005
    Location
    blahblahblah
    Posts
    1,447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    questions about phemto

    Hello,

    I'd like to ask a few questions about phemto in this thread.


    Let's consider the following code:

    PHP Code:
    class Foo
    {


      public function 
    doSomething($var)
      {
       
        require_once(
    'Helper.php');
        
    $h = new Helper();
        
    $var $h->lowerCase($var);
        return 
    $var;
        
      }


    How would phemto handle this situation?

    Regards

    -jj.

  2. #2
    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)
    (I know you directed this question towards Marcus, but since I've used Phemto recently, I figured I could open the ball)

    Phemto is a container, that can instantiate objects (A kind of factory really). In its simplest form, you can use it to instantiate your class Foo like this:
    PHP Code:
    // configure the container
    $container = new Phemto();
    $container->register('Foo');

    // Create an instance of Foo
    $foo $container->instantiate('Foo');

    // test that it works
    assert($foo instanceOf Foo); 
    If this seems verbose and pointless, remember that you will usually do the configuration in one place, but use the container in another. In a moment, you'll see how a container helps with that.

    In this example, the class Foo depends on the class Helper. This is known as a concrete class dependency, and is generally considered a weakness. The first thing you would want to do, is decouple this relationship, so that Foo depends on Helper's interface, but not the concrete implementation.

    You can do this by defining an interface HelperInterface, and let Helper implement it:
    PHP Code:
    interface HelperInterface {
      function 
    lowerCase($var);
    }

    class 
    Helper implements HelperInterface {
      function 
    lowerCase($var) {
        return 
    strtolower($var);
      }

    (Note: Since this is PHP, you don't strictly need to declare an interface, but to keep things simple, just play along here)

    Now that we have separated interface from implementation, we can rewrite our Foo class:
    PHP Code:
    class Foo {
      protected 
    $helper;
      public function 
    __construct(HelperInterface $helper) {
        
    $this->$helper;
      }
      public function 
    doSomething($var) {
        
    $var $this->h->lowerCase($var);
        return 
    $var;
      }

    This solves the problem of coupling; We can now replace Helper with something else, should we need to. There are other benefits than just this, but let's leave that for the time being.

    It leaves us with a different issue though. Since Foo now takes an argument to the constructor, you need to change the places, where you instantiate it.

    Luckily we're using Phemto, and it is smart enough to realise that the requirements of Foo has changed. However, before it can satisfy the abstract requirement (Foo needs a HelperInterface), it must know which concrete manifestation of HelperInterface to use:
    PHP Code:
    // configure the container
    $container = new Phemto();
    $container->register('Foo');
    $container->register('Helper');

    // Create an instance of Foo
    $foo $container->instantiate('Foo');

    // test that it works
    assert($foo instanceOf Foo); 
    Notice how the code that instantiates Foo doesn't need to know which dependencies it has. This is tugged away inside the container. This makes it easy to change code, without any impact on the code that uses it.

  3. #3
    SitePoint Enthusiast
    Join Date
    Jun 2007
    Posts
    27
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Very good explanation kyber, thx for that.

    I created something similiar to Phemto aswell, but I faced one problem I couldnt solve. I am really interested if Phemto can handle that.
    Lets imagine there is another Class Bar like this:

    PHP Code:
    class Bar
    {
      public function 
    __construct(HelperInterface $helper)
      {
         
    $this->helper $helper;
      }

    Now lets assume we have two helper that implement HelperInterface, and Foo needs the one, while Bar needs the other. Is there any way we can tell Phemto to use the one helper at Foo and the other helper at Bar?

  4. #4
    ********* 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 myc View Post
    Is there any way we can tell Phemto to use the one helper at Foo and the other helper at Bar?
    Why would you want to do that? I am sure you have a ligitimate reason of course, it's just that I didn't think it would be needed.

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

  5. #5
    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 myc View Post
    Now lets assume we have two helper that implement HelperInterface, and Foo needs the one, while Bar needs the other. Is there any way we can tell Phemto to use the one helper at Foo and the other helper at Bar?
    This is something that have bothered me as well. The simple answer is that this is a matter of granularity; If you need multiple implementations of your interface, it means that the interface isn't accurate enough. You can create new interfaces that extends the common interface.

    I think there is a little more to it than that, though.

    To introduce a finer-grained interface, you would have to modify both the depending class and the dependee. This seriously cripples the purpose of the container in the first place - You may not have control over the source code for both parts. Besides, this can only be done at compile time, since the dependencies are tied to a static class system.

    A solution to this limitation could be to allow the user to configure a specific instance to use for a specific dependency. Eg.:
    PHP Code:
    interface MailGateway {
      function 
    sendMail($to$message);
    }

    class 
    SmtpMailGateway implements MailGateway {
      function 
    sendMail($to$message) {
        
    // todo: mail
      
    }
    }

    class 
    LoggingMailGateway implements MailGateway {
      protected 
    $mailgateway;
      function 
    __construct(MailGateway $mailgateway) {
        
    $this->mailgateway $mailgateway;
      }
      function 
    sendMail($to$message) {
        
    // todo: log
        
    return $this->mailgateway->sendMail($to$message);
      }
    }

    class 
    Spammer {
      protected 
    $mailgateway;
      function 
    __construct(MailGateway $mailgateway) {
        
    $this->mailgateway $mailgateway;
      }
      function 
    spam() {
        
    $this->mailgateway->sendMail('somebody@example.org''Buy viagra!');
      }
    }

    $container = new Phemto();
    $container->register('Spammer');
    $container->register('LoggingMailGateway')->using('SmtpMailGateway');

    $spam $container->instantiate('Spammer');

    // Which is the same as:
    $spam = new Spammer(new LoggingMailGateway(new SmtpMailGateway())); 
    Adding this to Phemto should be fairly simple, I suspect?

  6. #6
    SitePoint Enthusiast
    Join Date
    Jun 2007
    Posts
    27
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I really like your solution, it looks nice and clean and (after a rough view) without any disadvantages.

    I edited the rest of my post since it was non-sense (I shouldnt post at 4am .. )

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

    I was thinking this...
    PHP Code:
    $injector->for('LoggingMailGateway')->register('SmtpMailGateway'); 
    ...and yes, this is do-able. It will probably double the code size, but given it's less than 200 lines anyway, this shouldn't be a problem .

    I am also thinking of renaming "register" into "use"...
    PHP Code:
    $injector->for('LoggingMailGateway')->use('SmtpMailGateway'); 
    I am also thinking of removing the need for "register"/"use" if there is only one choice amongst the available classes anyway. Is this too much automagic?

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

  8. #8
    SitePoint Wizard
    Join Date
    Jan 2005
    Location
    blahblahblah
    Posts
    1,447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you very much for this explanation.

    remember that you will usually do the configuration in one place, but use the container in another.
    Where would be the ideal place to do the configuration? Could you elaborate on the separation between the configuration and the actual use of the container (e.g. where will the container typically be used)?


  9. #9
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by jjshell View Post
    Where would be the ideal place to do the configuration? Could you elaborate on the separation between the configuration and the actual use of the container?
    Configuration generally ought to be in a single, well-known location so make one, somewhere. What I do is include a file with a bunch of register calls:

    PHP Code:
    $this->injector
        
    ->register('Foo')
        ->
    register('Bar')
        ->
    register(...)
        ->
    register(...)

    Like any config file you could add in comments to explain the choices where this is helpful:

    PHP Code:
    $this->injector
        
    /*
            If it's a full moon select an implementation
            which fires silver bullets.
        */
        
    ->register('Foo')
        
    /*
            Anything with a public garlic property. 
        */
         
    ->register('Bar')
        
    /*
            ...
            ...
        */
         
    ->register(...)

    Quote Originally Posted by jjshell View Post
    where will the container typically be used?
    An instance of phemto won't appear very often (it tends to do everything behind the scenes) but will pop up wherever you specifically need to pull an object out. Typically that will be somewhere near the "top" because it's often used to wire up the main object graph but you can use it anywhere or even have multiple containers for different purposes. Anywhere you want to drop little bombs of logic into the app.

  10. #10
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    use might end up a reserved keyword in 5.3.

  11. #11
    SitePoint Evangelist tetsuo shima's Avatar
    Join Date
    Oct 2005
    Location
    Switzerland
    Posts
    597
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,
    Quote Originally Posted by Selkirk View Post
    use might end up a reserved keyword in 5.3.
    Why not "uses"? Or even "using"? Semantically, we're closer to what the app does than "register". But register is fine. Not splitting hair should be a golden rule when it comes to refactoring, should it not?
    The SEO Faq thread
    Dependency injection made easy: Phemto

  12. #12
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Damn. "Try" used to be a useful method name too...

    What about :

    PHP Code:
    $object_graph->for('LoggingMailGateway')->choose('SmtpMailGateway'); 
    And a simple get() in place of "instantiate"?

    PHP Code:
    $object_graph->get('Foo'); 
    Quote Originally Posted by lastcraft
    I am also thinking of removing the need for "register"/"use" if there is only one choice amongst the available classes anyway. Is this too much automagic?
    I don't think so. If there are many secondary dependencies it could simplify things a lot. Often there will only be one option.

    PS: can someone do something about the (sitepoint) php highlighting? It's hard to read code through a keyhole. The box really ought to have the max width available.
    Last edited by McGruff; Oct 21, 2008 at 07:51.

  13. #13
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff View Post
    Damn. "Try" used to be a useful method name too...

    What about :

    PHP Code:
    $object_graph->for('LoggingMailGateway')->choose('SmtpMailGateway'); 
    Sorry, McGruff. for is reserved too.

  14. #14
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Any thoughts about using DI to drive a rules engine? Something like...

    PHP Code:
    $rules->on('OrderFailed')
        ->
    when('GoldCustomer')->tell('AccountRep')
        ->
    otherwiseTell('CustomerSupportLevelOne'); 
    The OrderFailed event would contain a Customer. If this Customer satisfies GoldCustomerSpec, AccountRep looks up Customer's representative and emails/pages them. Otherwise, a ticket is created by CustomerSupportLevelOne.

    Right now I'd just drop the listeners into an events/orders/ directory and have a dispatcher load them up whenever it receives an OrderEvent. The specifications are embedded directly in the listener classes, so it's all pretty easy to manage.

    It feels like DI would be overkill. What do you think?

  15. #15
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah I don't mean "global" as in "global vars". An assembler-type object is a sort of party host who knows how to introduce everybody to everybody else - which implies all the guests are mulling around in the same scope, in some sense. What I meant to say was, if an object is identified to the assembler simply by a parameter name, you'd need to take account of the possibility that there could be different parameters (and objects) with the same name in other classes. Occasionally some minor changes to the code might be required but that's not a deal breaker, I think.

  16. #16
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PHP Code:
    $injector->use('SmtpMailGateway')->with('LoggingMailGateway'); 
    sounds nice to me. It has the additional implications of being able to merely "use" something and only optionally bind it to something else.

  17. #17
    SitePoint Wizard
    Join Date
    Jan 2005
    Location
    blahblahblah
    Posts
    1,447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    How should I redesign my classes to make the following scenario:
    PHP Code:
    class Foo{

      public function 
    Test($var){

        
    //switch $var
        //case: 'blah' -> require/instantiate Blah(). 
        //case: 'blahvar' -> require/instantiate BlahVar().  
        //case: 'blahfoo' -> require/instantiate BlahFoo(). 

      
    }


    Should I instantiate all the objects. and then only use the one that is required?

    Regards,

    -jj.

  18. #18
    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 jjshell View Post
    Should I instantiate all the objects. and then only use the one that is required?
    If your object instantiates other objects, depending on a run time condition, then you are looking at a factory. You'll generally have few of those, but you may have some. If the objects you instantiate are sufficiently small (Eg. Don't have any dependencies), you can simply instantiate them with the new operator. If they need dependencies, then your factory will need to hold on to the container and use that for creating the instances. That's also fine, if you need it, but then you should make sure that your factory is just that - don't mix application logic into the factory.

  19. #19
    SitePoint Wizard
    Join Date
    Jan 2005
    Location
    blahblahblah
    Posts
    1,447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks

    But what about lazy instantiation?

    PHP Code:
    if(!$var){
      return 
    false;
    }else{
      
    //new object created.
      //do some stuff


  20. #20
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    348
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    where can I download phemto? it doesn't seem to be in any of your signatures, or is still in a development stage?

  21. #21
    ********* 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 old_iron View Post
    where can I download phemto? it doesn't seem to be in any of your signatures, or is still in a development stage?
    Yes.

    It's a sourceforge project and you should fetch the CVS version:
    Code:
    cvs -z3 -d:pserver:anonymous@phemto.cvs.sourceforge.net:/cvsroot/phemto co -P phemto
    The only docs are the test cases .

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

  22. #22
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    There's now a basic website, including some docs, here: http://phemto.sourceforge.net/index.php.

    Note that we've just switched from cvs to subversion. Checkout with:

    svn co https://phemto.svn.sourceforge.net/svnroot/phemto/trunk phemto
    Last edited by McGruff; Oct 27, 2008 at 07:53.

  23. #23
    SitePoint Wizard
    Join Date
    Jan 2005
    Location
    blahblahblah
    Posts
    1,447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Finally!

  24. #24
    SitePoint Wizard
    Join Date
    Jan 2005
    Location
    blahblahblah
    Posts
    1,447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The documentation is really clear, and straight to the point.

    I have one or two questions though. Let me start with:
    If you want an object to have a singleton lifecycle:

    <?php

    $phemto->register(new Singleton('Charm'));
    What would a singleton lifecycle bring that a "regular" lifecylce does not? I mean, I can't figure out why I should singletonize an object or not.

    Thank you all for your patience .

  25. #25
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This just means that every time you ask for an object (or when phemto passes it to a client behind the scenes) it's always the same instance. For example a Request object is usually passed around to several other objects. They must all access the same instance of Request. Or perhaps a PurchaseOrder passes through several processing stages in the script, each new client manipulating the order in some way. That's the kind of thing for which you need a singleton lifecycle.

    Any object can be singleton-ised. The container handles that without requiring any changes to your own classes.

    Sometimes it doesn't matter whether an object is singleton or not and I find myself using singletons simply because it's more convenient to pass (scalar) parameters at the registration stage rather than later on in the app, wherever the object is pulled out of the container.

    Edit: objects are singleton-ised with respect to their parent container (phemto) not the script as a whole. If you have two or more containers on the go, and each has registered a singleton Foo, it will be a different Foo per container.


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
  •