Hierarchy of static properties in inherited class - php

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;
}
}

Related

static property sharing/defining between children in PHP classes

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;
}
}

Execute method once for each child class

I am trying to execute initialize() method for each class that extends from a baseClass, by using late static bindings:
class BaseClass
{
protected static $initialized = false;
public static function find()
{
static::initialize();
//TODO search entries and return as instances...
}
protected static function initialize()
{
if (static::$initialized)
return;
echo 'Initializing ', static::class, '<br/>';
//do important stuff
static::$initialized = true;
}
}
class Child1 extends BaseClass {}
class Child2 extends BaseClass {}
$initialized property is being shared across the extending classes.
Child1::find(); //Outputs 'Initializing Child1', as expected
Child2::find(); //Doesn't execute 'initialize()' because
//$initialized property was set to true by Child1
//Expected: 'Initializing Child2'
Is there a simple way to achieve it?
You can change $initialized to an array (as suggested in comment) using class names as keys:
class BaseClass
{
protected static $initialized = [];
public static function find()
{
return static::initialize();
}
protected static function initialize()
{
$class_name = static::class;
if (!empty(static::$initialized[$class_name])) {
return static::$initialized[$class_name];
}
echo 'Initializing ', $class_name, '<br/>';
//do important stuff
static::$initialized[$class_name] = new static();
// do another important stuff and return instance
return static::$initialized[$class_name];
}
}
class Child1 extends BaseClass {}
class Child2 extends BaseClass {}

Access private properties in a class hierarchy from a common base function

I want to define a method once in a base class and call it in successive constructors in a class hierarchy. Each time is it called I want it to operate on the properties of the class from which it is called.
For example, A is the base class and the method is defined here. B inherits from A, and C inherits from B.
When I instantiate the concrete class C the constructor will call the base class method, and I want it to operate on the properties of C. (This will be a private array which I will populate when I initialise it).
The constructor of C then calls parent::__construct. When the B constructor calls the base class method, the method should operate on the properties of B. Before the B constructor is complete it will call parent::_construct, and the A constructor will operate on the properties of A.
I was looking into LSB, but it won't work correctly because parent::__construct is a forwarding call. I tried using the result of get_parent_class() in place of parent::, but my calls to static::propertyName error out because propertyName is not a constant.
How can I do this?
EDIT: Here's a code example. The code below outputs "P Q P Q P Q". I want it to output "P Q R S T U".
class A {
private $property = array('P','Q');
function __construct() {
$this->myMethod();
}
public function myMethod() {
foreach ($this->property as $value) {
echo $value . " ";
}
}
}
class B extends A {
private $property = array('R','S');
function __construct()
{
parent::__construct();
$this->myMethod();
}
}
class C extends B {
private $property = array('T','U');
function __construct()
{
parent::__construct();
$this->myMethod();
}
}
$c = new C();
As far as I know it is not possible. In case of private the variable is not available to method and in case of public/protected its overwritten.
You can get your desired result by passing
$this->property
in each myMethod call like this -
$this->myMethod($this->property)
and changing your myMethod definition accordingly.
If I got you right, this is your example:
class A {
public function __construct() {
}
public function someMethod($arg) {
$someProperty = $arg;
}
}
class B extends A {
public function __construct() {
parent::__construct();
}
}
class C extends B {
private $someProperty;
public function __construct() {
parent::__construct();
}
}
Everything works as you would expect. There is only one problem; class A has no property someProperty. If you want to use it in class A, you have to define it there. If you want to use it in subclasses, you have to make it protected. Your class A has therefore to look like this:
class A {
protected $someProperty;
public function __construct() {
}
public function someMethod($arg) {
$this->someProperty = $arg;
}
}
Now you can use it in class B and C, and class A can use the property with someMethod:
$instance = new C();
$instance->someMethod("test");
Because your example has $property defined as private and you never override myMethod, when an object of type C or B is instantiated, it runs class A's constructor, which calls myMethod() from class A where the value of $property is array('P', 'Q');.
If you want it to print 'P Q R S T U' you'll have to override myMethod() and call it from each individual class' __construct() method, OR declare $property to be protected so that the child classes can overwrite its value.
Something like this:
class A {
private $property = array('P','Q');
function __construct() {
$this->myMethod();
}
public function myMethod() {
foreach ($this->property as $value) {
echo $value . " ";
}
}
}
class B extends A {
private $property = array('R','S');
function __construct()
{
parent::__construct();
$this->myMethod();
}
public function myMethod() {
foreach ($this->property as $value) {
echo $value . " ";
}
}
}
class C extends B {
private $property = array('T','U');
function __construct()
{
parent::__construct();
$this->myMethod();
}
public function myMethod() {
foreach ($this->property as $value) {
echo $value . " ";
}
}
}
$c = new C();
OR THIS:
class A {
protected $property = array('P','Q');
function __construct() {
$this->myMethod();
}
public function myMethod() {
foreach ($this->property as $value) {
echo $value . " ";
}
}
}
class B extends A {
protected $property = array('R','S');
function __construct()
{
parent::__construct();
$this->myMethod();
}
}
class C extends B {
protected $property = array('T','U');
function __construct()
{
parent::__construct();
$this->myMethod();
}
}
$c = new C();
proptery cannot be overloaded in child classes. Once you have a C, it does not have the private properties of A anymore. It has no way to access them (outside of reflection). The only way you would be able to do this would be to pass the properties to someMethod in a chain, but this requires access to the desired property.
Using protected would not work either because then the children would just override the parent property.
The solution to this is not have have C be an a (is-a) but have C have the properties of an A (has-a .. composition). This would require some rework of your hierarchy, though, and your calls would have to be more explicit. Something like
class C {
private $b;
private $properties = array('T', 'U');
public function __construct(B $b) {
$this->b = $b;
}
public function someMethod() {
$this->b->someMethod($properties);
}
}
class B {
private $properties = array('R', 'S');
private $a;
public function __construct(A $a) {
$this->a = $a;
}
public function someMethod($properties) {
$this->a->someMethod(array_merge($this->properties, $properties));
}
}
class A {
private $properties = array('P', 'Q');
public function someMethod($properties) {
//your implementation plus an array_merge
}
}
This obviously increases verbosity in your definitions, which is bad. You may want to reconsider why you have to do some things the way you are doing them. You may be able to get around this with traits (assuming you have PHP 5.4)

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.

