The SitePoint Forums have moved.

You can now find them here.
This forum is now closed to new posts, but you can browse existing content.
You can find out more information about the move and how to open a new account (if necessary) here.
If you get stuck you can get support by emailing forums@sitepoint.com

If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

My first checkin defines a simple interfaces for the "new instance" capability by using the __call magic method. The function name corresponds to the class name. So, to create class Foo, with a parameter of 'hello', you can use:

PHP Code:

$registry = new WactRegistry();
$obj = $registry->Foo("hello");

This is the equivalent of

PHP Code:

$obj = new Foo("hello");

So what good is that? Well, if you leave off required parameters, the container will attempt to instantiate the necessary objects as best it can.

Thanks to the prompting of some members I finally got around to cleaning up a basic injector I built based on my experience with Grails. Essentially it reads 'services' from a location as a baseline registry (as well as manual registration), then uses reflection to interrogate objects that bind dependencies into them. It's 'limited' in that those objects that get injected need to be declared public, but the services are publicly accessible through the registery anyway, so no biggy. http://mikenovember.com/2008/another...ency-injector/ if you're interested, without trying to derail the thread too much .

How does "WactAssembler" grab you? WactInjector? WactWiring? I tend to think of a registry as a simple box to stuff things in rather than an object which knows how to wire up the object graph, manage lifecycle, etc.

phpimpact:
Thanks on the __call. I don't think a typical php program will actually create a lot of object this way. Perhaps optimizations will be required later?

McGruff:

I'm open to renaming, but not right now.

Actually, my next step was probably going to be some boring test cases for optional parameters and what happens if you try to create a class that doesn't exist.

In the testCanInstantiateByInterface, adding a register method may not be the best thing, because I've used the "methods" axis of the class as the new instance call. (This was intentional as both a convenience syntax and to limit the complexity of the interface.) I'm not ready for that part of the interface where we define components yet.

So, my question is, in the absence of the register statement, how and should the container satisfy the request for ->Foo() in your example?

A quick question abut test cases. I've been having a discussion lately about this. Isn't that a "test script" instead of a "test case"? For me a use case becomes a test case, and then a test case finally becomes test script, right? Or wrong....?

in the absence of the register statement, how and should the container satisfy the request for ->Foo() in your example?

I think it definitely should. I'd use a DI container to provide an easy way to tweak the behaviour of an app. There'd be a "well-known" configuration file, or class somewhere, which sets up the container to use the chosen classes (or holds the information required to do so).

With a register/etc method ruled out, WactRegistry could maybe read some kind of configuration file - YAML, XML, or whatever. Maybe something new designed for the purpose.

That keeps the WactRegistry interface simpler - on the other hand you have a configuration file format to learn. I haven't got a huge amount of experience using DI containers but I think I might prefer the $injector->register(...) style. They both look pretty similar when you want to pull the setup out to a single configuration file but maybe doing it in php is a little more flexible at other times if you just want to stick a couple of register() steps in a class method without having to create a separate YAML/etc file.

Maybe WactRegistry could do a simple check for a class implementing an interface (abstract class, etc) in the list of declared classes? A bunch of include calls would be the configuration (or autoload could try to hunt them down - could be slow ). This might be limiting if you need several different classes from the same inheritance tree so I'm not sure if it's really worth considering.

A quick question abut test cases. I've been having a discussion lately about this. Isn't that a "test script" instead of a "test case"? For me a use case becomes a test case, and then a test case finally becomes test script, right? Or wrong....?

In SimpleTest, the classes all extend a UnitTestCase. Then there are test methods, and individual assertions in test methods. I'm actually not sure which of these it would be correct to refer to as a "test case".

I'd see a script as the actual executable bit of code. Test runners might run a single method, a whole class, or a whole suite of tests.

I'd prefer to see a prefix there to improve readability and to reflect the function nature of the call.

Given there's a prefix, you can easily make the distinction between instantiation and other calls, such as register(), so the interface still provides the same simplicity while allowing the free extension of the API.

Regarding optional parameters, would it be worth considering a way to configure their injection too?

I think there has been a misunderstanding. I didn't mean to say that there would be no way to configure the container to provide a specific answer to a request for Foo(). I just meant that configuration wouldn't take the form of a method and might have a different set of parameters.

So, I think the question remains. In the absence of configuration information, when the container is asked to create a class that implements an interface or an abstract class, what should it do?

Throw an exception?

Search for a concrete class that can match the request? What if there is more than one? Might this be hard to follow and debug?

I hadn't though of prefixes. I think when the configuration part goes in, there will be a way to specify that a value should be injected into an optional parameter. There is just not a way to do that in the constructor style interface.

except that it will always return the same object in subsequent calls.

You can also set values

PHP Code:

$registry->foo = new StdClass();
$registry->bar = "any data";

If it is necessary to inject a value and a singleton is available, the singleton will be injected. Otherwise, a new instance will be created (as in the last version). This newly created instance will not become a singleton. Is this the best behavior?

I ran into what appears to be a SimpleTest bug in testing this. The following assertion fails.

Means that other kinds of factories could be added to the DI container.

Regarding selecting the appropriate implementation when handed an interface...hm...tricky without an explicit registration. If there is only one class that fulfils the interface, then I could not recommend just instantiating it. It means that app configuration would be set by file includes, which kinda' sucks. And I wouldn't want to try to figure out the interactions of this with autoload. If there are choices, then it should be up to the user anyway, so just throw regardless.

