static property sharing/defining between children in PHP classes - php

I'm facing a misunderstanding about what the title describe and i'd like to know if there is another way to achieve what I'm looking for there.
I have an abstract class which declare a static property without value, and I instanciate subclasses from it that are defining the value of that static property. The base class also define another static property value depending of the first one, but the problem is subclasses are losing their first property value for the last one defined in other subclasses and then this second property get the bad value from the parent class.
This code demonstrate it better than I explain:
abstract class A
{
protected static $name;
protected static $path;
public function __construct()
{
static::$path = static::$name."Path";
}
public function getPath()
{
return static::$path;
}
}
class B extends A
{
protected static $name = "B";
}
class C extends A
{
protected static $name = "C";
}
$b = new B();
$c = new C();
echo $b->getPath();
I expected the echo to print "Bpath", but unfortunately it prints "CPath".
If I comment the line that instanciate the C class, then the print is good.
EDIT:
The thing is if i do this code :
abstract class A
{
protected static $name;
protected static $path;
public function __construct()
{
static::$path = static::$name."Path";
}
public function getPath()
{
return static::$path;
}
public function getName()
{
return static::$name;
}
}
class B extends A
{
protected static $name = "B";
}
class C extends A
{
protected static $name = "C";
}
$b = new B();
$c = new C();
echo $b->getName();
The name printed is "B" and good. So the fact of redefining value in subclass property doesn't have the same consequence of doing it in constructor, even if using static:: keyword.

The static property $path is only defined once on class A. Setting static::$path from anywhere will always set A::$path, so the value is shared among all classes.
You'd see a different result if you declared protected static $path; on both child classes, e.g.:
class B extends A {
protected static $name = "B";
protected static $path;
}
Now each class would have its own static $path property and they could be set independently.
It would make much more sense to use an instance property though instead of this bending over backwards with static properties:
abstract class A {
protected static $name;
protected $path;
public function __construct() {
$this->path = static::$name . 'Path';
}
public function getPath() {
return $this->path;
}
}

Related

Hierarchy of static properties in inherited class

I want to define a static property in a class, and have each child class have its own value for that property.
Here is what I tried:
class A {
static protected $v = "A";
static public function getV() {
return static::$v;
}
static public function setV($value) {
static::$v = $value;
}
}
class B extends A {}
class C extends A {}
B::setV("B");
print_r(A::getV());
print_r(B::getV());
print_r(C::getV());
print_r("\n");
C::setV("C");
print_r(A::getV());
print_r(B::getV());
print_r(C::getV());
print_r("\n");
What I expected:
ABA // C::$v hasn't been initialized, so it holds the parent's value
ABC
What I got:
BBB
CCC
So, there is only one property available, and it's the parent's one.
To get what I expected, I had to redeclare and initialize my static property in the child classes:
class B extends A {
static protected $v;
static public function init() {
self::$v = parent::$v;
}
}
B::init();
class C extends A {
static protected $v;
static public function init() {
self::$v = parent::$v;
}
}
C::init();
Result:
ABA
ABC
Is there a more elegant way to do this, without having to redeclare and initialize my property in the child classes?
You can have instead of scalar variable, an array containing all initialized values for each class. However it's not nice solution because it's pretty limited with static variables.
class A {
static protected $v = array("A" => "A");
static public function getV() {
return static::$v[get_called_class()] ?? static::$v[get_class()];
}
static public function setV($value) {
static::$v[get_called_class()] = $value;
}
}

Call static properties within another class in php

