SitePoint Sponsor

User Tag List

Page 2 of 3 FirstFirst 123 LastLast
Results 26 to 50 of 54
  1. #26
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    phpimpact:
    I have no plans to make the Registry/Container itself a singleton, nor to introduce static methods. I understand the issue with controllers instantiating objects directly. I hope to create an injection aware controller to avoid this problem, rather using static methods, globals, or psuedo globals. Failing that, I would probably pass an instance of the registry to the controller.

    Kyber:
    I'm not hooked to the registry name, but I have to change the file name, too at this point. So, I'm going to defer renaming the class until more of its capabilities are finished.

    Lastcraft:
    I don't think you can abstract the lifecycle away from the use of the object and only into the definition. I think there is value in the stability of knowing the lifecycle of the objects that you access from the container. The pooling examples don't excite me because I really don't use the pooling pattern in PHP. Got any other examples where you would start out with a singleton and then need independent instances, or vise versa, where this change could be made in ONLY the registration information and not require a change to the consuming code anyway?

    Here is a use case where you would want to know that you are always getting a new instance of an object. Lets say that you are building some kind of complex composite object. Each node is going to implement the same interface. Only the composite as a whole is "interesting" to the container, the sub-nodes will never be injected into anything else. The DI container can assist in injecting perhaps obscure dependencies into the composite nodes without the knowledge of the composite building code. The builder code could then use something like:
    PHP Code:
        $childNode $registry->CompositeNode($parentNode); 
    So the CompositeBuilder fills in the structure that it knows about (parent/child), while the DI container takes over for the things the CompositeBuilder doesn't and shouldn't know how to create.

    More use cases to come. I'm out of time for today.

    Here is today's progress. I've extracted the reflection stuff from WactRegistryFactory and cleaned it up a bit.
    registry.inc.php

  2. #27
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    great stuff

  3. #28
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    *off topic* Also jeff, now that you are talking about DI and TDD, how come that if scott is going to Chicago in May, you are not coming to London this month to talk about all this stuff? Come on, there are nice beaches here, everything is cheap and the weather is great. Also, bring marco with you

  4. #29
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Tonight, I've added a bit of docblock reflection.

    If there is no type hint for a constructor parameter, the WactRegistryClassInfo class will examine the @param docblocks for the constructor and pick a type out of there.

    Keep in mind that you can put pretty much anything you want here. So, this allows a pattern of using a "fake" class name to inject non-class values or multiple or distinguished instances of a particular interface or class.

    PHP Code:
    $registry->database_password "secret"
    /**
    * @param database_password $pass The password
    */
    function __construct($pass
    One thing to keep in mind is that the docblock type only comes into play in the absence of a type hint. If this is a desirable pattern, then perhaps it would be a good idea to make the docblock type override the typehint for purposes of injection.

    Perhaps there is an alternate syntax that would allow the @param to retain the string type for documentation purposes, but still specify an alias to use for injection that superceeds the specified @param type or the specified type hint?

    Any suggestions? This is one where I could really use some feedback.

    The code in WactRegistryClassInfo is getting rather nasty. In my limited time and my desire to show progress every day, I'm running up a bit of a technical debt.

    registry.inc.php
    registry.test.php

  5. #30
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Chris Corbyn View Post
    I still have it and actually registered a project on SF.net for it. The new version of Swift Mailer was is 100% DI-driven but I'm still not sure if I'll use that container yet, or just write simple factories. It's a beautiful way to code, especially when you take a test-first approach to development. If you nosey around the codebase in /branches/v4-simplified for swiftmailer you'll see how lloosely coupled it is compared with earlier versions, and also how good the test coverage is as a result.

    http://www.sourceforge.net/projects/phpcrafty (heck, I was so close to getting it public, there's even documentation and a partly built website :P I'll finish it I promise. Needs some optimizations.)

    (Don't pay too much attention to the new code in here... it's changing every few hours)
    http://swiftmailer.svn.sourceforge.n...v4-simplified/

    One thing with Crafty is that it's currently just a proof-of-concept and hasn't had any real-world application. It will likely need some tweaking.
    Excellent work Chris. I'm glad that you took this a step further! What about the option of binding parameters in future versions? For example:

    Code XML:
    <constructor>
          <!-- Each argument has an arg tag -->
          <arg>
            <!-- ... and it's value has either a value, componentRef
              or collection element -->
            <value bind="username" />
          </arg>
          <arg>
            <value>the-password</value>
          </arg>
          <arg>
            <value bind="hostname" />
          </arg>
        </constructor>

    Where "username" is the placeholder name.

    Great work, well done!

  6. #31
    ********* 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
    Any suggestions? This is one where I could really use some feedback.
    In "blink" mode, i'd say id want the docblock to take preference. If I've taken the trouble to put a comment in, then the type hint is probably incidental. Type errors will tell me when I've done something contradictory.

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

  7. #32
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Selkirk View Post
    Tonight, I've added a bit of docblock reflection.
    I'd been thinking of doing something similar, but using @inject. I suppose it would be slightly more verbose, but it would be more flexible...

  8. #33
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Marcus, I think I agree that the docblock should override the type hint. Additionally, I think that there is no need for a specific alias indicator. Consider the following syntax:
    PHP Code:
    /**
    * @param database_password | string Yada Yada Yada
    */
    function __construct($password); 
    Using the | notation, this communicates the type hint (none), the value to be injected (database_password), and the documented type (string). The first type specified is injected, the rest are documentation.

    This style simplifies the docblock parsing. I've changed the code so that the docblock overrides the type hint and added two test cases.

    Sorry for the delay. Got busy. Tommorrow, @inject.

    registry.inc.php
    registry.test.php

  9. #34
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Jeff, IMO a DI container has to adapt to the API and not the other way around. By using the DocBlock, you are assuming that the user can edit the API. And in most of the cases, only core developers have this privilege.

    Quote Originally Posted by lastcraft View Post
    If I've taken the trouble to put a comment
    It's more about permissions than taking the trouble to add a comment. I don't know any company that maintains a framework where users can just login in, open a core component, and edit it. They are usually in the same working group, they can read and execute files, but not write.

  10. #35
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by phpimpact View Post
    Jeff, IMO a DI container has to adapt to the API and not the other way around. By using the DocBlock, you are assuming that the user can edit the API. And in most of the cases, only core developers have this privilege.
    Ideally it should acomodate both ways of working; using docblocks and reflection make configuration very simple, but there will always be situations where full manual configuration is required.

  11. #36
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I discarder the annotation approach a while ago, because is a non standard way of using configuration files. If you think about it, a file with annotations is almost the same as having a configuration file, but offers less advantages, for example: Some people might not even know that you are using annotations as configuration parameters.

    Having something like this:
    PHP Code:
    class Core {
        
    /**
         * Sets A
         * @inject A
         */
        
    function setA(A $a) {
        }
        
    /**
         * Sets B
         * @inject B
         */
        
    function setA(B $b) {
        }

    Is the same as:

    PHP Code:
    Class CoreConfig
    {
        
    /**
         * @class  Core
         * @method setA
         * @inject A
         *
         * @class  Core
         * @method setB
         * @inject B
         */

    And what's the advantage of using those annotations as configuration parameters, over xml, ini, yaml files? None. And it also adds a non-standard way of working and extra complexity to the system.

  12. #37
    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 phpimpact View Post
    And what's the advantage of using those annotations as configuration parameters, over xml, ini, yaml files?
    I does sit closer to the code, which is an advantage. Mind you, I'm not a fan of annotations either, because of some of the reasons you already mentioned, but I think they have some differences from configuration files.

  13. #38
    ********* 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 phpimpact View Post
    I don't know any company that maintains a framework where users can just login in, open a core component, and edit it.
    Why would they have to edit the core API docs?

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

  14. #39
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Because you might have a problem with this class:

    PHP Code:
    class Core
    {
        
    /**
         * @param obj $component Component Foo or FooMocked
         */
         
    function __construct(FooInterface $component) {
         }

    What do you inject there?

  15. #40
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Posts
    94
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Some suggestions/thoughts from me

    usage
    A well developed interface and an easy integration of the container is - of course - fundamental. But i believe that you can put a fancy interface on
    a well developed and conservative designed library.

    Oh, wtf guice was never mentioned on the whole forum? (is the search broken?) guice has an
    interesting and very different approach to the old irons.

    explicity
    I prefer to force the developer to define dependencies to inject explicit.
    Just imagine that you have a setter to add additional behaviour which has a
    type hinted interface argument. If you have no implementation for it, the
    container can't resolve the dependency. Explicity makes you think about
    what you're doing and keeps class resolvers idle.

    completeness
    i believe a useful DI container must be almost feature complete. (this is
    anyones own decision of course!)

    - constructor injection
    - property injection
    - setter injection
    - resolve type via typehint or annotation (for properties/methods)
    - bind an implementation to an specific interface in a component
    - any datatype must be injectable
    - any complex argument list must be injectable
    - providers aka prototype factories (ie for active records)
    - request and session scope
    - autowiring (but explicit)
    - lifecycles (ie default call to $foo->init())
    - help as much with debugging as possible
    - class loading

    I'm curious about discussion on that list.


    Quote Originally Posted by phpimpact View Post
    Because you might have a problem with this class:

    PHP Code:
    class Core
    {
        
    /**
         * @param obj $component Component Foo or FooMocked
         */
         
    function __construct(FooInterface $component) {
         }

    What do you inject there?
    If you have more than one implementation for an interface you must announce
    which one to use.

  16. #41
    ********* 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 phpimpact View Post
    What do you inject there?
    As obj won't be found, FooInterface.

    Anyway, the framework authors will be the ones using DI. I am sure they can fix their own API docs.

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

  17. #42
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi marcus,

    Quote Originally Posted by lastcraft View Post
    As obj won't be found, FooInterface.
    How do you do that, if FooInterface is an interface? Human intervention is needed.

    PHP Code:
    interface FooInterface {

    Quote Originally Posted by lastcraft View Post
    Anyway, the framework authors will be the ones using DI. I am sure they can fix their own API docs.
    Well, I don't agree with this. Not only framework authors will use DI, also millions of users using that particular framework. Take the Zend framework for example. If they cerate a DI for their users to use, I doubt that they would spend hundred of hours and thousands of dollars refactoring the API docs just to offer an alternative way of injecting dependencies, such as using the docblock.

  18. #43
    ********* 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 phpimpact View Post
    How do you do that, if FooInterface is an interface? Human intervention is needed.
    Agreed, but isn't declaring a dependency orthogonal to fulfilling it.

    Quote Originally Posted by phpimpact View Post
    Take the Zend framework for example.
    Well, Zend is not quite sure whether it's a class library or a framework. I mean frameworks that run the whole show. Even so, if the docs differ from the type declarations, isn't that a documentation bug?

    That said, I'm not sure that this will arise that much. Once the type is declared in the signature, redecaring it in the docblocks is pretty pointless. I wouldn't bother. Maybe only PEAR cares. If it's recently ported PHP4 code, then no signature typing will exist.

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

  19. #44
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Interesting, thanks Marcus. I also learnt a new word today, orthogonal

  20. #45
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft View Post
    Well, Zend is not quite sure whether it's a class library or a framework. I mean frameworks that run the whole show.
    Just to clarify this point, a framework will always be a library of components. Now, if the components are tight coupled or decoupled, that's a different story.

    Maybe you are referring to one of the most important futures of the framework, the ability to work standalone. Zend_Controller is the heart of the framework, it's one of the best designed components out there. If there where a competition of PHP components today, this would definitely be the one to beat.

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

    What I mean is that a framework handles the request and you get to place code only where the framework lets you. If you control the request/response cycle and can put code where you want, but you make use of services (no matter how rich) then I'd have trouble calling it a framework.

    If you use a framework you give up control of this stuff to the framework author. It's this type of framework where I think DI has a big part to play.

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

  22. #47
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Back From the dead.

    Ok, I've done several things. Sorry, I didn't do them in small increments here.

    I've converted class names to pear naming standards to use autoloading.

    WactRegistry is now Wact_Config_Registry
    WactRegistryFactory is now Wact_Config_Registry_Factory and exists in a separate file.

    WactRegistryClassInfo and WactRegistryParameterInfo have now been refactored out into a separate reflection augmentation library.

    wact/reflection

    As was suggested, I changed the method instance creation pattern to $registry->createClass(), rather than $registery->Class();

    Additionally createInstance is now simply create() and is public.

    getSingleton() is now simply folded into __get(). I'm re-thinking this one.

    Here are the base dependency injection files:
    wact/config/registry.inc.php
    wact/config/registry.test.php
    wact/config/registry/factory.inc.php

  23. #48
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, I've committed the next set of changes.

    I've implemented a define once policy for the configuration container. So, this code, is now illegal and throws an exception:
    PHP Code:
    $config = new Wact_Config_Registry();
    $config->key 'something';
    $config->key 'something else'
    Oh, and I implemented a Wact_Config_Exception which is the base exception for all exceptions in this package.

    I also disabled __unset in line with the define once policy.

    So what happens if you want to override some existing configuration options? I've also added the concept of a heirarchy of configuration with different context and fallback. For example:
    PHP Code:
    $config = new Wact_Config_Registry();
    $config->'base';
    $config->'base';
    $myContext $config->newContext();
    $myContext->'overridden';
    echo 
    $myContext->a' '$myContext->b"\n";
    // result: base overridden 
    The newContext() method creates a new instance of Wact_Config_Registry, using the oldone as a fallback. You are then allowed to "define once" in the new context. Undefined items are retrieved from the fallback.

    I'm not quite sure what the best definition of __isset is, so i've made it throw an exception until I figure that out.

    I did define a new method, singletonExists. This will return true if the registry or its fallbacks contains an instance of that singleton. It will not create the instance and does not account for whether the container could create it if asked to.

    Here are the new files:

    wact/config/registry.inc.php
    wact/config/registry/factory.inc.php
    wact/config/exception.inc.php
    wact/config/registry.test.php

    Sorry, with the introduction of the dependencies on autoloading, Wact_Exception_Base and Wact_Reflection, its not so easy to just run the container any more.

  24. #49
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, enough with the singletons.

    The next iteration introduces a define method. The define method defines a factory to be used when creating objects of a certain type.

    There are three ways to use the define method:
    PHP Code:
    $config->define('Class'); 
    This is the default that the container uses when you do not define a factory. It creates a factory for 'Class' and uses it when you try to access 'Class' or create an instance of type 'Class'.

    Lets look at something more useful.
    PHP Code:
    $config->define('AbstractClass''ConcreteClass');
    $obj $config->AbstractClass// Returns instance of ConcreteClass 
    This lets you define an alias where a abstract class or abstract type may be defined in terms of another type.

    The third form is to explicitly pass the factory in
    PHP Code:
    $config->define('AbstractClass', new Wact_Config_Registory_Factory('ConcreteClass')); 
    define returns the factory and will be the starting point for a fluent interface of more completely describing how to create instances of that type. But thats later.

    I've also added a pre-defined singleton for Wact_Config_Container. This allows the container to inject itself into any object that it creates that specifies a Wact_Config_Container typehint or docblock hint.

    Here are the changed files:
    wact/config/registry.inc.php
    wact/config/registry.test.php

  25. #50
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, this is the last post for tonight.

    I've added a DSL for describing the factories. First, forget everything about the define method I mentioned above. There is now only one form for defining a type.

    So here is a small example defining a db connection and a Model object that uses it.
    PHP Code:
    class MySqlConnection implements Connection {
        function 
    __construct($dsnCache $optional NULL) {}
    }

    class 
    MyModelClass {
        function 
    __construct(Connection $conn) {}
    }

    $config->define('Connection')
        ->
    asClass('MySqlConnection')
        ->
    parameterValue('dsn''mysql:dbname=testdb;host=127.0.0.1');

    $model $config->MyModelClass// Connection will be injected. 
    We can also specify constructor parameters by position. You can replace 'dsn' above with 0.

    We can also inject the dsn.
    PHP Code:
    $config->dsn_type 'mysql:dbname=testdb;host=127.0.0.1';
    $config->define('Connection')
        ->
    asClass('MySqlConnection')
        ->
    parameterType('dsn''dsn_type');

    $model $config->MyModelClass// Connection will be injected. 
    Notice how we made up for the lack of type information on the MySqlConnection constructor with parameterType.

    There is another form of parameterType. If the type is unspecified, the type defined in the class will be used, but the parameter will forced to be injected, even if it is optional.

    PHP Code:
    $config->Cache = new Cache('location');
    $config->dsn_type 'mysql:dbname=testdb;host=127.0.0.1';
    $config->define('Connection')
        ->
    asClass('MySqlConnection')
        ->
    parameterType('dsn''dsn_type')
        ->
    parameterType('optional'); 
        
    // force a value to be injected based on default type for this parameter

    $model $config->MyModelClass// Connection will be injected. 
    Property injection works the same way with propertyValue and propertyType. Setter injection works with setterValue and setterType. Both properties and setters will use the default type defined in the typehint or doc comment if the second parameter to xxxType is omitted.

    There is also a shortcut form for recording setter methods
    PHP Code:
        ->setValue('foo');  // same as ->setterValue('setValue', 'foo');
        
    ->setValue();  // same as ->setterType('setValue'); 
    And one last method for setting aliases of other types.

    PHP Code:
    $config->original 'foo';
    $config->define('derived')->asType('original');
    echo 
    $config->derived// 'foo' 
    The code so far:
    wact/config


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
  •