PHP Accessing Parent Class Variable

class A {
private $aa;
protected $bb = 'parent bb';
function __construct($arg) {
//do something..
}
private function parentmethod($arg2) {
//do something..
}
}
class B extends A {
function __construct($arg) {
parent::__construct($arg);
}
function childfunction() {
echo parent::$bb; //Fatal error: Undefined class constant 'bb'
}
}
$test = new B($some);
$test->childfunction();
Question:
How do I display parent variable in child?
expected result will echo 'parent bb'
echo $this->bb;
The variable is inherited and is not private, so it is a part of the current object.
Here is additional information in response to your request for more information about using parent:::
Use parent:: when you want add extra functionality to a method from the parent class. For example, imagine an Airplane class:
class Airplane {
private $pilot;
public function __construct( $pilot ) {
$this->pilot = $pilot;
}
}
Now suppose we want to create a new type of Airplane that also has a navigator. You can extend the __construct() method to add the new functionality, but still make use of the functionality offered by the parent:
class Bomber extends Airplane {
private $navigator;
public function __construct( $pilot, $navigator ) {
$this->navigator = $navigator;
parent::__construct( $pilot ); // Assigns $pilot to $this->pilot
}
}
In this way, you can follow the DRY principle of development but still provide all of the functionality you desire.
Just echo it since it's inherited
echo $this->bb;
With parent::$bb; you try to retrieve the static constant defined with the value of $bb.
Instead, do:
echo $this->bb;
Note: you don't need to call parent::_construct if B is the only class that calls it. Simply don't declare __construct in B class.
class A {
private $aa;
protected $bb = 'parent bb';
function __construct($arg) {
//do something..
}
private function parentmethod($arg2) {
//do something..
}
}
class B extends A {
function __construct($arg) {
parent::__construct($arg);
}
function childfunction() {
echo parent::$this->bb; //works by M
}
}
$test = new B($some);
$test->childfunction();`
$bb has now become the member of class B after extending class A.
So you access $bb like it's an attribute of class B.
class A {
private $aa;
protected $bb = 'parent bb';
function __construct($arg) {
//do something..
}
private function parentmethod($arg2) {
//do something..
}
}
class B extends A {
function __construct($arg) {
parent::__construct($arg);
}
function childfunction() {
echo $this->bb;
}
}
$test = new B($some);
$test->childfunction();
all the properties and methods of the parent class is inherited in the child class so theoretically you can access them in the child class but beware using the protected keyword in your class because it throws a fatal error when used in the child class.
as mentioned in php.net
The visibility of a property or method
can be defined by prefixing the
declaration with the keywords public,
protected or private. Class members
declared public can be accessed
everywhere. Members declared protected
can be accessed only within the class
itself and by inherited and parent
classes. Members declared as private
may only be accessed by the class that
defines the member.
PHP Accessing Parent Class Protected Variable & Methods
class A {
protected $bb = 'parent bb';
protected function sayHello(){
echo 'Say Hello';
}
}
class B extends A {
public function childfunction() {
echo $this->bb.'<br>';
echo $this->sayHello();
}
}
$test = new B();
$test->childfunction();
Through parent class contructor you can pass data to parent class from child class. Have a look below example for better understanding
<?php
class Student
{
public $name;
function __construct($name){
$this->name = $name;
}
}
class Test extends Student
{
public $age;
function __construct($name,$age){
$this->age = $age;
parent::__construct($name);
}
}
$obj = new Test("sajib khan" ,21);
echo $obj->name;
echo $obj->age;
?>
class A {
private $points = 100;
public function getPoints() {
return $this->points;
}
}
class B extends A {
protected $points = 70;
public function getPoints() {
return parent::getPoints();
}
}
$element = new B();
echo $element->getPoints();
change the visibility private or protected for test

Categories