=== test.php ===
<?php
var_dump(class_exists('Base'));
var_dump(class_exists('A'));
var_dump(class_exists('B'));
class A extends Base {}
class B extends Base {}
class Base
{
public static function e()
{
static $number = 0;
$number++;
var_dump('number is: '.$number);
}
}
run it, result is:
bool(true)
bool(false)
bool(false)
class A and class B extends class Base.
php found class Base.
why class A and class B not found?
Thanks.
You defined a class after var_dump function. Put var_dump below into the class. then it will return true.
Classes in PHP are only bound at compile-time when the information is already available.
So, Base can be bound as it has no dependencies, but A and B depend on Base, which isn't yet bound at the time A and B are defined. So their binding is delayed to run-time. (means the class only exists after the line they're defined on has been executed).
Try putting the Base class before the definitions of A and B and these will be compile-time bound too.
It's the order of the dumps, put them bellow the class code. It's advised that you first put the base class, Base, then afterwards declare the other classes, A and B. And only after they were made, you can actually, var_dump their existance.
So the code should look something like:
<?php
class Base
{
public static function e()
{
static $number = 0;
$number++;
var_dump('number is: '.$number);
}
}
class A extends Base {}
class B extends Base {}
var_dump(class_exists('Base'));
var_dump(class_exists('A'));
var_dump(class_exists('B'));
Just tested in case it's something else and it returns:
bool(true) bool(true) bool(true)
Related
Lets say we have class B which extends class A. When creating new instance of class B and providing value into it that value is used in constructor of class A. Please check sample below.
I'm little bit confused about such behavior. If method "display" do not pass value into class A it should not get there, or am i missing something?
class A {
protected $changeableString = 'initial value';
public function __construct($providedText)
{
$this->changeableString = $providedText;
}
public function printString(){
echo $this->changeableString;
}
}
class B extends A {
public function display(){
echo $this->changeableString;
}
}
$test = new B('provided value');
$test->display();
edit: I have changed function __construct from protected to public according comments. It is indeed gives error, so if someone will review this issue now code is correct.
First of all the topic you are talking about is called Inheritance in Object-Oriented Programming (OOP).
If class B has no construct (__construct) then PHP will call the construct of class A.
And about display function, think of it as an addition to everything in class A but will not be available in class A
So class B is like class A but has one more function called display.
Note:
As #Brian said, if you try to call new B('provided value') that will produce the following error:
Fatal error: Uncaught Error: Call to protected A::__construct() from global scope in ..
and to solve it just make the construct of class A public so class B.
In some cases defining PHP class extensions out of order causes a fatal error, and in some cases it doesn't. I'm trying to understand the underlying behavior.
For example, both
<?php
class BaseClass {}
class FirstExt extends BaseClass {}
and
<?php
class FirstExt extends BaseClass {}
class BaseClass {}
are fine, so it's not the case that simply defining subclasses out of order causes a problem.
However, errors arise when there are three classes involved, but only in one specific case, namely when the chain of classes is defined in reverse order. That is, the following code results in a fatal error:
<?php
class SecondExt extends FirstExt {}
class FirstExt extends BaseClass {}
class BaseClass {}
If you try to run this from the command line (say as main.php), you get
PHP Fatal error: Class 'FirstExt' not found in /path/to/main.php on line 2
However, any of the other five orderings of the three classes run with no errors. I was quite surprised that even
<?php
class SecondExt extends FirstExt {}
class BaseClass {}
class FirstExt extends BaseClass {}
works fine. The distinguishing factor is that all three possible pairs of classes are out of order in the case that gives an error, whereas in all the other cases at most two of the three pairs are out of order.
What is going on under the hood to produce this behavior?
The behavior isn't intuitive, but I don't think it's a bug, it's just an effect of the way PHP loads classes.
In order for a class to extend a parent class, the parent class must already be defined when the child class is defined.
Based on my observations, it appears that after the file is parsed and execution begins, the following classes are defined:
built-in classes
all user-defined classes defined before the file was parsed
user-defined base classes in that file
user-defined classes in that file that extend another class already defined, either earlier in that file or before that file was parsed
Basically any class that can be defined at compile time will be, and any other classes not defined at that point will be (attempted to be) defined at run time.
So in this example:
<?php
echo class_exists('A') ? "Yes" : "No"; // No
echo class_exists('B') ? "Yes" : "No"; // Yes
echo class_exists('C') ? "Yes" : "No"; // Yes
class A extends B {}
class C {}
class B extends C {}
class B is defined when class A tries to extend it, because it was defined when the file was parsed, because class C was defined before it in the file.
But in this example:
<?php
echo class_exists('A') ? "Yes" : "No"; // No
echo class_exists('B') ? "Yes" : "No"; // No
echo class_exists('C') ? "Yes" : "No"; // Yes
class A extends B {}
class B extends C {}
class C {}
class B is not defined when class A tries to extend it, because it was not defined when the file was parsed, because class C was not defined before it in the file.
PHP tries to find it, but it's not going to check in the same file again, it's going to try to autoload it. That's when you get "not found".
Add a fourth class and you can see it doesn't only happen when the classes are defined in reverse order:
echo class_exists('A') ? "Yes" : "No"; // No
echo class_exists('B') ? "Yes" : "No"; // No
echo class_exists('C') ? "Yes" : "No"; // Yes
echo class_exists('D') ? "Yes" : "No"; // Yes
class A extends B {}
class D {}
class B extends C {}
class C extends D {}
The PHP docs says the following about overriding trait properties:
If a trait defines a property then a class can not define a property
with the same name unless it is compatible (same visibility and
initial value), otherwise a fatal error is issued.
However, when you use a trait in an abstract class, then you can override the properties defined in the trait in a class extending that abstract class:
<?php
trait PropertyTrait
{
public $prop = 'default';
}
abstract class A
{
use PropertyTrait;
}
class B extends A
{
public $prop = 'overridden';
public function write()
{
echo $this->prop;
}
}
$b = new B();
$b->write(); // outputs "overridden"
Live demo
The code above works, but I can't find any reference about it in the documentation. Is this an intended feature?
Because for all intents and purposes B is not using PropertyTrait. That's used by A to compose the abstract class.
B has no visibility of what traits A is using. If you were to execute class_uses on B, you'd get an empty array. Docs, and example.
Since B is not using any traits, the class is free to override any inherited properties.
The fact that A is an abstract class has no bearing on this. The same behaviour would happen with any class that extended a class that was composed using traits.
This code works without problems:
<?php
namespace NamespaceA;
class A extends \NamespaceB\B {}
namespace NamespaceB;
class B {}
But why the following code cause Fatal error: Class 'NamespaceB\B' not found in ...file?
<?php
namespace NamespaceA;
class A extends \NamespaceB\B {}
namespace NamespaceB;
class B extends \NamespaceC\C {}
namespace NamespaceC;
class C {}
And this code also works without problems:
<?php
namespace NamespaceA;
class A extends \NamespaceB\B {}
namespace NamespaceC;
class C {}
namespace NamespaceB;
class B extends \NamespaceC\C {}
UPD:
Without any namespace, also Fatal error: Class 'B' not found in ...file:
<?php
class A extends B {}
class B extends C {}
class C {}
Works without problems:
<?php
class A extends B {}
class B {}
http://php.net/manual/en/keyword.extends.php
Classes must be defined before they are used. If you want the class A to extend the class B, you will have to define the class B first. The order in which the classes are defined is important.
Edit:
Found more:
Fatal error when extending included class
After some research, it became clear, that actually you can use a class before declaring it. But, declaration of the class and all parent classes must be in the same file.
So if you declare a parent class in one file and a child class in another, it won't work.
Also, you must declare parent classes first. After that you can extend them.
Edit Number 2:
Okay so I did some more research on the issue. There is probably some internal implementation detail that currently allows for the one case to work (my guess would be something regarding auto-loading) however this is something that could change at any time and should never be relied upon.
First use include_once() to add all the files in your index file and when your are extends to any class, instantiate that parent class first.Example:
index.php-->
<?php
include_once('parentClass.php');
include_once('childClass.php');
$parentObj = new parent();
$childObj = new child();
?>
child.php-->
<?php
class child extends parent{
function __construct(){
}
}
?>
I have a PHP object that consists of a set of classes. For sake of simplicity lets call it an object of class C that extends class B which in its turn extends class A. At some point in my code I want to clean up the object by calling its doCleanup() function which it inherits from interface I:
interface I { public function doCleanup(); }
class A implements I { ... }
class B extends A { ... }
class C extends B implements I { ... }
In the doCleanup function in class C I want to also execute any cleanup function in my parent classes (in this case, the doCleanup() in class A). However, for some objects I am not sure whether any of the parent classes actually implement interface I, so I am not sure whether I can simpley call parent::doCleanup().
My question therefore is if there is a way to check whether any of my ancestors implement the interface for example by using some sort of instanceof call?
You can do this nicely with get_parent_class and is_subclass_of (which works for interfaces as well as parent classes):
<?php
interface I {
public function doCleanup();
}
class A implements I {
public function doCleanup() {
echo "done cleanup\n";
}
}
class B extends A {}
class C extends B implements I {
public function doCleanup() {
if (is_subclass_of(get_parent_class($this), 'I')) {
parent::doCleanup();
}
}
}
$c = new C;
$c->doCleanup(); // outputs "done cleanup"
Since class C extends B, and B extends A, and A is required to implement doCleanup, then logically you can call parent::doCleanup() in C and it will work. If B does not implement it, it will be passed up to A, which must implement it. More accurately, B will run it, using A's implementation.
If you didn't know whether A implemented I or not, then it wouldn't necessarily be your responsibility to call it. If it were library code, for example, docs might tell you what you should do.