I have problem about calling a static property of a class inside another class.
Class A {
public $property;
public function __construct( $prop ) {
$this->property = $prop;
}
public function returnValue(){
return static::$this->property;
}
}
Class B extends A {
public static $property_one = 'This is first property';
public static $property_two = 'This is second property';
}
$B = new B( 'property_one' );
$B->returnValue();
I expect to return This is first property But the Output is just the name a parameter input in __construct;
When I print_r( static::$this->property ); the output is just property_one
Just change:
return static::$this->property;
with:
return static::${$this->property};
Maybe like this?
<?php
Class A {
public $property;
public function __construct( $prop ) {
$this->property = $prop;
print static::${$this->property};
}
}
Class B extends A {
public static $property_one = 'This is first property';
public static $property_two = 'This is second property';
}
$B = new B( 'property_one' );
(I mean you can access (print,...) the property this way, but the constructor will return an object anyway.)
There are several issues here:
the static property $property_one is declared in class B, the A class's constructor won't have access to that property, nor can you guarantee this property to be present.
Granted, since PHP 5.3, late static binding is supported, but that doesn't change the fact that you're never going to be sure that some static property that just happens to be called whatever $this->property happens to be assigned. What if it's assigned an object? an int, or float?
You access a static property like this: static::$propery or self::$property. Note the $! When you write static::$this->property, you're expecting this to evaluate to self::property_one. You're clearly missing the $ sign.
The very least you need is self::${$this->property}. Check the PHP manual on variable variables.
You're attempting to return a string from a constructor function, that's not possible. A constructor must, must return an instance of the class. Any return statements that don't will be ignored.
To have access to a static property of a child class in the constructor, you can't but rely on the child's constructor:
Class A
{
public $property;
}
Class B extends A
{
public static $property_one = 'This is first property';
public static $property_two = 'This is second property';
public function __construct( $prop )
{
$this->property = $prop;
print self::${$this->property};
}
}
$B = new B( 'property_one' );
An alternative would be:
Class A
{
public $property;
public function __constructor($prop)
{
$this->property = $prop;
}
public function getProp()
{
return static::${$this->property};
}
}
Class B extends A
{
public static $property_one = 'This is first property';
public static $property_two = 'This is second property';
}
$B = new B( 'property_one' );
$B->getProp();

"Cannot access protected property" Error

