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.
Related
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().
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
In effect, if I have a class c and instances of $c1 and $c2
which might have different private variable amounts but all their public methods return the same values I would like to be able to check that $c1 == $c2?
Does anyone know an easy way to do this?
You can also implement a equal($other) function like
<?php
class Foo {
public function equals($o) {
return ($o instanceof 'Foo') && $o.firstName()==$this.firstName();
}
}
or use foreach to iterate over the public properties (this behaviour might be overwritten) of one object and compare them to the other object's properties.
<?php
function equalsInSomeWay($a, $b) {
if ( !($b instanceof $a) ) {
return false;
}
foreach($a as $name=>$value) {
if ( !isset($b->$name) || $b->$name!=$value ) {
return false;
}
}
return true;
}
(untested)
or (more or less) the same using the Reflection classes, see http://php.net/manual/en/language.oop5.reflection.php#language.oop5.reflection.reflectionobject
With reflection you might also implement a more duck-typing kind of comparision, if you want to, like "I don't care if it's an instance of or the same class as long as it has the same public methods and they return the 'same' values"
it really depends on how you define "equal".
It's difficult to follow exactly what you're after. Your question seems to imply that these public methods don't require arguments, or that if they did they would be the same arguments.
You could probably get quite far using the inbuilt reflection classes.
Pasted below is a quick test I knocked up to compare the returns of all the public methods of two classes and ensure they were they same. You could easily modify it to ignore non matching public methods (i.e. only check for equality on public methods in class2 which exist in class1). Giving a set of arguments to pass in would be trickier - but could be done with an array of methods names / arguments to call against each class.
Anyway, this may have some bits in it which could be of use to you.
$class1 = new Class1();
$class2 = new Class2();
$class3 = new Class3();
$class4 = new Class4();
$class5 = new Class5();
echo ClassChecker::samePublicMethods($class1,$class2); //should be true
echo ClassChecker::samePublicMethods($class1,$class3); //should be false - different values
echo ClassChecker::samePublicMethods($class1,$class4); //should be false -- class3 contains extra public methods
echo ClassChecker::samePublicMethods($class1,$class5); //should be true -- class5 contains extra private methods
class ClassChecker {
public static function samePublicMethods($class1, $class2) {
$class1methods = array();
$r = new ReflectionClass($class1);
$methods = $r->getMethods();
foreach($methods as $m) {
if ($m->isPublic()) {
#$result = call_user_method($m->getName(), $class1);
$class1methods[$m->getName()] = $result;
}
}
$r = new ReflectionClass($class2);
$methods = $r->getMethods();
foreach($methods as $m) {
//only comparing public methods
if ($m->isPublic()) {
//public method doesn't match method in class1 so return false
if(!isset($class1methods[$m->getName()])) {
return false;
}
//public method of same name doesn't return same value so return false
#$result = call_user_method($m->getName(), $class2);
if ($class1methods[$m->getName()] !== $result) {
return false;
}
}
}
return true;
}
}
class Class1 {
private $b = 'bbb';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class2 {
private $a = 'aaa';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class3 {
private $c = 'ccc';
public function one() {
return 222;
}
public function two() {
return "bendy";
}
}
class Class4 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
public function three() {
return true;
}
}
class Class5 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
private function three() {
return true;
}
}
You can define PHP's __toString magic method inside your class.
For example
class cat {
private $name;
public function __contruct($catname) {
$this->name = $catname;
}
public function __toString() {
return "My name is " . $this->name . "\n";
}
}
$max = new cat('max');
$toby = new cat('toby');
print $max; // echoes 'My name is max'
print $toby; // echoes 'My name is toby'
if($max == $toby) {
echo 'Woohoo!\n';
} else {
echo 'Doh!\n';
}
Then you can use the equality operator to check if both instances are equal or not.
HTH,
Rushi
George: You may have already seen this but it may help: http://usphp.com/manual/en/language.oop5.object-comparison.php
When using the comparison operator (==), object variables are compared in a simple manner, namely: Two object instances are equal if they have the same attributes and values, and are instances of the same class.
They don't get implicitly converted to strings.
If you want todo comparison, you will end up modifying your classes. You can also write some method of your own todo comparison using getters & setters
You can try writing a class of your own to plugin and write methods that do comparison based on what you define. For example:
class Validate {
public function validateName($c1, $c2) {
if($c1->FirstName == "foo" && $c2->LastName == "foo") {
return true;
} else if (// someother condition) {
return // someval;
} else {
return false;
}
}
public function validatePhoneNumber($c1, $c2) {
// some code
}
}
This will probably be the only way where you wont have to modify the pre-existing class code