1

Currently, I have a pretty average php auto loader loading in my classes. I've come to a point in development where I will need to override a class with another class based on a variable. I'm running a custom SaaS application, and we have the occasional organization that will demand some weird change to the way the system functions. In the past, we've filled up our code with garbage by massive IF statements for orgs, such as

if(ORGID == 'ABCD'){
    //do this insane thing
}else{
    //Normal code here.
}

So, I've been toying with the idea of a dynamic auto loader. ORGID is one of the very first defines in the application. The entire application is running under a fixed namespace of COMPANY\PRODUCT; Here's a code sample of what I was thinking I could do.

class MyLoader {
    static public function load($name) {
        $temp = explode('\\',$name);
        $class = array_pop($temp);
        $name = str_replace('_',DIRECTORY_SEPARATOR,$class);
        if(file_exists(ROOT.'includes/_classes/' . $name . '.php')){
            include(ROOT.'includes/_classes/' . $name . '.php');
        }
    }
}
spl_autoload_register(__NAMESPACE__ .'\MyLoader::load');

Since ROOT and ORGID are defined before the autoloader comes into play, I thought about doing this

class MyLoader {
    static public function load($name) {
        $temp = explode('\\',$name);
        $class = array_pop($temp);
        $name = str_replace('_',DIRECTORY_SEPARATOR,$class);
        if(file_exists(ROOT.'includes/_classes/' . ORGID . '/' . $name . '.php')){
            include(ROOT.'includes/_classes/' . ORGID . '/' . $name . '.php');
        }elseif(file_exists(ROOT.'includes/_classes/' . $name . '.php')){
            include(ROOT.'includes/_classes/' . $name . '.php');
        }
    }
}
spl_autoload_register(__NAMESPACE__ .'\MyLoader::load');

While this works, I have to copy/paste my entire class into the org specific class file, then make changes. I can't extend the primary class, because the classes share the same name. The only option I've been able to come up with that would allow me to extend my classes in such a way is to never load the base class.

Instead of

$myObj = new myClass();

I call

$myObj = new customMyClass();

and I have a file called customMyClass(); which simply extends myClass without making any changes to it. This way, the auto loader will load customMyClass and then load myClass. If an organization has their own customMyClass in their organization folder, then it will load in that class, which will then properly load in myClass.

While this works, we have hundreds of class files, which would double if we had a custom file for each.

I've seen a couple of examples that use eval to handle similar situations. Is that really the only way to do this type of thing?

UPDATE: Just so I'm clear, the end goal is so that the thousands of places we've called $myObj = new myClass(); doesn't need to be rewritten.

GameCharmer
  • 614
  • 1
  • 7
  • 20
  • Reference submission: http://stackoverflow.com/questions/3800061/php-autoload-and-dynamic-runtime-class-definition-is-there-a-way-without – GameCharmer Jan 22 '14 at 21:49
  • public_html/includes/_classes/* suggests you placed the php classes in a publicly accessible directory. This is not a good practice... – Lajos Veres Jan 22 '14 at 21:50
  • *"I can't extend the primary class, because the classes share the same name"* <- this sounds truly ridiculous. Create a base class with shared functionality, extend it for the org specific classes and create a factory for instantiating said classes at runtime. – Phil Jan 22 '14 at 21:51
  • You mean I really can do "class myclass extends myclass {}" Phil? – GameCharmer Jan 22 '14 at 21:55
  • @GameCharmer No, but you can do `class ABCD extends myclass` – Phil Jan 22 '14 at 21:56
  • @Phil updated the original post. The end goal is such a system where I can call the same class and get different code based on which organization the current person is logged into. If I wanted to re-write everything, I wouldn't have asked the question. – GameCharmer Jan 22 '14 at 21:59
  • 2
    When you define your classes try using the "use" operator to give your class a different name. See: http://stackoverflow.com/a/10542287/1013526 – mason81 Jan 22 '14 at 22:01
  • @mason81 in my modified class, if I do something like USE ThisClass as ThatClass; can ThisClass still extend ThatClass? – GameCharmer Jan 22 '14 at 22:03
  • @GameCharmer In that case, you've written it badly to begin with. Your classes should not have the same name. They should instead extend from a common base class and / or implement an interface. This lets them all be the same *type* of thing (org). Use the [factory pattern](http://www.oodesign.com/factory-pattern.html) to instantiate your org classes instead of `new myclass`. You don't have to create a class for *every* org, just override the base class when necessary – Phil Jan 22 '14 at 22:08
  • @Phil for a project that's been evolving since 2002 before PHP5, I'd say it's doing pretty well. We're to the point where we need to standardize. A factory system such as what you've suggested seems to be the best route. I was looking for an "is this possible and if so how" scenario, not personal criticism of a project I've inherited. – GameCharmer Jan 22 '14 at 22:18
  • 2
    @GameCharmer I apologise, my comment above does come across as rude. What I'm trying to suggest / recommend is that you should try and clean it up now before it gets worse. Relying on auto-loader, directory-switching magic is only going to turn into a maintenance nightmare. Your classes should adhere to [SOLID](http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)) and [DRY](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principals – Phil Jan 22 '14 at 22:21
  • @Phil thank you. That's exactly what I was looking for. We're only just starting to upgrade the system to something more modern (a decade more modern) and all I had discovered so far was auto loaders. It was a magic cure-all until we ran into this exact scenario. Thanks again for the link, it's a great deal of help. Should that be submitted as an answer so I can accept it? – GameCharmer Jan 22 '14 at 22:35
  • 1
    @GameCharmer yes if you do something like `use ThisClass as ThatClass;` then you can do: `class ThisClass extends ThatClass` and that should work around your issue. I think this is only a temporary solution as @Phil has pointed out. I understand the precarious situation (old code base, slowly upgrading) and wish you the best of luck in your noble quest. – mason81 Jan 23 '14 at 15:39

0 Answers0