I have two classes which depending on each other:
class A
{
public function __construct(B $b)
{
$this->b = $b;
}
}
class B
{
public function __construct(A $a)
{
$this->a = $a;
}
}
And I need to wrap them through Pimple like this:
$c = new \Pimple();
$c['aService'] = function($c){
return new A($c['bService']);
}
$c['bService'] = function($c){
return new B($c['aService']);
}
But unfortunately I get cycling:
Fatal error: Maximum function nesting level of '100' reached, aborting!
Is there any way to reach this cross-dependency without cycling? Or I can use only unidirectional dependencies?
This reminds me of baboushka's
Of course you're bound to get infinite recursion here. Both functions call each other, each time returning a new instance, that is passed the return value of the call to their function-counter part, which in turn calls the function again, which calls the other function again, which calls....
Bottom line: when you have 2 classes that depend on each other from the get-go (__construct), your design is probably flawed.
The way you've defined both constructors, you'll never be able to create an instance of the classes. Simply because you need to instantiate both classes at the same time.
You can't, you simply cannot do that.
Try this:
class A
{
public $b = null;
public function __construct(B $b = null)
{
$this->b = $b;
}
public function setB(B $b = null)
{
if ($b === null)
{
$b = new B($this);//pass A here
}
$this->b = $b;
return $this;
}
}
class B
{
public $a = null;
public function __construct(A $a = null)
{
$this->setA($a);
}
public function setA(A $a = null)
{
if ($a === null)
{
$a = new A($this);//pass B here
}
$this->a = $a;
return $this;
}
}
By setting the default value of the constructor arguments to null, passing an instance has become optional, so now you can do this:
$a = new A;
$b = new B($a);
//or even:
$bFromA = $a->b;
BTW: always declare your properties beforehand. It'll speed up your classes.
Personally, I'd use a getter and a setter, and lazy-load the dependency, but I would keep the constructor as is:
class A
{
//protected, or private. Access this via getter/setter
protected $b = null;
public function __construct(B $b = null)
{
$this->setB($b);
return $this;
}
//setter, allows injection later on
public function setB(B $b = null)
{
$this->b = $b;//allow to set to null
return $this;
}
//getter, lazy-loader:
public function getB()
{
if ($this->b === null)
{//create new instance, if b isn't set
$this->setB(
new B($this)
);
}
return $this->b;
}
}
class B
{
protected $a = null;
public function __construct(A $a = null)
{
$this->setA($a);
return $this;
}
public function setA(A $a = null)
{
$this->a = $a;
return $this;
}
public function getA()
{
if ($this->a === null)
{
$this->setA(
new A($this)
);
}
return $this->a;
}
}
Using Pimple:
$c['aService'] = function($c)
{
return new A;
};
$c['bService'] = function($c)
{
return new B;
};
$b = $c->bService;
$b->getA();//works just fine
Related
class foo
{
public $bar = 1;
}
#foo.bar = 2; //change class variable and affect all after
$a = new foo();
$a->bar = 2;//avoid DRY
$b = new foo();
$b->bar = 2;//avoid DRY
echo $a->bar;
echo $b->bar;
Is there any way I can change class variable, which would affect all instances created after?
I want to change one default value, but I don't want to keep repeating every time I instantiate a new object.
You'd need to make the variable static.
class foo
{
public static $bar = 1;
}
$a = new foo();
$a::$bar = 2;
$b = new foo();
echo $b::$bar; // 2
You can also do Foo::$bar = 2; to set the variable for all instances.
Having a static property works great if the constructor should be called each time. If you're attempting to chain invocations, however, you might use clone instead.
class Foo
{
public $bar = 1;
public function bar(?int $bar = null): Foo
{
$this->bar = $bar ?? $this->bar;
return $this;
}
public function copy(?int $bar = null): Foo
{
return $this->fork()->bar($bar ?? $this->bar);
}
public function fork(): Foo
{
return clone $this;
}
}
$a = new foo();
$a2 = $a->copy();
$b = $a->copy(2);
$b2 = $a->copy();
$c = $b->copy(3);
$c2 = $b->copy();
https://3v4l.org/Fevli
Note, there is also a __clone() magic method that allows you to configure the actual cloned properties that go along (e.g., reset or increment, etc).
Here's method using static::with() methodology, which might fit best for the DRY approach of preconfiguration.
class Foo
{
public const DEFAULT_BAR = 5;
public $bar = FOO::DEFAULT_BAR;
public function bar(?int $bar = null): Foo
{
$this->bar = $bar ?? $this->bar;
return $this;
}
static public function withBar(?int $bar = null): Foo
{
return (new self())->bar($bar ?? Foo::DEFAULT_BAR);
}
public function fromBar(?int $bar = null): Foo
{
return (new self())->bar($bar ?? $this->bar);
}
public function fork(): Foo
{
return clone $this;
}
}
$a = Foo::withBar();
$b = Foo::withBar(4);
$b2 = $b->fromBar();
$c = Foo::withBar();
https://3v4l.org/qI8SY
How do we conditionally chain methods in PHP? For example, this works fine:
$a->foo()->bar->baz->qux();
However, depending on a condition, I'd like to chain some methods but not others. Basically, shorten the following code:
if ($cond === true) {
$a->foo()->baz();
} else {
$a->foo()->bar();
}
Ideally something like the following would work:
$a->foo()
->bar()
($cond === true) ? ->baz() : ->qux()
->more();
Additionally, how would we conditionally chain a method (or not) depending on a condition? For example:
$a->foo()
->bar()
if($cond === true) ->baz()
->more();
The Self-Explanatory Mock-Snippet below (which you may Quick-Test Here) shows how you could do that
<?php
class Test{
protected $prop1;
protected $prop2;
protected $prop3;
protected $prop4;
public function __construct() {
}
public function setProp1($prop1) {
$this->prop1 = $prop1;
return $this;
}
public function setProp2($prop2) {
$this->prop2 = $prop2;
return $this;
}
public function setProp3($prop3) {
$this->prop3 = $prop3;
return $this;
}
public function setProp4($prop4) {
$this->prop3 = $prop4;
return $this;
}
}
$a = 2;
$b = 7;
$cond = ($a > $b);
$cond2 = ($b > 50);
$test = new Test;
$test->setProp1(2)->{($cond === true) ? 'setProp4' : 'setProp3'}(11);
$test->setProp3(3)->{($cond2 === false) ? 'setProp2' : 'setProp4'}(6);
var_dump($test);
//YIELDS::
object(Test)[1]
protected 'prop1' => int 2
protected 'prop2' => int 6
protected 'prop3' => int 3
protected 'prop4' => null
What you're looking for is variable methods (see example #2). They allow you to do something like this:
class a {
function foo() { echo '1'; return $this; }
function bar() { echo '2'; return $this; }
function baz() { echo '3'; return $this; }
}
$a = new a();
$cond = true;
$a->foo()->{($cond === true) ? 'baz' : 'bar'}();
// Prints 13
$cond = false;
$a->foo()->{($cond === true) ? 'baz' : 'bar'}();
// Prints 12
Here's a way that lets you set up requirements for each of the function calls. Note that this is just as hard to maintain as the previous solution, if not harder. You'll probably want to use some sort of configuration and the ReflectionClass's getMethods function, too.
class a {
function foo() { echo '1'; return $this; }
function bar() { echo '2'; return $this; }
function baz() { echo '3'; return $this; }
}
function evaluateFunctionRequirements($object, $functionRequirements, $condition) {
foreach ($functionRequirements as $function=>$requirements) {
foreach ($requirements as $requiredVariableName=>$requiredValue) {
if (${$requiredVariableName} !== $requiredValue) {
continue 2;
}
}
$object->{$function}();
}
}
$a = new a();
$functionRequirements = array('foo'=>array(), 'bar'=>array(), 'baz'=>array('condition'=>true));
$condition = true;
evaluateFunctionRequirements($a, $functionRequirements, $condition);
// Prints 123
$condition = false;
evaluateFunctionRequirements($a, $functionRequirements, $condition);
// Prints 12
Notes: This has the added even harder to maintain of requiring the functions in order for the $functionRequirements array. Additionally, this rudimentary example has only one possible condition var passed, update to another setup for getting more $requiredVariableName vars with func_get_args. You'll also want to verify that the methods passed in via $functionRequirements are is_callable() safe.
Try this by assigning the chaining to variable
$a = $a->foo();
if ($cond === true) {
$a = $a->baz();
} else {
$a = $a->bar();
}
$a->more();
Another way to solve this is to create a method when (or name it whatever makes sense to you):
public function when($condition, $callback)
{
if ($condition) {
return $callback($this) ?: $this;
}
return $this;
}
Of course, you can extend it to accept additional arguments if you need to pass them to your methods foo, bar, etc...
And the usage with chaining would be:
$a->when($cond === true, function ($a) {
return $a->foo();
})->when($cond !== true, function ($a) {
return $a->bar();
}
)->baz(); // a regular chaining method without condition
I need to assign b value in a inside the method onec, but its failing. Please let me know what I am doing wrong here:
<?php
class One {
public $a = 10;
public $b = 20;
public static function onec() {
$this->a = $this->b;
return $this->a;
}
}
echo One::onec();
?>
Use the self keyword. The $this keyword is not accessible under static context. Also, you should make your variables static
Like this..
<?php
class One {
public static $a = 10;
public static $b = 20;
public static function onec() {
self::$a = self::$b;
return self::$a;
}
}
echo One::onec();
You use $this in static function.
http://www.php.net/manual/en/language.oop5.static.php
<?php
class One {
public $a = 10;
public $b = 20;
public static function onec() {
$obj = new One();
$obj->a = $obj->b;
return $obj->a;
}
}
echo One::onec();
Use this code
class One {
public $a = 10;
public $b = 20;
public function onec() {
$this->a = $this->b;
return $this->a;
}
}
$obj = new One();
echo $obj->onec();
So I recently ran across a code segment like this:
private static function FOO() {
static $c = null;
if ($c == null) {
$c = new ParentClass_InnerClass();
}
return $c;
}
So what is up with this code? How is this different from:
private static $C;
//other methods and fields
private static function FOO() {
if(!$self::c) {
$self::c = new ParentClass_InnerClass();
}
return $self::c;
}
Or are they even the same concept?
They are, essentially, the same concept, though the scope is different:
class Foobar
{
private static $c = null;
public static function FOO()
{
if (self::$c === null)
{
self::$c = new stdClass;
}
return self::$c;
}
public static function checkC()
{
if (self::$c === null)
{
return false;
}
return self::$c;
}
}
Foobar::checkC();//returns false, as $c is null
//function checkC has access to $c
$returned = Foobar::FOO();//returns object
if ($returned === Foobar::checkC())
{//will be true, both reference the same variable
echo 'The same';
}
Whereas, if we were to change the code to:
class Foobar
{
public static function FOO()
{
static $c = null;
if ($c === null)
{
$c = new stdClass;
}
return $c;
}
public static function checkC()
{
if ($c === null)
{
return false;
}
return $c;
}
}
We will get a notice when calling checkC: undefined variable. the static variable $c is only accessible from within the FOO scope. A private property is scoped within the entire class. That's it really.
But really, do yourself a favour: only use statics if you need them (they're shared between instances, too). And in PHP, it's very rare you'll actually need a static.
They're the same concept, except that in the first code block, the static variable is local to the function, and such a static variable could be used in any function, even outside the context of a class:
function test() {
static $counter = 0;
return $counter++;
}
echo test(); // 0
echo test(); // 1
In the top example you can access $c only from the local scope (FOO()) and in the bottom example you can access $c from the entire class, so not only form FOO().
One of my dreams is to use python rich comparison (something like __eq__) on php objects.
class A {
public $a = 1;
public function __eq__($other) {
return $this->a == $other->a;
}
}
class B {
public $a = 2;
public function __eq__($other) {
return $this->a == $other->a;
}
}
class C {
public $a = 1;
public function __eq__($other) {
return $this->a == $other->a;
}
}
$a = new A();
$b = new B();
$c = new C();
echo $a == $b; //false
echo $a == $c; //true
I'd like to have some smart mechanism to fast-compare models (objects) on database id, for example.
Is it possible in some way in PHP?
No, it isn't. A common way to achieve this is to use an equals() method, but there isn't any magic method. You'll have to call it manually. For example:
<?php
class User
{
private $id;
public function __construct($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function equals(User $user)
{
return $this->getId() === $user->getId();
}
}
$user1 = new User(1);
$user2 = new User(2);
var_dump($user1->equals($user2)); // bool(false)
var_dump($user2->equals($user1)); // bool(false)
?>
Which, I think, isn't much different from:
var_dump($user1 == $user2);
var_dump($user2 == $user1);
Anyway, my example will work even using the == operator, since it will compare the values of all the properties.