Question

I tried to find a similar question or scenario, but I couldn't. I have the following structure:

  • classes/
    • dev/
      • class1.php
      • class2.php
    • class1.php
    • class2.php
    • class3.php

Then, the code calls:

$class1 = new class1();
$class2 = new class2();
$class3 = new class3();

The 'dev' folder contains classes that should overwrite the root classes. There's an extended class1 inside the 'dev' folder, class2 as well, but not class3. Currently, I'm trying to load this extended class inside the autoload function, which works, but not the way I was expecting :D

Instead of loading class1 from the root folder, then class1 from the 'dev' folder, it loads the root classes only after leaving the autoload function. Simplifying the code, I have something like:

function $autoloader($class) {
    if (!file_exists($class)) {
        echo 'failed - fallback or do something else';
        return false;
    }
    require_once($class);

    // Instantiates extended class from 'dev'
    if (file_exists($classFromDevFolder)) {
        require_once($classFromDevFolder);
        $newClass = new $classFromDevFolder();
        unset($newClass);
    }            
}

It works, loads the class from the 'dev' folder, and extends class1 finely. But, as said, it instantiates the dev class BEFORE the root class. I assume because it's loading the dev class inside the autoload function, even after calling the root class.

Would you guys have any ideas on how to achieve it? I also thought to create another function to load 'dev' classes, but I have no idea where to put it in order to make this function work on any class.


I added this very simplified code sample to illustrate and clarify a bit better what I'd like to achieve.

Root folder:

class Class1() {
    ...
    public function __construct() {
        $this->x = 1;
    }
    ...
}

Dev folder:

class Class1_Dev extends Class1() {
    ...
    public function __construct() {
        $this->x = 2;
    }
    ...
}

Autoloader:

function autoloader($class) {
    if (!file_exists($class)) {
        echo 'failed - fallback or something else';
        return false;
    }
    require_once($class);
    ...
    $devClass = 'dev/' . $class;
    if (file_exists($devClass)) {
        require_once($devClass);
        $newClassName = $class . '_Dev';

        // Here is the issue: it instantiates Class1_Dev before Class1,
        // and ultimately, Class1 keeps 'x' as 1, not 2
        $newClass = new $newClassName();
    }
}

Code:

$class1 = new Class1();
var_dump($class1->x);

It's expected $class->x to be 2, and not 1.

Was it helpful?

Solution

If I understand your question correctly, you want to first load classes from the dev folder or if they do not exist there from the root folder. You could achieve this easily through spl_autolaod_register:

// Please be aware that anonymous function are first available for spl_autoload_register as of PHP 5.3.0
spl_autoload_register(function ($class) {

    include 'classes/dev/' . $class . '.php';

    if (! class_exists($class)) {
        include 'classes/' . $class . '.php';
    }

}); 

You should then be able to to call your classes

$class1 = new class1();
$class2 = new class2();
$class3 = new class3();

Which would ideally load class1 and class 2 from the dev folder and class3 from the root folder.


Update:

As already posted in the comment, if you load two classes with the same name, the first loaded class will be the persistent one and if you trie to load another class with the same class name, this will fail.

I am not quite sure why you are trying to do this?

But looking at your code, I could think of a few ways of achieving the above when using the autoload method I posted earlier.

For one example using a baseclass that implements the shared logic and then extending it through classes in different folders:

Root folder

class BaseClass1 {
   protected $devMode;
   protected $x;

   public function __construct() {
        $this->devMode = false;
        $this->x = 0;
   }

   public function isDevClass() {
        return $this->devMode;
   }

   public function getX() {
        return $this->x;
    }
}

Dev folder:

class Class1 extends BaseClass1 {

    public function __construct() {
        $this->devMode = true;
        $this->x = 2;
    }
    ...
}

Root folder:

class Class1 extends BaseClass1 {

    public function __construct() {
        parent::__construct();
        $this->x = 1;
    }
    ...
}

Now depending if on the situation, if we had a class1 class in the dev folder who's constructer overrides the base class, we could identify where the class has been loaded from:

$class1 = new class1();

// Check if devMode is set (false by default)
if ($class1->isDevClass()) {
    // Class1 was loaded from dev, do bla; 
} else {
   // Class1 was loaded from root, do bla; 
}

// or
echo $class1->getX(); // will return 1 or 2 depending on the folder the class was loaded from

Not sure if that is what you're looking for though. There are defiantly other ways of doing this. You should want to reduce redundancy as much a possible. Eventually you would rather define a site wide constant which you could then pass as a identifier into the constructer of your class. You could then just make minor differences inside the class depending on what was set when constructing the class.

e.g Removing the redundant dev folder completely:

define("DEV", true);
...

class Class1 {
    protected $devMode;
    protected $x;

    public function __construct($mode = false) {
        $this->devMode = $mode;

        if ($this->devMode) {
            $this->x = 2;
        } else {
            $this->x = 1;
        }
    }

    public function doSomething() {
        if ($this->devMode) {
            // Do only devMode specific stuff
        }
    }
    ...
}
...
$class1 = new class1(DEV);

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top