SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 52
  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
    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)?


  5. #5
    ********* 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

  6. #6
    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?

  7. #7
    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 .. )

  8. #8
    ********* 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

  9. #9
    SitePoint Wizard
    Join Date
    Jan 2005
    Location
    blahblahblah
    Posts
    1,447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,
    Quote Originally Posted by lastcraft View Post
    I am also thinking of renaming "register" into "use"...
    PHP Code:
    $injector->for('LoggingMailGateway')->use('SmtpMailGateway'); 
    Much much much clearer to me.

  10. #10
    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 lastcraft View Post
    Hi...

    I was thinking this...
    PHP Code:
    $injector->for('LoggingMailGateway')->register('SmtpMailGateway'); 
    Agreed - Much better.

    Quote Originally Posted by lastcraft View Post
    I am also thinking of renaming "register" into "use"...
    Because it makes the interface read as a sentence, I presume? I think it's perhaps taking it a bit too far for my taste, but it wouldn't hurt.
    edit: I take that back; .. for .. use .. makes good sense.

    Quote Originally Posted by lastcraft View Post
    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?
    Not sure. A problem with that is that behaviour can change unexpected. On the other hand, it would make initial work easier. How about raising a warning if there is a candidate, but it hasn't been registered. That would guide the user to register as needed, without any magic.

    While we're at it: Another issue I'm having with Phemto, is that you have to load classes before you can register them. That can be a performance problem for web applications, where you generally don't want to load more than you need. An opcode cache could counter this, but as long as it's not a standard fitting with PHP, it's not really something you can expect people to have. I'm not sure how to fix this, since classes must be loaded before you can tell which interfaces they implement.

  11. #11
    ********* 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 kyberfabrikken View Post
    I'm not sure how to fix this, since classes must be loaded before you can tell which interfaces they implement.
    I did have a caching hack at one point, but it sucked. I also don't understand autoload enough to know what complications that will cause.

    I'll do a few experiments over the next few days.

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

  12. #12
    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.

  13. #13
    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 06:51.

  14. #14
    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.

  15. #15
    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

  16. #16
    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.

  17. #17
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    Agreed - Much better.
    I'm not sure how to fix this, since classes must be loaded before you can tell which interfaces they implement.
    Eager reflection trumps lazy instantiation. It seems like there are only a couple options. Push eager registration out of the container and back at the user of the class. (Which is what I did.) Or cache the reflection information.

  18. #18
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft View Post
    Hi...
    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'm not sure. I think there is a need for stability. In other words, consider if you have two different parts of a system sharing the same container. Then, if a developer on one subsystem registers a new object with the container, that should not impact the existing configuration of any other subsystem unless that config is already somehow shared.

    There is an issue of isolation and change of time on larger projects. I think automagic looks good in forum sized examples because it makes them more forum sized. In a large application? I'm not so sure.

    It seems like there is enough magic in constructing entire chains of dependencies. I think that process needs to be very deterministic.

  19. #19
    ********* 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 Selkirk View Post
    There is an issue of isolation and change of time on larger projects. I think automagic looks good in forum sized examples because it makes them more forum sized. In a large application? I'm not so sure.
    I've run this past my legal department and they point out that users would still have the option of registering dependencies if they wanted. Omission would be optional.

    That's lawyers for you.

    Looking good in forum posts? That's exactly what phemto is for . It was originally a demo for an old PHP Architect article. The idea was that people would have a play, then use Pico. Looks like it has enough traction (2 users!) to be a real project.

    Hm..I think I'll try it. Not least, because your version is doing the opposite and I want to see which one wins out.

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

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

    "For" has very few synonyms. How about one of...?
    PHP Code:
    $injector->in('LoggingMailGateway')->uses('SmtpMailGateway');
    $injector->forInterface('LoggingMailGateway')->willUse('SmtpMailGateway');
    $injector->forInstance('LoggingMailGateway')->willUse('SmtpMailGateway');
    $injector->on('LoggingMailGateway')->uses('SmtpMailGateway');
    $injector->upon('LoggingMailGateway')->willUse('SmtpMailGateway');
    $injector->whenInstantiating('LoggingMailGateway')->willUse('SmtpMailGateway');
    $injector->whenCreating('LoggingMailGateway')->willUse('SmtpMailGateway'); 
    ...plus various combinations.

    Another feature I am toying with...fulfilling variable names as well as type hints. If someone has...
    PHP Code:
    class MyController implements Controller {
        function 
    __construct($session) { ... }

    ...they don't want to necessarily clutter up the code with type hints, especially for old PHP4 code or for classes with long lists of constructor parameters.

    So...
    PHP Code:
    $injector->forVariable('session')->willUse('Session'); 
    ...or...
    PHP Code:
    $injector->in('Controller')->forVariable('session')->willUse('Session'); 
    I am taking the DI to the brink of madness, but I'd actually like to do this for a project right now.

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

  21. #21
    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 lastcraft View Post
    PHP Code:
    $injector->forVariable('session')->willUse('Session'); 
    That's interesting. It allows you to cut out static interfaces. Essentially, it's very similar to having the information in annotations, but it's much more compact. The downside is of course that you can easily get false positives, but perhaps it's not that big a problem; If you're namespacing class names PEAR-style, they are going to be quite unique anyway. And of course, typehints would take precedence, so if you don't like ambiguity, you wouldn't have to reply on it.

    I think I like this idea very much.

    Oh .. and how a bout provide:
    PHP Code:
    $injector->for('LoggingMailGateway')->provide('SmtpMailGateway'); 
    Slightly different semantic, but still meaningful.

  22. #22
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think I like this one best:
    PHP Code:
    $injector->whenCreating('LoggingMailGateway')->willUse('SmtpMailGateway'); 
    Some more ideas:

    PHP Code:
    $injector->feed('LoggingMailGateway')->with('SmtpMailGateway');
    $injector->fill('LoggingMailGateway''MailGateway')->with('SmtpMailGateway');
    $injector->give('LoggingMailGateway')->a('SmtpMailGateway');
    $object_graph->node('LoggingMailGateway')->uses('SmtpMailGateway'); 
    Quote Originally Posted by Selkirk
    I think automagic looks good in forum sized examples because it makes them more forum sized. In a large application? I'm not so sure
    In a complex example where dependencies have dependencies which have etc.. I think that this might be particularly useful. The bit at the top is deliberately written with a variety of options in mind but the secondary dependencies are not - or at least not necessarily. It's less likely that there will be many choices to make here and so it woud be nice if Phemto just sorts that out for me. All I have to do is just make sure everything's included.

    Quote Originally Posted by Lastcraft
    I am taking the DI to the brink of madness
    PHP Code:
    $injector->forVariable('session')->willUse('Session'); 
    $injector->in('Controller')->forVariable('session')->willUse('Session'); 
    I don't see a problem with the second but the first (which I assume is setting a "global" value filling all Session dependencies everywhere) could lead to problems if there are variable name clashes. Still, in some cases you might specifically want to set a common value in order to avoid repeating registration calls so I guess Phemto could just trigger an error / throw exception if a second $injector->forVariable('session') is registered.

  23. #23
    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?

  24. #24
    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 McGruff View Post
    .. but the first (which I assume is setting a "global" value filling all Session dependencies everywhere) could lead to problems if there are variable name clashes.
    Everything within the container is global, in the sense that there is just one scope, that must be shared by all identifiers. These can be interfaces, or they can be variable names. Perhaps the chance of clashing variable names is bigger, but it's not fundamentally different from the chance of clashing names with interfaces.

    Quote Originally Posted by lastcraft View Post
    PHP Code:
    $injector->in('Controller')->forVariable('session')->willUse('Session'); 
    I don't like this version that much. It asks the user to specify the dependencies for a particular component, which means that the configuration needs to know which dependencies this component has.

  25. #25
    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.


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
  •