I have used Dice PHP DI container for quite a while and it seems the best in terms of simplicity of injecting dependencies.
From Dice Documentation:
class A {
public $b;
public function __construct(B $b) {
$this->b = $b;
}
}
class B {
}
$dice = new \Dice\Dice;
$a = $dice->create('A');
var_dump($a->b); //B object
However, when you have to use objects that are directly dependent on each other, the finall result is server error, because of the infinite loop.
Example:
class A {
public $b;
public function __construct(B $b) {
$this->b = $b;
}
}
class B {
public $a;
public function __construct(A $a) {
$this->a = $a;
}
}
Author of Dice says that there is no way to construct an object from the A or B classes. As:
An 'A' object requires a 'B' object to exist before it can be created
But a 'B' object requires an 'A' object to exist before it can be created
Author says, that this limitation concerns all DI containers!
Question:
What would be the best solution for overcoming this problem nicely without changing initial code? Could anyone provide an example of using other DI containers, when it would be possible to run exampled code without bulky workarounds?
As mentioned on your post on the Dice github ( https://github.com/TomBZombie/Dice/issues/7 ), the only way to resolve without removing the circular dependency is to refactor one of the classes to use setter injection:
class A {
public $b;
public function __construct(B $b) {
$this->b = $b;
}
}
class B {
public $a;
public function setA(A $a) {
$this->a = $a;
}
}
This allows the objects to be constructed:
$b = new B();
$a = new A($b);
$b->setA($a);
With the original code:
class A {
public $b;
public function __construct(B $b) {
$this->b = $b;
}
}
class B {
public $a;
public function __construct(A $a) {
$this->a = $a;
}
}
You cannot construct it and run into the same problem as the container:
$b = new B(new A(new B(new A(new B(.............))))
The problem with having a container work around this issue using a hack such as ReflectionClass::newInstanceWithoutConstructor is that your objects are now dependent on creation logic which uses this method. You essentially couple the code to the container which is a poor design as your code is now no longer portable and cannot be used without the container to perform the object construction.
You have a circular dependency, which is very hard to solve. The first thing to do is to try to get rid of this circular dependency by refactoring your classes and how they interact.
If you really can't manage to do it, there are solutions. I'll copy-paste my answer from Self-referencing models cause Maximum function nesting level of x in Laravel 4:
Setter injection
Rather than injecting a dependency in the constructor, you can have it injected in a setter, which would be called after the object is constructed. In pseudo-code, that would look like that:
$userRepo = new UserRepository();
$cartRepo = new CartRepository($userRepo);
$userRepo->setCartRepo($userRepo);
Lazy injection
I don't know if Dice does support lazy injection, but that's also a solution: the container will inject a proxy object instead of the actual dependency. That proxy-object will load the dependency only when it is accessed, thus removing the need to build the dependency when the constructor is called.
Here is an explanation on how lazy injection works if you are interested: http://php-di.org/doc/lazy-injection.html
Related
I have class A, B, C, D
class A {
public $b;
public $c;
public $d;
// Other properties
function __construct() {
$this->b = new B();
$this->c = new C();
$this->d = new D();
}
function process() {
$x = new ExternalClass($this->b, $this->c, $this->d)
// $x = new ExternalClass($this)
}
// other functions.
}
Here, is there any performance deference passing parameter as $this instead of $this->b, $this->c, $this->d ?
Later, i may need to send more objects $this->e, $this->f etc. Instead of passing each object variables, if i passing $this object I can access whatever objects i want. But I want know is there any performance issue involved in this.
In this case, you really don't need to worry about the performance. You never measure difference in real world application.
What I would be woried much more is your implementation. Read something about Dependency Injection (DI) and try decouple your application to more classes using Single Responsibility Principle (SRP).
I am trying to assign an ancestor(A) to descendant(B) type variable and call descendant method on ancestor in PHP but I can't get it working.
class B extends A{
public function c(){ echo 'B.c'}
}
I have tried:
Rewriting B constructor
class B extends A{
public function __construct($parent)
{
$this = $parent;
}
...
}
$this cannot be rewritten, so it's a no-go.
This thing I am forgetting how is it called
function d(B $b){
$b = new A();
b.c();
}
Explicit type casting
function d(){
$b = (B) new A();
b.c();
}
Or
function d(){
$b = settype(new A(),'B');
b.c()
}
This doesn't work either as settype only allows certain types not other user defined object types.
All the information I've seen suggests that there is no clean way to modify an object's type. (PHP merely fakes certain kinds of casting, e.g. there is an (int) operator but type int is unknown by itself.)
The approaches I've found are of two types:
Serialize your object, hack the string to modify its type, deserialize. (See here or here-- equivalent). Or
Instantiate a new object and copy over all its properties. (See here)
I'd go with the second approach since you seem to be dealing with a known object hierarchy:
class B extends A {
public function __construct(A $object) {
foreach($object as $property => $value) {
$this->$property = $value;
}
}
}
If you have some control over the creation of the original object, consider implementing a factory pattern so that you object is created with type B from the start.
I'll try to explain what the problem is with code
class A {
protected $a;
}
class B extends A {
protected $b;
}
$b = new B();
$b->c = true;
get_class_properties($b);
I need this function to return me only the properties which the class was declared with, excluding inherited and dynamically created properties. Expected result of the code is
Array (
[0] => string(1) 'b'
)
Is this possible without the use of reflection classes?
I came up with a solution, of course after I posted the question. I'll just add the answer in case anybody is looking for this in future.
Also if anyone has a better solution, please post!
What I came up with is I needed to add an additional method for getting the property names
public function getOwnProperties(){
return get_class_vars(__CLASS__);
}
So in the example from the question it would look like
class A {
protected $a;
public final function getOwnProperties(){
return get_class_vars(get_called_class());
}
}
class B extends A {
protected $b;
}
$b = new B();
$b->c = true;
print_r($b->getOwnProperties());
IMPORTANT
This solution does not satisfy one of the requirements from the question - it gives inherited properties as well.
I have a small problem. I've tried searching, but I can't get the search terms quite right and was hoping someone could help.
I have an include on every page in my system that works something like this:
<?PHP
require_once("class.system.php");
require_once("class.mysql.php");
$oMySQL = new MySQL();
$oSystem = new SystemClass();
... ?>
But I have a problem. As you may guess - the MySQL class is a bunch of functions that I use to make MySQL calls easier. This isn't the only example of where I want to use it but it's a good example.
I have functions in the system class I want to be able to reference the MySQL class (And vice versa...).
As an example, I have a function in the system class that will populate a session variable with data from MySQL. The only way I can think of doing this (Which I know is wrong...) is:
class SystemClass {
function PopulateSession(){
global $oMySQL;
if($oMySQL->Select('abc')){
$_SESSION['def']= blahblahblah;
return true;
} else {
return false;
}
}
}
It works, but it means every function I want to use it, I have to use global, which I'm sure is very bad practice. Could someone advise??
Thanks
What you encountered is called composition. A good solution would be to use a dependency injection framework. An easy solution is to roll with constructor parameters.
public class A {
private $b;
public function __construct($b) {
$this->b = $b;
}
}
$b = new B;
$a = new A($b);
Or, as a more flexible solution, when you have mutual dependencies:
public class A {
private $b;
public function setB($b) {
$this->b = $b;
}
}
public class B {
private $a;
public function setA($a) {
$this->a = $a;
}
}
$a = new A;
$b = new B;
$a->setB($b);
$b->setA($a);
But the downside is that as the number of dependencies grows, it's hard to manage and remember to set all the dependencies. This is exactly the reason why Dependency Injection frameworks are popular.
I have:
class A{
public $name = 'A';
public $B;
public function B_into_A($b)
{
$this->B = $b;
}
}
class B{
public $name = 'B';
public $A;
public function new_A_into_B($a)
{
$this->A = new $a;
}
}
$a = new A;
$b = new B;
$a->B_into_A($b);
$b->new_A_into_B('A');
Is this a good way to insert another class inside a "main" class at the beginning of the runtime?
Should I use references?
(Background: I currently work on a MVC framework in which I have to handle many classes within some main classes e.g. bootstrapper, wrapper, modules, adapter etc.)
Yes and No...
Your first function call was fine, I would just use a more standard name:
$a = new A;
$b = new B;
$a->setB($b); // B_into_A is a little bit of a whacky function name
Your second call, it doesn't really make sense to pass a string and then create the object (Unless you're looking for some sort of factory). If you want B to own A:
$b->new_A_into_B( new A );
public function new_A_into_B($a)
{
$this->A = $a;
}
Again i don't like the name.. Id probably go with setA() there as well.
Passing an object instead of its class name makes sense because then it is easier for the garbage collector to guess when it is not needed anymore.
<?php
class A {
public $B;
}
class B {
public $A;
}
$a = new A;
$b = new B;
$a->B = $a;
$b->A = $b;
Furthermore, I'd try to get rid of the setter methods. In practice the only benefit they provide is to validate input. Since it's not the case here, I'd advise you to directly assign the classes.
You asked "Should I use references?". Actually all objects are passed by reference since PHP5. The only way to get an exact copy of them is to use the clone keyword.
By the way, you do not need to store the class name within each object. Just use get_class().