Execute method once for each child class - php

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

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

Mock or Stub a method in a php parent class

I am testing a class in phpunit, but I am not mocking it, the class is like that:
class MyClass extends ParentClass
{
public function doSomething($param)
{
//do some stuff
$someValue = $this->anotherMethod(); //this method is defined in the parent class
//do some other stuff with $someValue
return $finalValue;
}
}
in the test class I am doing like this
public function testDoSomething($param)
{
$myclass = new MyClass();
//here I need to control the value of $someValue, as it affects the final value returned
$res = $myClass->doSomething();
$this->assertEqual('sonething', res);
}
so my question is How can I control the value returned from anotherMethod method? I'd prefer to mock it so it does not call other methods in it
You could partially mock your class and instrument the methods that you do not want to test, as the following example:
public function testDoSomething()
{
/** #var \App\Models\MyClass $classUnderTest */
$classUnderTest = $this->getMockBuilder(\App\Models\MyClass::class)
->onlyMethods(['anotherMethod'])
->getMock();
$classUnderTest->expects($this->once())
->method('anotherMethod')
->willReturn('mocked-value');
$this->assertEquals("from-test mocked-value", $classUnderTest->doSomething("from-test"));
}
with the following sources:
ParentClass
class ParentClass
{
public function anotherMethod() {
return "parent-value";
}
}
and MyClass
class MyClass extends ParentClass
{
public function doSomething($param)
{
//do some stuff
$someValue = $this->anotherMethod(); //this method is defined in the parent class
//do some other stuff with $someValue
$finalValue = $param . ' '. $someValue;
return $finalValue;
}
}

Can I create a child class out of a parent class?

Lets say I have a class that is responsible for composing another class:
class ClassBuilder
{
protected $baseClass;
public function __construct()
{
$this->baseClass = new BaseClass();
}
public function set()
{
$this->baseClass->foo = 'bar';
}
// other methods to further modify BaseClass
}
class BaseClass
{
public $foo;
}
class ChildClass extends BaseClass {}
I want to create a method in the ClassBuilder that would allow me to update its baseClass property to an instance of ChildClass with the same property values as the current BaseClass object. How can I do that?
public function update()
{
// $this->baseClass = new ChildClass() with the current property values in BaseClass
}
I'm not sure that your overall approach is correct, but about the only way is to loop and set:
public function update()
{
$new = new ChildClass();
foreach($this->baseClass as $name => $value) {
$new->$name = $value;
}
$this->baseClass = $new;
//or
$this->baseClass = clone $new;
}

How to instantiate a child singleton of an abstract class?

I have an abstract class and a child that extends the abstract class. The child is supposed to be a sigleton. Here is simplified example of the abstract class:
abstract class AbstractClass{
protected static $instance = NULL;
abstract protected function meInit();
private function __construct(){
$this->meInit();
$this->init();
}
private function __clone(){}
static function getInstance(){
if (is_null(self::$instance)){
self::$instance=new self;
}
return self::$instance;
}
function init(){
'code here;
}
}
Here is simplified child class:
class ChildClass_A extends AbstractClass{
protected function meInit(){
'some code;
}
}
When I try to get an instance of the child $child = ChildClass_A::getInstance(); I get this error:
Fatal error: Cannot instantiate abstract class AbstractClass in
C:\wamp\www\Classes\AbstractClass.php on line 7
I suspect the culprit is in self::$instance=new self;. How should I redo it to achieve what I need?
You're almost there; you just can't use new self() like this because it's trying to do a new A(). Instead, use get_called_class() so that a new B is created instead.
// ONLY SUPPORTS ONE SUBCLASS
// KEEP READING BELOW FOR COMPLETE SOLUTION
abstract class A {
static protected $instance = null;
abstract protected function __construct();
static public function getInstance() {
if (is_null(self::$instance)) {
$class = get_called_class();
self::$instance = new $class();
}
return self::$instance;
}
}
class B extends A {
protected function __construct() {
echo "constructing B\n";
}
}
var_dump(B::getInstance()); // constructing B, object(B)#1 (0) {}
var_dump(B::getInstance()); // object(B)#1 (0) {}
OK, but what happens now when we try to make another subclass?
class C extends A {
protected function __construct() {
echo "constructing C\n";
}
}
var_dump(C::getInstance()); // object(B)#1 (0) {}
var_dump(C::getInstance()); // object(B)#1 (0) {}
Well that sucks! I wanted a C instance, not the B one! This is because the abstract class A is only saving one instance. We have to make it support one of each subclass.
Well that's easy!
// SOLUTION:
// WORKS FOR MULTIPLE SUBCLASSES
abstract class A {
static protected $instances = array();
abstract protected function __construct();
static public function getInstance() {
$class = get_called_class();
if (! array_key_exists($class, self::$instances)) {
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
}
Class B and C can stay the same ...
class B extends A {
protected function __construct() {
echo "constructing B\n";
}
}
class C extends A {
protected function __construct() {
echo "constructing C\n";
}
}
Now let's check out how they behave
var_dump(B::getInstance()); // constructing B, object(B)#1 (0) {}
var_dump(B::getInstance()); // object(B)#1 (0) {}
var_dump(C::getInstance()); // constructing C, object(C)#2 (0) {}
var_dump(C::getInstance()); // object(C)#2 (0) {}
Oh good! Just what we always wanted!

PHP class property reference to an object

class Hello {
public function hi() {
echo "Hello, hi!\n";
}
}
class ParentClass {
public $obj;
public function __construct() {
$this->obj = new Hello;
}
}
class Test extends ParentClass {
public function __construct() {
$this->obj->hi();
}
}
$temp = new Test;
The error message I get is "Call to a member function hi() on a non-object". $obj should be referencing to an instance of the class "Hello", but it obviously is not - what am I doing wrong?
You are defining __construct() in your Test class but not calling the parent constructor. If you want the parent constructor to execute, you need to explicitly specify so. Add a call to ParentClass constructor in in Test class constructor.
class Test extends ParentClass {
public function __construct() {
parent::__construct();
$this->obj->hi();
}
}
Also as #Tasos Bitsios pointed in his comment you also need to update your ParentClass constructor as follows:
class ParentClass {
public $obj;
public function __construct() {
$this->obj = new Hello; // Use $this->obj and not just $obj.
}
}
You need call to parent constructor:
class Test extends ParentClass {
public function __construct() {
parent::__construct();
$this->obj->hi();
}
}

Categories