Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order of PHP class extensions in a single file

Tags:

php

subclass

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?

like image 694
MTS Avatar asked Apr 10 '19 17:04

MTS


People also ask

How to get the extension of a file in PHP?

We have used the pathinfo () PHP function to get the extension. Well, the extension could be both in lower case or upper case. So we have checked the extensions also for lower case. Well, by modifying our code we can list files by checking both multiple and single file extensions. So we did it. How to detect the file extension in PHP? »

How to get the parent and child directory path in PHP?

In the above code, we have used scandir () PHP function. The scandir function of PHP is inbuilt in PHP. If we pass the directory path as the parameter in this function, it will return an array contains all the file names with extensions. Well, it also contains the parent and child directory path.

What is a PHP Archive and how do I use it?

The most common usage for a phar archive is to distribute a complete application in a single file. For instance, the PEAR Installer that is bundled with PHP versions is distributed as a phar archive. To use a phar archive distributed in this way, the archive can be executed on the command-line or via a web server.

What is the best approach for code organization in PHP?

This is a question about code organization. I have a friend who insists the best approach regarding organization to PHP programming is to have one file/class with all functions ever written inside it. He says it's simpler like this because he can just do a find on function myFunctionName in order to find a function.


1 Answers

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 {}
like image 56
Don't Panic Avatar answered Oct 19 '22 18:10

Don't Panic