Is there any way to detect the target class in static methods? - php

Below is an example class hierarchy and code. What I'm looking for is a way to determine if 'ChildClass1' or 'ChildClass2' had the static method whoAmI() called on it without re-implementing it in each child class.
<?php
abstract class ParentClass {
public static function whoAmI () {
// NOT correct, always gives 'ParentClass'
$class = __CLASS__;
// NOT correct, always gives 'ParentClass'.
// Also very round-about and likely slow.
$trace = debug_backtrace();
$class = $trace[0]['class'];
return $class;
}
}
class ChildClass1 extends ParentClass {
}
class ChildClass2 extends ParentClass {
}
// Shows 'ParentClass'
// Want to show 'ChildClass1'
print ChildClass1::whoAmI();
print "\n";
// Shows 'ParentClass'
// Want to show 'ChildClass2'
print ChildClass2::whoAmI();
print "\n";

I believe what you're referring to is a known php bug. Php 5.3 is aiming to address this issue with a new Late Static Binding feature.
http://www.colder.ch/news/08-24-2007/28/late-static-bindings-expl.html

Now that PHP 5.3 is widely available in the wild, I wanted to put together a summary answer to this question to reflect newly available techniques.
As mentioned in the other answers, PHP 5.3 has introduced Late Static Binding via a new static keyword. As well, a new get_called_class() function is also available that can only be used within a class method (instance or static).
For the purpose of determining the class as was asked in this question, the get_called_class() function is appropriate:
<?php
abstract class ParentClass {
public static function whoAmI () {
return get_called_class();
}
}
class ChildClass1 extends ParentClass {
}
class ChildClass2 extends ParentClass {
}
// Shows 'ChildClass1'
print ChildClass1::whoAmI();
print "\n";
// Shows 'ChildClass2'
print ChildClass2::whoAmI();
print "\n";
The user contributed notes for get_called_class() include a few sample implementations that should work in PHP 5.2 as well by making use of debug_backtrace().

Class identification is often a symptom of not well understood Polymorphism.
The clients of ChildClass1 and ChildClass2 shouldn't need to distinguish between them.
There's no place where any class should ask about someObject.whoAmI().
Whenever you have the urge to write if someObject.whoAmI() == 'ChildClass1' { do X(someObject) } you should really add an X() method to the ParentClass with various implementations in the various ChildClasses.
This kind of "run-time type identification" can almost always be replaced with properly polymorphic class designs.

As of PHP 5.3 it'll be possible with the use of the static keyword, but for now it isn't possible.

No. Wait for PHP 5.3.

Related

Invoke parent constructor from child as though it were in the child's scope

