class A extends B {}
class B extends C{}
class C {}
result
PHP Fatal error: class 'B' not found ...
if the order is like this
class A extends B {}
class C {}
class B extends C{}
everything is ok.
PS: if I remove class C {}
class A extends B {}
class B extends C{}
php tells me class 'B' is not found, why?
php version 5.3.4
The PHP manual clearly mentions:
Classes must be defined before they
are used! If you want the class
Named_Cart to extend the class Cart,
you will have to define the class Cart
first. If you want to create another
class called Yellow_named_cart based
on the class Named_Cart you have to
define Named_Cart first. To make it
short: the order in which the classes
are defined is important.
clearly a parser bug
this works
class A extends B {}
class B {}
this doesn't
class C extends D {}
class D extends E {}
class E {}
consider reporting on bugs.php.net
Class order matters in PHP definitions.
Does the order of class definition matter in PHP?
This is why you don't have visibility of the class defined after the one you are defining (in this case class A cant see class B because it is defined after).
Because php is interpreted rather than compiled the order of declaration must be valid. In this example class B doesn't exist for A to extend.
Obivously class B is not defined in the moment you trying to extend it, because it occurs after extends B. Its not a bug, its the way the world works: You can only use, what exists ;)
Related
Let's take the following class structure:
class A {
... has all the functionalities of A
}
class B extends A {
... has all the functionalities of A and B
}
And these two other class structures:
class C extends B {
... has all the functionalities of A and B and C
}
class D extends A {
... all the functionalities of A, and D
}
how could I create a class easily, that would gain all the functionalities of all four of my classes, if I am not allowed to modify class A or class B? Would something like this be possible with PHP?
EDIT:
The reason I would like to do this is the following, I am open for suggestions on other ways my desired outcome can be achieved:
I have a module, which has several classes on which I plan to build on, and I do not want to edit the module directly, but I would like to add functionalities potentially to multiple classes of this module (this is where class A and class B is coming from).
So to edit class A, I would create a class D, which extends it, and add new functionalities, or rewrite already added functionalities that needs rewrite in class D.
But there are multiple classes in this module, which are simmilar in structure to class B, which I would also like to potentially modify, hence my class C. But if I modified the modules class A in my class D, I would need my new class C to extend the class D instead of the class A. (hope you can still follow me:P)
No, in PHP it is not possible to inherit from multiple parents due to "Dimond Problem". In your case, this means you can extend c for the functionality of a, b & c, but you cannot extend d too. Since you cannot modify the other classes, there is not right solution here, but I'd recommend looking into traits (https://www.php.net/manual/en/language.oop5.traits.php), as these allow you to 'inherit' from multiple traits.
I've three classes lets say Class A, Class B and Class C.
Class A is parent class and class B and C are its child.
There is function in class B, I want to access that function in class C without inheriting Class C with B. Is it possible?
public class A {
}
class B extends A{
public function hello (){
return "Hello world";
}
}
public class C extends A{
public function world (){
$this->hello();
}
}
To expand upon #u_mulder's comment: it's indeed not possible, because though B and C both derive from the same class A, they are distinct classes, in the same way that a bicycle and a car are both vehicles. When using a bicycle you cannot assume to use functions from a car, simply because they're both vehicles.
The question you'd need to answer yourself is what is the functionality of the method I need in class B? Is it specific to class B? Or is it something all entities which are class A? Or different yet, is it a separately functionality altogether but which only class B needs to consume?
The solution to the first case would be to move the function to class B. In the second case you'd move the function to class A. In the third case you'd implement the function in a separate class and inject that into class B.
I recommend you read up on SOLID principles and other principles of OOP design
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 {}
I have a file created with classpreloader and some custom code which compresses a bunch of class files into a single file for distribution:
https://raw.githubusercontent.com/jnvsor/kint/1efd147f9831ade2e03921f7111ced07428556ab/build/kint.php
According to travis this fails on everything except nightly, with an error:
Fatal error: unknown class Kint_Renderer_Text in /home/travis/build/jnvsor/kint/build/kint.php on line 315
As you can see on line 315 is a class Kint_Renderer_Plain extends Kint_Renderer_Text which is defined later in the file with class Kint_Renderer_Text extends Kint_Renderer on line 418.
One would assume that this means class order in a single file is significant.
But when I sort() the source files before building the release file, travis says that everything went perfectly smoothly, despite the fact that the new file also has similar cases:
https://raw.githubusercontent.com/jnvsor/kint/1a657f06cd693b3f9db8f9458f900ef7bb378b53/build/kint.php
For example, class Kint_Object_Closure extends Kint_Object_Instance on line 1249 and class Kint_Object_Instance extends Kint_Object on line 1352
So the question becomes: What exactly are the ordering requirements for classes in PHP?
What exactly are the ordering requirements for classes in PHP
Class must exists to be extended. So if class A extends class B and B is unknown at the moment of instantiation of A your code will fail. You can use autoloader to solve that yet it will defeat the purpose of keeping all in one file (which is wrong idea anyway).
And once again I end up answering my own question because no-one on SO can be bothered to read for more than 10 seconds...
Turns out PHP only checks for dependencies in the same file once, not recursively, and then fails to update the error message. As a result you get things like this:
<?php
class A extends B {} // boom: <b>Fatal error</b>: Class 'B' not found...
class B extends C {}
class C {}
Which work fine when the dependencies dependency is reordered:
<?php
class A extends B {}
class C {}
class B extends C {}
This behaves the same way with more than 2 extends. If it's the 3rd class that can't find its extend it will still report the second couldn't be found:
<?php
class A extends B {} // boom: <b>Fatal error</b>: Class 'B' not found...
class C extends D {}
class B extends C {}
class D {}
This is fixed in 7.2 (Still pre-release) where it will recursively check, or at least check deeper - since as I mentioned in the question this error doesn't occur in nightly.
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(){
}
}
?>