SitePoint Sponsor |
|
User Tag List
Results 101 to 116 of 116
Thread: Dependency Injection in PHP4
-
Sep 13, 2005, 08:49 #101
- Join Date
- Apr 2004
- Location
- germany
- Posts
- 4,324
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Care to explain a bit?
-
Sep 13, 2005, 08:58 #102
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by stereofrog
PHP Code:require_once('../simpletest/unit_tester.php');
require_once('../simpletest/reporter.php');
error_reporting(E_ALL);
function &single($klass) {
static $map = array();
if(!isset($map[$klass])) {
$e=(($c=count($a=func_get_args()))<2)?''
:'$a['.implode('],$a[',range(1,--$c)).']';
eval("\$map['$klass']=&new $klass($e);");
}
return $map[$klass];
}
// playtoys
// ids show that we have singletons
class App {
function App(&$db, &$logger) {
$this->id = rand();
$this->db = &$db;
$this->logger = &$logger;
}
function run() {
var_dump($this);
}
}
class DbLogger {
function DbLogger(&$db) {
$this->id = rand();
$this->db = &$db;
}}
class DB {
function DB($dsn) {
$this->id = rand();
$this->dsn = $dsn;
}}
// Config is able to handle everything by itself.
class Config {
var $dsn = "mysql://user:pass@host/db";
function &App() {
return single('App', $this->DB(), $this->Logger());
}
function &Logger() {
$l = &single('DbLogger', $this->DB());
// $l = &single('DbLogger');
// $l->db = &$this->DB();
return $l;
}
function &DB() {
return single('DB', $this->dsn);
}
}
class TestOfSingleton extends UnitTestCase
{
function TestOfSingleton() {
$this->UnitTestCase();
}
function test_referential_integrity() {
$conf =& new Config();
$app =& $conf->App();
$this->assertReference($conf->DB(), $app->db);
}
}
$test = new TestOfSingleton();
$test->run(new HtmlReporter());
-
Sep 13, 2005, 09:58 #103
- Join Date
- Apr 2004
- Location
- germany
- Posts
- 4,324
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Oh that's ill.
I'm sure someone will come with the fix though...
-
Sep 13, 2005, 12:24 #104
- Join Date
- Aug 2004
- Location
- California
- Posts
- 1,672
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by DougBTX
Christopher
-
Sep 13, 2005, 12:31 #105
- Join Date
- Nov 2001
- Location
- Bath, UK
- Posts
- 2,498
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by arborint
It could be simplified if you removed the ability to pass in paramaters to get. I've simplified my application so that I can use the class as a ServiceLoactor which happens to inject dependencies behind the scenes if need be, so I've renamed it Container instead of DI. It works fine as a plain registry too, which is helpful for me. As I'm not using the static access anymore, so a short name isn't as important as it was. The syntax looks more like this:
PHP Code:$c =& Container::getInstance();
$c->add('Checkout', 'MyCheckout', array('Store', 'Order'));
$c->add('Store', 'MyStore', array('Gateway'));
$c->add('Order', 'MyOrder');
$c->set('Gateway', $test_payment_gateway);
Edit: just added a simplified version without the null arguments support, and without autowrapping dependencies in an array, so you have to use an array even if you have only one dependency. It must be good, it has a KB less.
Edit 2: removed unneeded reference, and added needed reference
DouglasLast edited by DougBTX; Sep 14, 2005 at 03:38.
Hello World
-
Sep 13, 2005, 13:23 #106
- Join Date
- Aug 2004
- Location
- California
- Posts
- 1,672
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
As I recall, something like this will let you get rid of set():
PHP Code:function register ( /* $name, $classname, $signature = array() */ ) {
if (func_num_args() >= 2) {
$args = func_get_args();
$this->registry[$arg[0]]['constructor'] = array(
'classname' => $args[1],
'signature' => $args[2]
);
}
}
Christopher
-
Sep 13, 2005, 14:11 #107
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by arborint
PHP Code:function &call_constructor_array ( $type, &$array )
-
Sep 13, 2005, 14:30 #108
- Join Date
- Nov 2001
- Location
- Bath, UK
- Posts
- 2,498
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by arborint
PHP Code:class Klass
{
var $foo;
}
$klass = new Klass;
$klass->foo = 'bar';
function manipulate ( )
{
$args = func_get_args();
$klass =& $args[0];
$klass->foo = 'manipulated';
}
manipulate($klass);
echo $klass->foo;
Originally Posted by kyberfabrikken
DouglasHello World
-
Sep 13, 2005, 15:24 #109
- Join Date
- Apr 2003
- Location
- London
- Posts
- 2,423
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Hi...
Originally Posted by Travis S
yours, MarcusMarcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things
-
Sep 13, 2005, 16:34 #110
- Join Date
- Oct 2004
- Location
- Kansas City, MO
- Posts
- 68
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Hey Marcus,
Originally Posted by lastcraft
And yes... My ObjectFactory does get a container passed into it to look up other dependencies which could get you in a segfault loop pretty quick. In my preview release, I don't worry about that as it's really up to the implementor to figure that out. I just want to give him plenty of rope to hang himself with
-
Sep 13, 2005, 21:15 #111
- Join Date
- Sep 2003
- Location
- Wixom, Michigan
- Posts
- 591
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by arborint
With Douglas' latest revision, it looks like we are back to an implementation which requires two separate registration methods: add and set. Personally I don't think that the difference between the two is that clear in the naming, but the issue could be solved by allowing add (or "register", I am still inclined to believe that name is more adequate to the purpose of the method) to receive objects as well as class names. After all, most of the time that you want to pass an object, I imagine you would call it as:
PHP Code:$c->add('Checkout', 'MyCheckout', array('Store', new MyOrder()))
The term "add" always makes me think of linear lists.
PHP Code:$c->add('Checkout', 'MyCheckout1');
$c->add('Checkout', 'MyCheckout2');
$c->add('Checkout', 'MyCheckout3');
Another smaller issue that I see is that by going from create_function to eval(), now the "Eval'd" code is interpreted everytime that a new component is instantiated instead of only once during the registration call, though I am not sure what type of performance impact this may have.
At the end of the day, I too prefer an API that allows you to work with parameters and arrays at registration time, instead of code blocks; I just wonder if for all the situations where you may need DI (many times dealing with 3rd party libraries which you would rather not rewrite) can be accounted for in Douglas' version. How would you do setter / property injection? I think this is an important point to allow the DI container to work with any preexisting Plain Old PHP Object.Garcia
-
Sep 13, 2005, 23:59 #112
- Join Date
- Aug 2004
- Location
- California
- Posts
- 1,672
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by ghurtado
Originally Posted by ghurtado
PHP Code:$c->register('Checkout', 'MyCheckout', array('Store', new MyOrder());
// and
$c->registerObject('Checkout', new MyCheckout('Store', new MyOrder()))
Christopher
-
Sep 14, 2005, 01:51 #113
- Join Date
- Apr 2004
- Location
- germany
- Posts
- 4,324
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by DougBTX
PHP Code:class A {
function A(&$b) {
$this->b = &$b;
}}
class B {}
$c = &Container::getInstance();
$c->add('A', 'A', array('B'));
$c->add('B', 'B');
$aa = &$c->get('A');
$b0 = &$aa->b;
$b1 = &$c->get('B');
$b0->x = 99;
assert($b1->x == 99); // :((((
It must be good, it has a KB less.
PHP Code:// carping permitted
function &single($klass) {
static $map = array();
if(!isset($map[$klass = strtolower($klass)])) {
array_shift($a = func_get_args());
foreach($a as $k => $v)
if(is_object($v) && isset($map[$c = strtolower(get_class($v))]))
$a[$k] = &$map[$c];
$e = '';
if($c = count($a))
$e = '$a[' . implode('],$a[', range(0, $c - 1)) . ']';
eval("\$map['$klass']=&new $klass($e);");
}
return $map[$klass];
}
PHP Code:class Conf {
function &A() {
return single('A', $this->B());
}
function &B() {
return single('B');
}
}
// A and B see above
$c = new Conf;
$aa = &$c->A();
$b0 = &$aa->b;
$b1 = &$c->B();
$b0->x = 99;
assert($b1->x == 99); // :)
-
Sep 14, 2005, 03:36 #114
- Join Date
- Nov 2001
- Location
- Bath, UK
- Posts
- 2,498
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Simplification by changing the problem, can't complain too much about that
(and I've taken your better implode code too!)
Patch this:
PHP Code:$object =& call_constructor_array($classname, $dependencies);
return $object;
PHP Code:$sevice['object'] =& call_constructor_array($classname, $dependencies);
return $sevice['object'];
Cheers,
DouglasHello World
-
Sep 14, 2005, 04:26 #115
- Join Date
- Apr 2004
- Location
- germany
- Posts
- 4,324
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Yeah, that works now, good job.
Simplification by changing the problem
-
Sep 14, 2005, 05:01 #116
- Join Date
- Nov 2001
- Location
- Bath, UK
- Posts
- 2,498
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
The main thing for me me is that the only things you can put in the "Conf" registry (== $map) are instances of objects which have had their dependencies injected, you can't have any objects in there that were constructed somewhere else.
That means you can just have the static $map; in the same function as where you gather dependencies, shaving off a few lines of code, which is a good thing if you don't want to use $map as a normal registry.
Keeping that $map tied up also makes it harder to have the clear() method, and you can't have multiple instances of the registry. Again, both of those let you shave off a few lines of code.
The last thing that affects me is that it makes it harder to have multiple sets of "Conf" code. If you register something in one place, and then register something which depends on it later, you'll have to use different code to resolve those dependencies. It is scaffolding code that you've got to add each time you need it, rather than something built into the container.
I consider the bits in red to be scaffolding I'd rather not have, though you do need to have some, so I'd like as little as possible:
Code:class Conf { function &A() { return single('A', $this->B()); } function &B() { return single('B'); } }
Code:$c = &Container::getInstance(); $c->add('A', 'A', array('B')); $c->add('B', 'B');
Code:DI::add('A', 'A', array('B')); DI::add('B', 'B');
DouglasHello World
Bookmarks