I like the responses so far, I think we are down the right path.

Originally Posted by
sweatje
Now due to timing of when we wanted to create the payment gateway and when we wanted to process the order I needed to create a setter method for the order. Is this allowed in your design? If not, there are tricks to cope with that...
Yes, of course. For the sake of completeness, let me introduce a mock Payment class.
PHP Code:
class Payment {
var $_order;
function setOrder(&$order){
$this->_order =& $order;
}
function checkPayment(){
// do some calls to remote gateway
// $curl = curl_init('https://www.example.com/payment_gateway');
// curl_setopt($curl, CURLOPT_POSTFIELDS, $this->_order);
// $status = curl_exec(); // needless to say, this doesnt work
if($status == STATUS_SUCCESS){
return true;
} else {
return false;
}
}
}
"Payment" can be extended so long as the child Payment class implements "checkPayment". I have commented out the lines that would actually process the cURL request since they are just there to "add fat".
Marcus,
I like the general style of solving the dependencies with Phemto at the top level, in that the wiring is rather minimal.
PHP Code:
$injector = Injector::instance();
$injector->register('MyOrder');
$injector->register('MyStore');
$injector->registerSingleton('PostRequest');
$injector->register('PaypalGateway');
This top level service locator aspect of it seems rather elegant, in that you are not required to know the dependencies, it seems that Phemto is smart enough to figure them out.
However, does this work with setter injection currently as well as constructor injection? I suppose that would be ideal, although my guess is that you get around that problem by requiring registered services to instanciate / use the service locator whenever they need a class instance themselves. Which leads me to...

Originally Posted by
DougBTX
IMO, from this stage, the above code shouldn't need to be changed in order to inject the dependencies.
This concept is key here. As others have pointed out, as soon as the "services" have to be aware of the injector, the injector starts to seem a little bit more like a service locator. I think it is that level of coupling that we would want to avoid, by using a "pure" dependency injector. After all, the greatest incentive anyone would have to use DI in their projects is that of not having to modify any of their classes. Sure, you need to follow some basic rules, like using setters or construction arguments for external dependencies, but these are good practises any developer should be following anyway.
So lets see how we can design the top level wiring of all these classes without them being aware of being injected. I like to think of it as an unsuspecting child who is brought to the doctor for vaccinations, and when he is looking the other way, bam! by the time he's thinking about crying, he has already been "injected" and is ready to go home
(ok, lame analogy, but you get my point)
So I guess in our case, we would need something that allows us to do:
PHP Code:
// from the bottom up, just because
$injector->registerService('Order', 'PostOrder'); // extends 'Order'
$injector->registerService('Payment', 'PaypalPayment');
$injector->registerService('Store', 'CameraStore');
$injector->registerService('Checkout', 'TestCheckout');
// this bottom part should never need to change if the
// interfaces for these classes remain stable
$injector->registerSetter('Payment', 'setOrder', 'Order'); // setter injection
$injector->registerSetter('Store', 'Store', 'Payment'); // constructor injection
$injector->registerSetter('Checkout', 'Checkout', array('Store', 'Order')); // constructor injection
I kinda like this syntax, in that it is very close to a config file "look", which is ultimately where you might want to elevate these types of wiring decisions in a full blown framework. But at the same time it allows you to cleanly see your class dependencies and change the selected implementations of each class in one single centralized place.
While it may seem a little verbose, the PHP5 version of the injector would probably be quite a bit more concise by using reflection.
What do you guys think?
PS.

Originally Posted by
DougBTX
And I'd also start wondering about the Checkout class itself at this stage... a class for one line of code?
Well, in the full blown real-world case, this is not so, but I wanted to keep things clean to be able to see the DI at use more clearly. The main reason I introduced it here, is for an additional level in the dependency tree, but you can assume that the class has a little more meat in the execute method. Like this if you want:
PHP Code:
function execute(){
if($this->_store->submitOrder($this->_order)){
Logger::log('Successful order', $this->_order);
mail(STORE_OWNER, 'New Order', 'A new order has been placed');
return true;
} else {
return false;
}
}

Originally Posted by
lastcraft
p.s. Thanks for pasting in those tabs. Grrrr...
"Thanks" or "Grrrr..." ?
Bookmarks