PHP - Static Local Variables - php

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().

Related

PHP pimple cross dependency

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

PHP call function within private function in class method

I try call Test3 function, but returned this error: "Fatal error: Call to undefined function".
Here is an example:
class Test {
public Test1(){
return $this->Test2();
}
private Test2(){
$a = 0;
return Test3($a);
function Test3($b){
$b++;
return $b;
}
}
}
How to call Test3 function ?
From PHP DOC
All functions and classes in PHP have the global scope - they can be called outside a function even if they were defined inside and vice versa.
Use Closures 
$test = new Test();
echo $test->Test1();
Modified Class
class Test {
public function Test1() {
return $this->Test2();
}
private function Test2() {
$a = 0;
$Test3 = function ($b) {
$b ++;
return $b;
};
return $Test3($a);
}
}
Not sure if you wanted a closure or if your 'inner' function was a typo.
If it was meant to be a separate method then the below is the correct syntax:
class Test
{
public function Test1()
{
return $this->Test2();
}
private function Test2()
{
$a = 0;
return $this->Test3($a)
}
public function Test3($b)
{
$b++
return $b;
}
}

Is it possible to have python rich comparison in php?

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.

shared variable across multiple class instances that I can change outside the class

the code explains it better:
class Class{
$var = 0;
function getvar()
echo $this->var;
}
}
$inst1 = new Class();
// I want to change $var here to 5
$inst2 = new Class();
echo $inst2->getvar(); // should be 5
Is it possible
Static. http://php.net/manual/en/language.oop5.static.php
class MyClass {
public static $var = 0;
function setVar($value) {
self::$var = $value;
}
function getVar() {
return self::$var;
}
}
echo MyClass::$var;
MyClass::setVar(1);
echo MyClass::getVar(); //Outputs 1
You should be able to do this using a static member variable.
class foo {
private static $var;
public static setVar($value) {
self::$var = $value;
}
public static getVar() {
return self::$var;
}
}
$a = new foo;
$a::setVar('bar');
$b = new foo;
echo $b::getVar();
// should echo 'bar';
You should declare $var to be static:
A data member that is commonly
available to all objects of a class is
called a static member. Unlike regular
data members, static members share the
memory space between all objects of
the same class.
You can use static variables:
class AAA{
public static $var = 0;
function getvar() {
return AAA::$var;
}
}
AAA::$var = "test";
$a1 = new AAA();
var_dump($a1->getvar());
var_dump(AAA::$var);

PHP: How do I check if all public methods of two classes return the same values?

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

Categories