Background
In a project with a PHP 5.6 runtime, I need to work around some third-party code. This code will not be changed by the vendor, nor will it be removed from the codebase.
Specifically, the third-party code (let's call its namespace Theirs) contains a class (\Theirs\BaseClass) whose constructor instantiates another class (\Theirs\Detector).
BaseClassTheirs.php:
<?php namespace Theirs;
class Detector {
public function __construct() {
print "Theirs\n";
}
}
class BaseClass {
public function __construct() {
$detector = new Detector();
}
}
I do not want BaseClass to instantiate \Theirs\Detector. Instead, I want BaseClass to instantiate a different Detector class, from a different namespace (Mine) that is outside of the third-party's control.
In all other respects, though, I want BaseClass to behave as it does in the third-party code, including if the vendor later adds additional functionality to \Theirs\BaseClass. (I'll call this property "non-fragility" and the lack of it "fragility".) As such, it seems sensible for me to create my own \Mine\BaseClass as a child of \Theirs\BaseClass, inheriting everything from it.
If I take the fragile, non-DRY approach of copy-pasting \Theirs\BaseClass's constructor into \Mine\BaseClass, then \Mine\Detector is instantiated, as I desired:
BaseClassMine.php:
<?php namespace Mine;
include "BaseClassTheirs.php";
class Detector {
public function __construct() {
print "Mine\n";
}
}
class BaseClass extends \Theirs\BaseClass {
public function __construct() {
$detector = new Detector();
}
}
\\ Prints "Mine"
$obj = new BaseClass();
However, if I change this into a DRY, non-fragile approach by removing the duplicated code so that \Mine\BaseClass invokes exactly the same constructor, but as inherited from its parent rather than being copy-pasted, then \Theirs\Detector gets invoked, which is not what I want:
BaseClassMine.php:
<?php namespace Mine;
include "BaseClassTheirs.php";
class Detector {
public function __construct() {
print "Mine\n";
}
}
use \Mine\Detector;
class BaseClass extends \Theirs\BaseClass {
}
\\ Prints "Theirs"
$obj = new BaseClass();
This happens regardless of whether the file contains a use \Mine\Detector; line, as above.
Question
How can I get the best of both approaches?
I.e. how can I invoke \Theirs\Baseclass's constructor from \Mine\Baseclass's constructor in order to have it invoke \Mine\Detector, as though \Theirs\Baseclass's constructor had simply been copy-pasted into \Mine\Baseclass's context, but without actually copy-pasting it and introducing the corresponding fragility?
For instance, is there a good way to use reflection or some other introspective technique to dynamically read the parent's constructor and to "paste" it at runtime into the child class?
Related but not identical questions
late static binding | without modifying parent class with static keyword
Is there any way to set a property before calling a constructor?

unable to debug the output

Today i was reading design pattern and i tried to make a sample program which consist of a interface, two class which implement that interface and a main index class.let have a look at the code given below.
firstly the interface Iproduct
<?php
interface Iproduct
{
//Define the abstract method
public function apple();
public function mango();
}
the two class which implement the interface
<?php
// Including the interface
include_once 'Iproduct.php';
class Apple implements Iproduct
{
public function apple()
{
echo ("We sell apples!");
}
public function mango()
{
echo ("We do not sell Mango!");
}
}
<?php
// Include the interface Iprodduct
include_once 'Iproduct.php';
class Mango implements Iproduct
{
public function apple()
{
echo ("We do not sell Apple");
}
public function mango()
{
echo ("We sell mango!");
}
}
now the main class
<?php
include_once ('apple.php');
include_once ('Mango.php');
class UserProduct
{
public function __construct()
{
$apple_class_obj=new Apple();
$mango_class_obj=new Mango();
//echo("<br/> the apple class object: ".$apple_class_obj);
}
}
//creating the object of the UserProduct
echo ("creating the object!<br/>");
$userproduct_obj=new UserProduct();
?>
the output which i get when i execute the code is:
creating the object!
we sell apples!we sell mango
now the problem is that i am unable to get that how is the second output ie, we sell apple! and we sell mango! is being displayed.please let me know the reason
In the past (PHP before version 5), the method with the same name as the class is called when the object is created (PHP old-style constructor methods).
Because PHP is backwards compatible to that behavior, you see the output now.
For backwards compatibility, if PHP 5 cannot find a __construct() function for a given class, and the class did not inherit one from a parent class, it will search for the old-style constructor function, by the name of the class. Effectively, it means that the only case that would have compatibility issues is if the class had a method named __construct() which was used for different semantics. [Bold by me]
From: Constructors and Destructors in the PHP Manual
So what you experience is less a problem with the interface or the objects per-se, it's just some side-effect you're likely not aware of (this is really old).
To work around that, just implement a __construct() method in both classes so that the old-style constructor is not called any longer:
class Mango implements Iproduct
{
public function __construct() {}
...
An empty method per class is enough here to stop that.
You might be as well interested in:
What is the function __construct() used for? (Jan 2009)
Why are functions and methods in PHP case-insensitive? (May 2010)
Why is my constructor still called even if the class and constructor case are different? (Oct 2011)
in php 4x a method with the same name as the class was considered as the constructor. With php 5x the constructor is explicitly named __construct.
Your experiencing the outcome due to backward compatibility of PHP.

PHP empty constructor

Just wondering is it best to define an empty constructor or leave the constructor definition out completely in PHP? I have a habit of defining constructors with just return true;, even if I don't need the constructor to do anything - just for completion reasons.
If you don't need a constructor it's best to leave it out, no need to write more code. When you DO write it, leave it empty... returning true doesn't have a purpose.
There is a difference between the two: If you write an empty __construct() function, you overwrite any inherited __construct() from a parent class.
So if you don't need it and you do not want to overwrite the parent constructor explicitly, don't write it at all.
EDIT:
previous answer is no longer valid, since PHP now behaves like other oop programming languages.
constructors aren't part of interfaces. therefore you are now allowed to override them how you prefer without any issues whatsoever
the only exception to this is:
interface iTest
{
function __construct(A $a, B $b, Array $c);
}
class Test implements iTest
{
function __construct(A $a, B $b, Array $c){}
// in this case the constructor must be compatible with the one specified in the interface
// this is something that php allows but that should never be used
// in fact as i stated earlier, constructors must not be part of interfaces
}
PREVIOUS OLD NOT-VALID-ANYMORE ANSWER:
there is an important difference between an empty constructor and no constructor at all
class A{}
class B extends A{
function __construct(ArrayObject $a, DOMDocument $b){}
}
VS
class A{
function __construct(){}
}
class B extends A{
function __construct(ArrayObject $a, DOMDocument $b){}
}
// error B::__construct should be compatible with A constructor
You should only define an empty constructor if your object should never be instantiated. If that is the case, make the __construct() private.
constructor always return instance of class in which its defined . Hence you never use "return" inside constructor . Lastly its better not to define it if you are not gona use it .
One reason you might want to define an empty constructor is when you want to avoid calling a function that has the same class name.
class FooBar {
function foobar() {
echo "Hello world";
}
}
new FooBar(); // outputs "Hello world" in PHP < 8
This is due PHP 4 backwards compatibility, where constructors had the same name of the class.
Anyway it got deprecated in PHP 7.4.26.
class FooBar {
function __construct() {
}
function foobar() {
echo "Hello world";
}
}
new FooBar(); // no output

PHP static variables in an abstract parent class

Quick code with the question included:
abstract class ClassParent {
public static $var1 = "ClassParent";
}
class ClassChild1 extends ClassParent{
public static function setvar1(){
ClassChild1::$var1 = "ClassChild1";
}
}
class ClassChild2 extends ClassParent{
public static function setvar1(){
ClassChild2::$var1 = "ClassChild2";
}
}
ClassChild1::setvar1();
echo ClassChild2::$var1;
// Returns "ClassChild1". Shouldn't this still be "ClassParent"?
I am assuming that the above is expected behaviour and not a PHP bug. In that case, how could I declare a static variable in the parent class which will be handled separately for the child classes. In other words, I want to have separate static values PER CHILD CLASS. Must I declare the static variable specifically in the child classes or is there perhaps another way?
Thanks!
EDIT: On further investigation, I think what you're asking is not directly possible, even with late static binding. Actually, I am a little surprised.
The answer to this question provides some workarounds.
Original answer:
In a parent class, if you refer to a static variable in the form:
self::$var
It will use that same variable in all inherited classes (so all child classes will be still accessing the variable in the parent class).
This is because the binding for the self keyword is done at compile-time, not run-time.
As of PHP 5.3, PHP supports late static binding, using the static keyword. So, in your classes, refer to the variable with:
static::$var
And 'static' will be resolved to the child class at run-time, so there will be a separate static variable for each child class.
Thanks for this question! I had some problems I couldn't track and this has helped me solve them. :)
You might be interested to know that there is a bug report for this behavior which includes the workaround. In your case this would be:
class ClassChild1 extends ClassParent{
public static function setvar1(){
$tmp = 'x';
static::$var1 =& $tmp; // break reference
// and now this works as expected: (changes only ClassChild1::$var1)
static::$var1 = "ClassChild1";
}
}
// do the same in ClassChild2...
Ugly as hell, I agree - but PHP works as expected this way, plus it has no side effects.
This is indeed a very doubtful (and poorly documented) "feature" in my eyes - let's hope they change it someday.
Ugly solution, but it works.
I've moved static $var1 to a trait that is required to be used in child classes.
It is essentially the same as declaring $var1 in each child class.
However, using this method there is no chance you forget to declare $var1.
trait Var1Trait
{
public static $var1 = "ClassParent";
protected function requiresVar1Trait()
{
}
}
abstract class ClassParent
{
abstract protected function requiresVar1Trait(); // make sure that Var1Trait is used in child classes
}
class ClassChild1 extends ClassParent
{
use Var1Trait;
public static function setvar1()
{
ClassChild1::$var1 = "ClassChild1";
}
}
class ClassChild2 extends ClassParent
{
use Var1Trait;
public static function setvar1()
{
ClassChild2::$var1 = "ClassChild2";
}
}
ClassChild1::setvar1();
echo ClassChild2::$var1;
// Returns "ClassParent" as requested

PHP interface problem - class not found

Hi I have a very simple class that implements an interface. Both the class and the interface are in the same file.
When I implement the interface I get a fatal error "Class not found", but when I remove the implements and then try to use the class I can use it fine???
Can anyone offer any advice on this?
Sorry here is some code that I am using to test at the moment:
$tester = new TypeOneTester();
$tester->test("Hello");
interface iTestInterface
{
public function test($data);
}
class TypeOneTester implements iTestInterface
{
public function test($data)
{
return $data;
}
}
Create an instance of your class after the class and the interface are defined, not before.
The order of definition in this case should be:
Interface
Class
Instance of Class (objects)
This is a (very poorly) documented limitation:
http://php.net/manual/pl/migration5.incompatible.php
In some cases classes must be declared before use. It only happens if some of the new features of PHP 5 (such as interfaces) are used. Otherwise the behaviour is the old.
I've filed a bug report nonetheless. IMO it should be fixed as it's inconsistent behaviour and the error message is not helpful for anyone who assumes as I did that PHP simply didn't care where you declare functions/classes. Come on, it's been there for over 10 years now...
https://bugs.php.net/bug.php?id=69665
smells like a bug in php. Make sure it's reproducible with the latest version and post to bugs.php.net.
Reproduce code
interface I {}
$a = new A;
$b = new B;
class A {
function __construct() { echo 'A'; }
}
class B implements I {
function __construct() { echo 'B'; }
}
Expected
AB
Actual:
A
Fatal error: Class 'B' not found...
That is because, php loading interface, and instantiate class class class object where there is a certain order and must be in a Php file, if the file is not in accordance with an order of 1. Require_one interface, 2. Require_one class

Categories