SitePoint Sponsor

User Tag List

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

    Phemto usability - your help needed

    Hi...

    I need your help!

    Rewritten Phemto in light of this thread: http://www.sitepoint.com/forums/showthread.php?t=577279.

    Of course code written in isolation can end up plain strange, so I need a bit of an interface review. Any help is gratefully appreciated.

    The specification is probably best described by the test file. I've currently packaged it as just two files for easy upload to this forum, but I'll break it down for the Sourceforge tarball.

    In summary your wiring file might look something like...
    PHP Code:
    $injector = new Phemto();
    $injector->whenCreating('Controller')->forVariable('session')->willUse('DbSession'); 
    Features are:
    • Lot's of automation (no need to register if Phemto can figure out the class).
    • Uses hints to fill dependencies.
    • Can specify a variable to map to an interface.
    • Nested scoping to allow different dependencies for particular interfaces.
    • Choice of lifecycle: Factory, Sessionable, Reused.
    • Setter injection.
    • Easy decorators (although the implementation is a bit icky).
    • Named parameters when creating instances.
    • Unnamed positional parameters when creating instances.

    There are still quite a few things missing, such as: value dependencies, Singleton lifecycle, full test coverage, any form of optimisation at all...you know, details .

    Anyway, the files are attached. Please post a few comments on first impressions and then I have a few specific questions to ask.

    yours, Marcus

    p.s. For goodness sake don't use this in production. It's probably riddled with inconsistencies.
    Attached Files Attached Files
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  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)
    Initial observations:
    PHP Code:
        function TODO_testTypeHintsTakePrecedence() {
            
    // or do they? specify one or the other
        

    I don't think they do. If you configure something that doesn't make sense, you will get a fatal error anyway. That's better than wondering why it didn't do as you asked it to.

    PHP Code:
    class CanInstantiateObjectsAsSingletons extends UnitTestCase {
        function 
    testSameInstanceCanBeReusedWithinFactory() {
            
    $injector = new Phemto();
            
    $injector->willUse(new Reused('LoneClass'));
            
    $this->assertSame(
                    
    $injector->create('LoneClass'),
                    
    $injector->create('LoneClass'));
        }

    "create" seems a bit misleading, when you are in fact getting the same object back on the second call. Thus it isn't created at all.

    PHP Code:
    class ClassWithParameters {
        function 
    __construct($a$b) { @$this->$a; @$this->$b; }

    What's the error suppression for?

    Oh .. and you're mixing tabs and spaces. Looks messed up on my end.

  3. #3
    ********* 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
    Initial observations:
    PHP Code:
        function TODO_testTypeHintsTakePrecedence() {
            
    // or do they? specify one or the other
        

    I don't think they do. If you configure something that doesn't make sense, you will get a fatal error anyway. That's better than wondering why it didn't do as you asked it to.
    Thinking out loud . Currently unspecified, but if a variable contradicts a type hint, what happens? It's a lot of work to keep track of configuration order here, but probably necessary. The implementation is much easier if I could just say one takes precedence.

    The catch is, suppose the framework author has set everything up with variable names, but the app. author want to append to the wiring file using type hints. Or maybe the other way around. As the app. author came later, their choice should now take precedence. One set needs to gasump the other.

    Anyway, I'll think about that later as my head hurts after a day of coding this thing.

    Quote Originally Posted by kyberfabrikken View Post
    "create" seems a bit misleading, when you are in fact getting the same object back on the second call. Thus it isn't created at all.
    Which sort of puts me back at "instantiate" (or maybe "get").

    Quote Originally Posted by kyberfabrikken View Post
    What's the error suppression for?
    Not sure. Is there not an E_STRICT warning if you assign to an undeclared variable? Something about it being forced to be public? Or am I imagining it.

    Quote Originally Posted by kyberfabrikken View Post
    Oh .. and you're mixing tabs and spaces. Looks messed up on my end.
    Sorry, it's been a real hack session and I probably forgot to set the editor up correctly. I'll try to fix it in SVN.

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

  4. #4
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Interesting, haven't kept up with Phemto developments in the other thread.
    Will look into it some more.

    Though I am wondering now that PHP5.3 has closures wether things could be easier.

  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 lastcraft View Post
    The catch is, suppose the framework author has set everything up with variable names, but the app. author want to append to the wiring file using type hints. Or maybe the other way around.
    Hm .. good point. But I still think it's better to have one clear way to resolve it. Making it configurable just seems like it could cause a lot of confusion.

    Quote Originally Posted by lastcraft View Post
    Not sure. Is there not an E_STRICT warning if you assign to an undeclared variable? Something about it being forced to be public? Or am I imagining it.
    I think it has been suggested for PHP 6.

    Quote Originally Posted by Ren View Post
    Though I am wondering now that PHP5.3 has closures wether things could be easier.
    Yes, I definitely think that could make a difference.

  6. #6
    ********* 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 Ren View Post
    Though I am wondering now that PHP5.3 has closures wether things could be easier.
    The obvious comparison here is with "needle" (Ruby project). Closures are more flexible (you can have any set up in the block), but you have to do more stuff manually. The wiring also seems less readable to me.

    I will be adding closure support upon 5.3 for sure, but as an addition.

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

  7. #7
    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 was playing around with Phemto today, and came about a new possible use case. When creating a named scope (Using Phemto->whenCreating), it might be useful to be able to specify a wildcard. Eg.:
    PHP Code:
    $phemto->whenCreating('people_*')->forVariable('gateway')->willUse('people_PersonGateway'); 
    Until 5.3, the way namespaces are done in PHP, is by prefixing classes with the namespace. Allowing such a wildcard would make it easy to wire up classes within the same module/namespace. I suspect that this concept could be automated further, but I'm not sure that's needed - or even desirable. Once PHP 5.3 comes out, it'll probably be less important too.

  8. #8
    ********* 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
    When creating a named scope (Using Phemto->whenCreating), it might be useful to be able to specify a wildcard. Eg.:
    PHP Code:
    $phemto->whenCreating('people_*')->forVariable('gateway')->willUse('people_PersonGateway'); 
    Interesting. It's a bit tricky the way I've written it right now, due to namespaces being a slightly different thing than the context. Hm. Could have...
    PHP Code:
    $phemto->whenPrefixed('people')->... 
    ...perhaps.

    I'll stash this for later unless you have a real need right now.

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

  9. #9
    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
    Hm. Could have...
    PHP Code:
    $phemto->whenPrefixed('people')->... 
    ...perhaps.
    That would be fine. Actually, it's probably better that way, because it makes a clear distinction between registerin something for a class/interface or for a pseudo-namespace.

    Quote Originally Posted by lastcraft View Post
    I'll stash this for later unless you have a real need right now.
    Oh, no real needs, since I'm just experimenting with it. Besides, this is more of a conveinance feature, than something fundamental. It just lowers the level of redundancy in the wiring code.

  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)
    I have found another use case, not covered by Phemto. Some objects have public properties and no accessors, so it isn't enough with a setter-injection that calls a function - There needs to be the option to assign directly to a public property. Case in point:
    PHP Code:
    $smarty = new Smarty();
    $smarty->template_dir "templates"
    So we need for example:
    PHP Code:
    $injector->forType('Smarty')->assign('template_dir''templates'); 
    or perhaps:
    PHP Code:
    $injector->forType('Smarty')->assign('template_dir');
    $injector->forVariable('template_dir')->willUse('templates'); 

  11. #11
    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)
    Hm .. After some tinkering, I came up with this solution, which I suppose is acceptable, albeit a bit verbose:
    PHP Code:
    class ConfigurableSmarty extends Smarty {
      function 
    __construct($template_dir) {
        
    parent::Smarty();
        
    $this->template_dir $template_dir;
      }
    }

    function 
    create_phemto_container() {
      
    $injector = new Phemto();
      
    $injector->willUse('ConfigurableSmarty');
      
    $injector->whenCreating('ConfigurableSmarty')->forVariable('template_dir')->willUse(new Value('path/to/templatedir'));
      return 
    $injector;


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

    Given the automagic nature of Phemto, getting the domain language right is critical. I have to explicitly solve the problem of assigning public variables.

    Maybe set() is the simplest way to do it...?
    PHP Code:
    $injector->whenCreating('Smarty')->set('dir')->to('/my/path'); 
    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  13. #13
    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)
    set .. to works good for reading, but I don't really see why a single function with two arguments couldn't do the job? It doesn't really make sense to set something, without setting it to some value?

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

    Yeah, I was just playing with ideas.

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

  15. #15
    SitePoint Enthusiast
    Join Date
    May 2007
    Posts
    28
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nice ideas.

    I've altered the method names a little.
    In which scope is an "alias" / "forVariable" defined? When "forVariable" is defined for "DbSession", will it also work for 'UserObject'?
    PHP Code:
    $injector = new Phemto();
    $injector->instantiate('Controller')
    ->
    dependingOn('DbSession')
    ->
    alias('session','Session')
    ->
    setVar('number','1')
    ->
    setObj('myUserObject',$injector->instantiate('UserObject'))
    ->
    setArray('myArray',array()); 
    --
    I have this idea of using Reflection only one-time building up an rule-set of dependencies. I think this make sense in regard to lower the costs for Reflection, especially when there are many Objects to deal with. Could this be an enhancement for Phemto?
    Last edited by mr.vain; Jan 24, 2009 at 17:39.

  16. #16
    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 mr.vain View Post
    I have this idea of using Reflection only one-time building up an rule-set of dependencies. I think this make sense in regard to lower the costs for Reflection, especially when there are many Objects to deal with. Could this be an enhancement for Phemto?
    If you look at the sources, there is already a ReflectionCache object, which does that.

  17. #17
    SitePoint Evangelist
    Join Date
    Aug 2005
    Location
    Winnipeg
    Posts
    498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've been following these discussions for a while now, but I don't htink I have yet to comment.

    Looks interesting.

    Quick question: Is this intended to replace a simplistic registry/service locator?

    I know your obviously big on TDD (personally not so much but there are other uses I'm sure).

    Could you show me a trivial on how one might use Phemto to access an object, like say SwiftMailer?

  18. #18
    SitePoint Evangelist
    Join Date
    Mar 2006
    Location
    Sweden
    Posts
    451
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You wouldn't be using Phemto to access a SwiftMailer-object, you would be letting Phemto instantiate the object needing Swift, and let Phemto make sure that a Swift-object is passed to it.

    So it's really not like a registry, since you pull stuff out of a registry, whereas Phemto/DI is pushing stuff in. That way, your classes doesn't need to be aware of a registry, or even Phemto itself, but only the actual classes that it needs.

    So if you have the classes Foo and Bar, and Foo needs an instance of Bar, you'd let Phemto instantiate Foo, and Phemto would make sure that Foo gets a Bar.

    PHP Code:
    <?php
    class Foo {
      protected 
    $bar;
      function 
    __construct(Bar $bar) {
        
    $this->bar $bar;
      }
      function 
    getBar() {
        return 
    $this->bar;
      }
    }

    class 
    Bar {

    }

    $phemto = new Phemto();
    $phemto->willUse('Foo');
    $phemto->willUse('Bar');

    $foo $phemto->create('Foo');
    var_dump($foo->getBar());
    ?>

  19. #19
    SitePoint Evangelist
    Join Date
    Aug 2005
    Location
    Winnipeg
    Posts
    498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You wouldn't be using Phemto to access a SwiftMailer-object, you would be letting Phemto instantiate the object needing Swift, and let Phemto make sure that a Swift-object is passed to it
    Ok, makes sense, although I already use constructor injection for almost everything already and if I was concerned about the right object types being passed around, I would use type hinting.

    I have a registry, which I push into and pull out of, like you said. I also have a service locator that creates objects and initializes them on occassion. This removes the concrete dependency of Class_A on any given method, making testing a lot easier.

    I guess I'm struggling to see the purpose behind Phemto, if I already heavily employ DI, use registry and service locators.

    So if you have the classes Foo and Bar, and Foo needs an instance of Bar, you'd let Phemto instantiate Foo, and Phemto would make sure that Foo gets a Bar.
    Call me dense but I have never understood Foo Bar examples

    I'd like to see how Phemto might be used for something like Swift, which is a class I use regularly. My code is not directly dependent on it's interface as I wrap it with a facade of sorts.

    So I guess my question is: What advantages does Phemto provide me if I already use DI and service locators to remove the dependency. I'm missing something but I'm not sure what.

    Using Swift as an example:

    Code:
    My::Controller()
    {
      global $registry; // Wherever it comes from (global, parameter, accessor)
      $locator = $registry->fetch('locator');
      $swift = $locator->lookup('Swift_Mailer'); 
    
      $swift->send('Subject', 'Message');  
    }
    As far as I am concerned I have achieved inversion of control, using a locator instead of injection, which in the context of a controller makes a good deal of sense -- IMHO anyways.

    Cheers,
    Alex
    The only constant in software is change itself

  20. #20
    SitePoint Evangelist
    Join Date
    Mar 2006
    Location
    Sweden
    Posts
    451
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I agree, Foo Bar examples are not especially good...

    The problem with your code as I see it is that it's dependant on two classes, which it only uses to fetch the one class that it really needs. If you were to pass in Swift through the constructor instead, your class would be blissfully ignorant of both the registry and the service locator. Let's compare these two pieces of code:

    PHP Code:
    <?php
    class MyController {
      function 
    doSend() {
        global 
    $registry// Wherever it comes from (global, parameter, accessor)
        
    $locator $registry->fetch('locator');
        
    $swift $locator->lookup('Swift_Mailer'); 

        
    $swift->send('Subject''Message');
      }
    }

    class 
    MyController {
      protected 
    $mailer;
      function 
    __construct(Mailer $mailer) {
        
    $this->mailer $mailer;
      }
      function 
    doSend() {
        
    $this->mailer->send('Subject''Message');
      }
    }
    ?>
    The second example doesn't know about the registry or service locator at all, it only knows about the class it really needs. And when this is true, it makes it easier for you to rearrange your application, and perhaps even remove the registry at some point, since your classes doesn't use the registry directly.

  21. #21
    SitePoint Addict webaddictz's Avatar
    Join Date
    Feb 2006
    Location
    Netherlands
    Posts
    295
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wysiwyg View Post
    The second example doesn't know about the registry or service locator at all, it only knows about the class it really needs. And when this is true, it makes it easier for you to rearrange your application, and perhaps even remove the registry at some point, since your classes doesn't use the registry directly.
    Though I agree with the demonstration of the benefit of using a DI container, I always tend to pass the container along to controllers, because the relevant controller is usually figured out at runtime. How do others cope with this? Do you let the DI container instantiate the appropriate controller(s)?

  22. #22
    SitePoint Evangelist
    Join Date
    Mar 2006
    Location
    Sweden
    Posts
    451
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Not many frameworks lets you have a DI to instantiate the controller, which is a shame. I guess the problem is that a framework has to pass in some dependencies to the controller for it to work, and the natural way is to pass it in through the constructor. But that closes the door for passing in controller-specific dependencies through the constructor, and you often have to use the frameworks registry to deal with dependencies.

    If you want to get away from that, check out the new version of Konstrukt (currently in the incubator-folder in svn), which lets you create controllers with a DI container.

  23. #23
    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 PCSpectra View Post
    I guess I'm struggling to see the purpose behind Phemto, if I already heavily employ DI, use registry and service locators.
    A type-hint aware DI container can save some work wiring up the object graph. In a Factory/Locator/etc you have to pass in dependencies, and dependencies of dependencies, and so on. You end up writing lots of Factory methods, which call other Factory methods, which...etc. In Phemto you just need to register some classes and then Phemto automatically wires everything up for you.

    That could be especially nice if dependencies change. A hard-coded factory would have to be edited to keep pace with that but if you were using Phemto you might not need to do anything (unless you have new classes to register). It's all in the constructor type hints.

    However the most important benefit, I think, is simply identifying wiring as a separate responsibility. A plain-old, hard-coded Factory can do a good job of that and it's an easy way to start experimenting. Not quite as nice as an "intelligent" DI container though.

  24. #24
    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 wysiwyg View Post
    Not many frameworks lets you have a DI to instantiate the controller, which is a shame.
    I'm seriously considering replacing the Zend_Controller_Dispatcher class with an unofficial version to get control of the instantiation. Unfortunately, the dispatch() method is 77 lines long, whereas the actual instantiation is just one line:
    PHP Code:
    $controller = new $className($request$this->getResponse(), $this->getParams()); 
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  25. #25
    SitePoint Enthusiast
    Join Date
    May 2007
    Posts
    28
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    If you look at the sources, there is already a ReflectionCache object, which does that.
    Excellent! Thanks for the hint; sometimes i'm just blind. (In my application i use the old version of Phemto. Besides the fact that Phemto is still marked as alpha, i will switch to the new version to have caching of Reflection supported. Grand stuff!)

    Quote Originally Posted by webaddictz View Post
    Though I agree with the demonstration of the benefit of using a DI container, I always tend to pass the container along to controllers, because the relevant controller is usually figured out at runtime. How do others cope with this? Do you let the DI container instantiate the appropriate controller(s)?
    That's my question, too! I pass the Injector to the Controller and to the View. But which arguments speak against passing it?


Tags for this Thread

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
  •