1

I'm encountering a common issue in PHP (and most other languages) at the moment, where systems I'm designing need to be updated in multiple places to add new features, whereas I would like to have a core, where I can add new features, plugin's etc without having to touch the core code.

An example would be a a navigation menu, where the menu loads it's items from directory or namespace X. My aim is so once I or another developer adds a new file, extends a MenuItem class, whichever it needs to be, the class loading the menu would discover this new file and display it, without ever having to touch the core menu loading code.

The best I have come up with so far is:

function get_menu_items() {
    $menu_items = array();
    $raw_files = scandir(DOCUMENT_ROOT . '/menu/menu_items');
    foreach($raw_files as $file){
        require_once($file);
        $class_name = substr($file, 0, -4); // cut away the '.php'
        $menu_items []= new $class_name();
    }
    return $menu_items;
}

But the above feels wrong as I shouldn't be checking a directory for existence of files for features! I imagine this is a common design pattern like wordpress would use for plugin discovery? I've been searching for a while but not been able to better the above! Any help would be greatly appreciated!!

  • 2
    How else would PHP know about classes that haven't been defined yet? Basically, it'd just have to look in the same places the autoloader would. (Either that, or you abandon autoloading, at which point you can just check the list of defined classes....but you'd end up doing something similar to the above just to load the classes. :P ) – cHao Jan 28 '14 at 13:17
  • Hi cHao, thanks! I'm really surprised by that, like it really doesn't seem robust, I thought the likes of wordpress or any php plugin architecture would need something robust than just checking folder in the filesystem to see what plugins have been installed! – John Jan 28 '14 at 13:26
  • If you think about it, anything else would be less robust. The filesystem is the ultimate authority on whether a plugin is installed; if the files that comprise it are where they should be, then it's in. Any other source of info...like, say, a list of installed plugins in the DB...couldn't even hope to be as up-to-date as that. – cHao Jan 28 '14 at 15:43
  • You have to check for files to see if the plugin is there, but I'd make that an occasional action, something triggered by an admin in the administrator interface. Once a plugin is found, record it in the db and generate your menu items from that. Enumerating the file system on every page load to generate a menu seems like overkill. – GrandmasterB Jan 28 '14 at 17:53
  • This is why apps use configuration files. – Reactgular May 22 '14 at 10:30
0

Even tho your solution works fine in principle, it's extremely slow because of using the File-system API and quite vulnerable I'd say. For example, in theory, I can use your upload profile photo form to inject a PHP file with .jpg extension and move it into your plug-ins directory and let your application load it for me.

You might not notice the slow-ness of using the file-system api -- on a simple website/CMS with couple of online users at the same time, and no one might even try to attack your web application at all, however if you're looking for a Robust solution, I highly encourage you to come up with a solution that gives you Control over what files are being used/loaded by your script.

As an example you can store all of the new plug-ins in a JSON file and use that file as a valid source of installed plug-ins for your script. In this way what you really need is to add the file to the directory and then just add a simple key/value pair to your JSON file. It won't really take that much time, however it will gives a lot piece of mind I'd say.

A database-based solution could be a better idea if you need to let a none-technical person to choose between the plug-ins, however in your example it might be an overkill.

  • Hi Mahdi, thanks for your feedback. I've a security layer on top of the code example above, I removed the code to make it clearer as to what it was doing. Plugins are built by myself, so I don't have to worry about third parties uploading malicious files. Performance isn't the real issue, the page loading that code is loading the list of plugins so it doesn't see much load, and if a user chooses to install one, that is saved in a DB. My issue was that I would have thought there would be a PHP solution for this, like Namespaces instead of needing to hit the filesystem. – John Apr 24 '14 at 12:18
  • @John Namespaces won't work like that. With your explanation I think your current solution is your best bet. – Mahdi Apr 24 '14 at 12:21
  • Yeah it seems that way! Just very strange when you look at something you wrote and go "that's a very hacky solution, surely there's a better way", and then to hear it's probably the best solution! Thanks for the help @Mahdi – John Apr 24 '14 at 12:27
  • @John Welcome! You might also try to add a feature like that to the PHP core, if you feel like you have a good solution for that. – Mahdi Apr 24 '14 at 12:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.