I have two classes involving composition not inheritance., class A and class B. One of class A's properties is an array of class B objects. Class A has a public method A::getName(). Class B also has a public method with the same name. The method for class A is as follows:
public function getName()
{
return $this->_name;
}
My problem is when I'm in Class B and I try to access this public method for class A, I get the "cannot access protected property" error. $_name is a private property in each class. Class A's would be the name, for example, of a form, and for B, the name of the field.
This is the code that generates the error (constructor for class B):
public function __construct($name)
{
$this->foo = A::getName() .'-'. $name;
}
Why is it not allowing me to access class A's public method getName()? Any way to fix or get around this?
FIX:
I realized I was invoking class A's method statically, though I need to deal with each object individually, as each object has a unique name. To solve my issue I gave a public set function for class B for the unique name, and called that in class A:
class A
{
...
$this->list[$B_name] = new B($B_name);
$this->list[$B_name]->setID($this->_name, $B_name);
}
class B
{
...
public function setID($name, $subName)
{
$this->foo = $name .'-'. $subName;
}
This is because of you are trying to call it statically and in that method you are accessing instance variable.
it could work like this:
class A {
private static $_name = "A";
public static function getName() {
return self::$_name;
}
}
class B {
public function __construct($name) {
$this->foo = A::getName() .'-'. $name;
}
}
or this way (this is imho your situation)
class A {
private $_name;
public function __construct($name) {
$this->_name = $name;
}
public function getName() {
return $this->_name;
}
}
class B {
public function __construct($name, A $parent) {
$this->foo = $parent->getName() .'-'. $name;
}
}

PHP Inheritance and Static Methods and Properties

Is it safe to say that static properties and methods can not be inherited in PHP? a few examples will be helpful.
No. That's not true. Static Methods and properties will get inherited the same as non-static methods and properties and obey the same visibility rules:
class A {
static private $a = 1;
static protected $b = 2;
static public $c = 3;
public static function getA()
{
return self::$a;
}
}
class B extends A {
public static function getB()
{
return self::$b;
}
}
echo B::getA(); // 1 - called inherited method getA from class A
echo B::getB(); // 2 - accessed inherited property $b from class A
echo A::$c++; // 3 - incremented public property C in class A
echo B::$c++; // 4 - because it was incremented previously in A
echo A::$c; // 5 - because it was incremented previously in B
Those last two are the notable difference. Incrementing an inherited static property in the base class will also increment it in all the child classes and vice versa.
No (Apparently I couldn't see the not in the question). public and protected static methods and properties are inherited as you would expect them to be:
<?php
class StackExchange {
public static $URL;
protected static $code;
private static $revenue;
public static function exchange() {}
protected static function stack() {}
private static function overflow() {}
}
class StackOverflow extends StackExchange {
public static function debug() {
//Inherited static methods...
self::exchange(); //Also works
self::stack(); //Works
self::overflow(); //But this won't
//Inherited static properties
echo self::$URL; //Works
echo self::$code; //Works
echo self::$revenue; //Fails
}
}
StackOverflow::debug();
?>
Static properties and methods obey the visibility and inheritance rules as illustrated in this snippet.

Dynamically populating a static variable in PHP

I have two static values: "type" and "typeID". Type is human readable and constant, and typeID needs to be looked up from the database, based on the value of type. I need the lookup to happen once, when the class definition is first loaded
To illustrate, here is some code that doesn't work because you can't call functions in the declaration space.
MyClass extends BaseClass {
protected static $type = "communities";
protected static $typeID = MyClass::lookupTypeID(self::$type);
}
Is there a magic method that is called exactly once when the class definition is loaded? If there is something obvious I'm missing it.
shamelessly pulled from the php manual's static keyword comments:
Because php does not have a static constructor and you may want to initialize static class vars, there is one easy way, just call your own function directly after the class definition.
for example.
<?php
function Demonstration()
{
return 'This is the result of demonstration()';
}
class MyStaticClass
{
//public static $MyStaticVar = Demonstration(); //!!! FAILS: syntax error
public static $MyStaticVar = null;
public static function MyStaticInit()
{
//this is the static constructor
//because in a function, everything is allowed, including initializing using other functions
self::$MyStaticVar = Demonstration();
}
} MyStaticClass::MyStaticInit(); //Call the static constructor
echo MyStaticClass::$MyStaticVar;
//This is the result of demonstration()
?>
Simple and no magic needed, don't forget you can always define a variable as null and test that it is null (doing the db call only then). Then it's just a matter if you want that to happen when the class is constructed or included (include_once etc...)
MyClass extends BaseClass {
protected static $type = "communities";
protected static $typeID = null;
public function __construct(){
if(is_null(self::$typeID)){
self::lookupTypeID(self::$type);
}
}
public static lookupTypeID($type){
self::$typeID = //result of database query
}
}
or
MyClass::lookupTypeID(); //call static function when class file is included (global space)
MyClass extends BaseClass {
protected static $type = "communities";
protected static $typeID = null;
public function __construct(){
}
public static lookupTypeID($type=null){
if(is_null($type)){
$type = self::$type;
}
self::$typeID = //result of database query (SELECT somefield FROM sometable WHERE type=$type) etc..
}
}
a static constructor is more like a factory method
if(!function_exists(build_myclass)){
function build_myclass(){
return MyClass::build();
}
}
MyClass extends BaseClass {
protected static $type = "communities";
protected static $typeID = null;
public function __construct(){
}
public static function build(){
return new self(); //goes to __construct();
}
}
$class = new MyClass(); //or
$class = MyClass::build(); //or
$class = build_myclass();
Such a thing would normally be called a "static constructor", but PHP lacks such things. You might want to consider one of the workarounds suggested in the PHP manual comments, e.g. http://www.php.net/manual/en/language.oop5.static.php#95217

Categories