Which brings it back to how you do registration. YAML? XML? PHP?

I ducked this in Phemto of course, going for an explicit register() and selecting the most recent registered class (or was it the most specific...can't remember?). Besides, I wanted to make the implementation choices, but not the wiring, as visible as possible in the application.

It's simply that the difference isn't self-explanatory in contrast with something like getSingleton and getNewInstance (not that I'm suggesting those as alternatives).

Now that I think about it, lifecycle choices possibly belong in a separate WactRegistry configuration stage. Clients which ask the registry for objects don't (I think) need to know about lifecycle. This seems like an injector/registry responsibility.

Actually I do feel a bit uneasy about direct property access in general. $foo->bar feels like rummaging around in someone's private underwear drawer. That's a whole other issue though...

I'm willing to abandon the method = class assumption, or at least weaken it to Norbert's callClass naming convention. However, I am unlikely to give up on the property notation as the primary technique for retrieving values.

I might be convinced that a property that hasn't been assigned a value should raise an exception, rather than creating a new instance. As you can see from tonight's code, making that change to the protected getSingleton method would also remove the ability to create instances of unregistered classes while satisfying the dependencies for another object.

In tonight's code, by having provideRequiredDependency call getSingleton, I've created a preference for singletons in satisfying required dependencies. The container will never create more instances of objects than necessary to satisfy its requirements.

As far as config files go, I've been trying to avoid them for a couple years. I've finally come to the conclusion that any non-trivial application will grow one. So, you might as well have good support for them.

I plan to allow both a PHP interface for setting configuration information (beyond the current __set) as well as allowing other non-code formats.

But that's for later.

Tonight's code is primarily a refactoring of the overly large __call method into smaller methods.

Progress is slow. Today I extracted yesterday's refactorings into a separate class, WactRegistryFactory. Now, this class can gain its own independent test cases. The new class also needs some work. You can also see the beginnings of how the WactRegistry will handle registrations. No test case changes today. (A true refactoring.)

Now, what you have there is a local registry, that acts as the main container for all your objects and dependencies. Does that mean that you are discarding the possibility of having a global registry in the future? If that's not the case, shouldn't WactRegistry be called WactDi. And WactDi use WactDiStorage as the local storage container.

So, you end up having something like this:

PHP Code:

class WactDi {}class WactDiFactory {}class WactDiStorage {}

I think that Registry should not be involved in the creation of objects and should only act as a container. I would leave that to the object responsable of injecting, like WactDi for example. Also, I think a clear separation of local and global containers is needed.

Separating registry from factory, is a good move. The factory is irrelevant for the user -- Only the registry should be part of the public interface. I'd like to see createInstance() become public. That way, the component can also be used for creating transient instances (Which might have shared dependencies).

It's perhaps nitpicking, but I would prefer the name Container over Registry. Registry implies that you use a certain type of implementation (the Registry pattern), while Container might refer to different backends.

Yes, I find Container to be much more intuitive. But, I would include Container inside Di. So you end up having WactDi, a name that can automatically be associated with the pattern.

PHP Code:

class WactDi {}class WactDiFactory {}class WactDiContainer {}

class WactDiStorage {}

WactDi can have 2 static methods, WactDi::createInstance() and WactDi::replaceObject() in case you want to use this component in a testing environment.

Or, a user can create an instance of WactDiContainer, that stores the objects created by WactDiFactory inside WactDiStorage (that implements Iterator and WactDiStorageInterface, so users can register their own storage containers).

--
EDIT:

The idea of having a static method to create an instance and replace objects comes from a TDD expert and creator of PHPSpec, P&#225;draic Brady. You can read his article here.

The more I think about this, the more I think I got it right with Phemto (regarding Singletons that is, nothing else). The lifecycle should be set at registration time. The client of the class just shouldn't care. After all, there are other lifecycles such as "session" and "persistent" and "persistent in memory". If anything, these are more important than Singleton.

The use case I'm thinking of is a database connection. It could have the following lifecycle rules:

1) An instantiation will only be a connect() if there aren't currently enough connections. The real act of instantiation is to take ownership of the connection. I'm thinking of the Mysql bug that lets database sessions crosstalk over transactions.

2) Destruction is actually just relinquishing ownership, ensuring unfinished transactions are rolled back, and returning the connection to a pool.

I guess you would say that a ConnectionPool factory should do all of this, and that ConnectionPool is the Singleton. This is a legitimate answer, and would push a lot of features out of scope.

In response to that I would say that if I'm writing an application in a framework, I'd write it initially without connection pooling. I'd then want to plug it in later, ideally without changing more than a configuration option.

I might also want to use a third party pooling solution (not necessarily a database one...pooling could be a crosscut), say when I switch from/to APC. I probably have a customised Connection as well, and that will be the one referenced in my code to date.

BTW: What use cases/stories are driving the current development? I take it acceptance tests are synonymous with unit tests at this early stage.

IMO, the most inspiring PHP DI container so far was the one created by Chris Corbyn, but unfortunately was left unfinished.

I still have it and actually registered a project on SF.net for it. The new version of Swift Mailer was is 100&#37; 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.