Autoload classes from different folders
Asked Answered
Q

12

46

This is how I autoload all the classes in my controllers folder,

# auto load controller classes
    function __autoload($class_name) 
    {
        $filename = 'class_'.strtolower($class_name).'.php';
        $file = AP_SITE.'controllers/'.$filename;

        if (file_exists($file) == false)
        {
            return false;
        }
        include ($file);
    }

But I have classes in models folder as well and I want to autoload them too - what should I do? Should I duplicate the autoload above and just change the path to models/ (but isn't this repetitive??)?

Thanks.

EDIT:

these are my classes file names in the controller folder:

class_controller_base.php
class_factory.php
etc

these are my classes file names in the model folder:

class_model_page.php
class_model_parent.php
etc

this is how I name my controller classes class usually (I use underscores and lowcaps),

class controller_base 
{
...
}

class controller_factory
{
...
}

this is how I name my model classes class usually (I use underscores and lowcaps),

class model_page 
    {
    ...
    }

    class model_parent
    {
    ...
    }
Qualm answered 12/3, 2011 at 3:26 Comment(1)
Shouldn't class_factory.php be class_controller_factory.php? You're going to need some consistency if you want to code to stay simple.Nudibranch
W
29

You should name your classes so the underscore (_) translates to the directory separator (/). A few PHP frameworks do this, such as Zend and Kohana.

So, you name your class Model_Article and place the file in classes/model/article.php and then your autoload does...

function __autoload($class_name) 
{
    $filename = str_replace('_', DIRECTORY_SEPARATOR, strtolower($class_name)).'.php';

    $file = AP_SITE.$filename;

    if ( ! file_exists($file))
    {
        return FALSE;
    }
    include $file;
}

Also note you can use spl_autoload_register() to make any function an autoloading function. It is also more flexible, allowing you to define multiple autoload type functions.

If there must be multiple autoload functions, spl_autoload_register() allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once.

Edit

Note : __autoload has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged. Please refer to PHP documentation for more details. http://php.net/manual/en/function.autoload.php

Wardship answered 12/3, 2011 at 3:28 Comment(7)
thanks Alex. but i dont understand what your code does at all i'm afraid. don't get it what you explained in this bit 'You should name your classes so the underscore (_) translates to the directory separator (/).' please have a look on my edit in my post. thanks.Qualm
multiple autoload functions? I am lost!Qualm
@lauthiamkok You will need to change how your classes are named. Otherwise autoloading can't tell what is a model or class (unless you look up both folders, which can become expensive and will lead to ambiguity if you have an article controller and an article model).Wardship
isn't a model a class too?? I'm lost further now. never mind... thanks.Qualm
@lauthiamkok It is a class. I meant to say controller. :)Wardship
this is how I name my model classes - class model_article. and this is how I name my controller classes - class controller_article. are they wrong? I thought I have made them clear which are models and controllers??Qualm
@lauthiamkok In that case, you have named then fine, and the code above should be close to what you want.Wardship
W
68

I see you are using controller_***** and model_***** as a class naming convention.

I read a fantastic article, which suggests an alternative naming convention using php's namespace.

I love this solution because it doesn't matter where I put my classes. The __autoload will find it no matter where it is in my file structure. It also allows me to call my classes whatever I want. I don't need a class naming convention for my code to work.

You can, for example, set up your folder structure like:

  • application/
    1. controllers/
      • Base.php
      • Factory.php
    2. models/
      • Page.php
      • Parent.php

Your classes can be set up like this:

<?php
namespace application\controllers;
class Base {...}

and:

<?php
namespace application\models;
class Page {...}

The autoloader could look like this (or see 'a note on autoloading' at the end):

function __autoload($className) {
    $file = $className . '.php';
    if(file_exists($file)) {
        require_once $file;
    }
}

Then... you can call classes in three ways:

$controller = new application\controllers\Base();
$model = new application\models\Page();

or,

<?php
use application\controllers as Controller;
use application\models as Model;

...

$controller = new Controller\Base();
$model = new Model\Page();

or,

<?php
use application\controllers\Base;
use application\models\Page;

...

$controller = new Base();
$model = new Page();

EDIT - a note on autoloading:

My main auto loader looks like this:

// autoload classes based on a 1:1 mapping from namespace to directory structure.
spl_autoload_register(function ($className) {

    # Usually I would just concatenate directly to $file variable below
    # this is just for easy viewing on Stack Overflow)
        $ds = DIRECTORY_SEPARATOR;
        $dir = __DIR__;

    // replace namespace separator with directory separator (prolly not required)
        $className = str_replace('\\', $ds, $className);

    // get full name of file containing the required class
        $file = "{$dir}{$ds}{$className}.php";

    // get file if it is readable
        if (is_readable($file)) require_once $file;
});

This autoloader is a direct 1:1 mapping of class name to directory structure; the namespace is the directory path and the class name is the file name. So the class application\controllers\Base() defined above would load the file www/application/controllers/Base.php.

I put the autoloader into a file, bootstrap.php, which is in my root directory. This can either be included directly, or php.ini can be modified to auto_prepend_file so that it is included automatically on every request.

By using spl_autoload_register you can register multiple autoload functions to load the class files any which way you want. Ie, you could put some or all of your classes in one directory, or you could put some or all of your namespaced classes in the one file. Very flexible :)

Weekley answered 9/3, 2012 at 2:16 Comment(5)
How can the autoloader find the class?Pigskin
@Pigskin I have added additional info regarding how I autoload namespaced classes. Let me know if it is still not clear.Weekley
I tried this, but this won't work If I have my class inherited from another class.Rader
The superclass should be loaded via the class loader. You may need to include a use statement at the top of the class (same as described in the answer) and/or make sure the superclass is correctly namespaced in the class definition.Weekley
You could just check the DIRECTORY_SEPARATOR value before replacement: if (DIRECTORY_SEPARATOR != '\\') $className = str_replace('\\', DIRECTORY_SEPARATOR, $className);Bail
W
29

You should name your classes so the underscore (_) translates to the directory separator (/). A few PHP frameworks do this, such as Zend and Kohana.

So, you name your class Model_Article and place the file in classes/model/article.php and then your autoload does...

function __autoload($class_name) 
{
    $filename = str_replace('_', DIRECTORY_SEPARATOR, strtolower($class_name)).'.php';

    $file = AP_SITE.$filename;

    if ( ! file_exists($file))
    {
        return FALSE;
    }
    include $file;
}

Also note you can use spl_autoload_register() to make any function an autoloading function. It is also more flexible, allowing you to define multiple autoload type functions.

If there must be multiple autoload functions, spl_autoload_register() allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once.

Edit

Note : __autoload has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged. Please refer to PHP documentation for more details. http://php.net/manual/en/function.autoload.php

Wardship answered 12/3, 2011 at 3:28 Comment(7)
thanks Alex. but i dont understand what your code does at all i'm afraid. don't get it what you explained in this bit 'You should name your classes so the underscore (_) translates to the directory separator (/).' please have a look on my edit in my post. thanks.Qualm
multiple autoload functions? I am lost!Qualm
@lauthiamkok You will need to change how your classes are named. Otherwise autoloading can't tell what is a model or class (unless you look up both folders, which can become expensive and will lead to ambiguity if you have an article controller and an article model).Wardship
isn't a model a class too?? I'm lost further now. never mind... thanks.Qualm
@lauthiamkok It is a class. I meant to say controller. :)Wardship
this is how I name my model classes - class model_article. and this is how I name my controller classes - class controller_article. are they wrong? I thought I have made them clear which are models and controllers??Qualm
@lauthiamkok In that case, you have named then fine, and the code above should be close to what you want.Wardship
L
7

I have to mention something about "good" autoload scripts and code structure, so read the following CAREFULLY


Keep in Mind:

  • Classname === Filename
  • Only ONE class per file

e.g: Example.php contains

class Example {}
  • Namespace === Directory structure

e.g: /Path1/Path2/Example.php matches

namespace Path1\Path2;
class Example {}
  • There SHOULD be a Root-Namespace to avoid collisions

e.g: /Path1/Path2/Example.php with root:

namespace APP\Path1\Path2;
class Example {}
  • NEVER use manually defined path or directory lists, just point the loader to the top most directory
  • Keep the loader AS FAST AS POSSIBLE (because including a file is expensive enough)

With this in mind, i produced the following script:

function Loader( $Class ) {
    // Cut Root-Namespace
    $Class = str_replace( __NAMESPACE__.'\\', '', $Class );
    // Correct DIRECTORY_SEPARATOR
    $Class = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, __DIR__.DIRECTORY_SEPARATOR.$Class.'.php' );
    // Get file real path
    if( false === ( $Class = realpath( $Class ) ) ) {
        // File not found
        return false;
    } else {
        require_once( $Class );
        return true;
    }
}

Where to place it..

  • /Loader.php <-- there goes the loader
  • /Controller/... <-- put ur stuff here
  • /Model/... <-- or here, etc
  • /...

Remeber:

  • if you use a root namespace, the loader has to be in this namespace too
  • you may prefix $Class to match your needs (controller_base {} -> class_controller_base.php)
  • you may change __DIR__ to an absolute path containing your class files (e.g. "/var/www/classes")
  • if you don't use namespaces, all files has to be in the same directory together with the loader (bad!)

Happy coding ;-)


A little review at other answers: THIS IS JUST MY PERSONAL OPINION - NO OFFENSE INTENDED!

https://mcmap.net/q/363534/-autoload-classes-from-different-folders @alex good solution, but don't make you class names pay for bad file structures ;-) this is job for namespaces

https://mcmap.net/q/363534/-autoload-classes-from-different-folders @Mark-Eirich it works, but its pretty nasty/ugly/slow/stiff[..] style to do it this way..

https://mcmap.net/q/363534/-autoload-classes-from-different-folders @tealou for his problem to be solved this is the most clear approach so far :-) ..

https://mcmap.net/q/363534/-autoload-classes-from-different-folders @br3nt this reflects my point of view, but please(!) .. dont use strtr!! .. which brings me to:

https://mcmap.net/q/363534/-autoload-classes-from-different-folders @Iscariot .. to you, a little "you-know-bullshit-benchmark:

Time        sprintf preg_replace strtr    str_replace v1 str_replace v2
08:00:00 AM 1.1334  2.0955       48.1423  1.2109         1.4819
08:40:00 AM 1.0436  2.0326       64.3492  1.7948         2.2337
11:30:00 AM 1.1841  2.5524       62.0114  1.5931         1.9200
02:00:00 PM 0.9783  2.4832       52.6339  1.3966         1.4845
03:00:00 PM 1.0463  2.6164       52.7829  1.1828         1.4981
Average     1.0771  2.3560       55.9839  1.4357         1.7237


Method         Times Slower (than sprintf)
preg_replace   2.19
strtr          51.97
str_replace v1 1.33
str_replace v2 1.6

Source: http://www.simplemachines.org/community/index.php?topic=175031.0

Questions?.. (But he is in fact right about full path including)

https://mcmap.net/q/363534/-autoload-classes-from-different-folders @Sunil-Kartikey https://mcmap.net/q/363534/-autoload-classes-from-different-folders @jurrien

NEVER loop in time critical environment! Don't search for files on os! - SLOW

https://mcmap.net/q/363534/-autoload-classes-from-different-folders @sagits .. much better than Marks ;-)

Lenity answered 29/1, 2014 at 10:31 Comment(0)
H
4

function autoload($className)
{
//list comma separated directory name
$directory = array('', 'classes/', 'model/', 'controller/');

//list of comma separated file format
$fileFormat = array('%s.php', '%s.class.php');

foreach ($directory as $current_dir)
{
    foreach ($fileFormat as $current_format)
    {

        $path = $current_dir.sprintf($current_format, $className);
        if (file_exists($path))
        {
            include $path;
            return ;
        }
    }
}
}
spl_autoload_register('autoload');
Henleigh answered 22/9, 2012 at 23:40 Comment(0)
Q
3

Here is my solution,

/**
     * autoload classes 
     *
     *@var $directory_name
     *
     *@param string $directory_name
     *
     *@func __construct
     *@func autoload
     *
     *@return string
    */
    class autoloader
    {
        private $directory_name;

        public function __construct($directory_name)
        {
            $this->directory_name = $directory_name;
        }

        public function autoload($class_name) 
        { 
            $file_name = 'class_'.strtolower($class_name).'.php';

            $file = AP_SITE.$this->directory_name.'/'.$file_name;

            if (file_exists($file) == false)
            {
                return false;
            }
            include ($file);
        }
    }

    # nullify any existing autoloads
    spl_autoload_register(null, false);

    # instantiate the autoloader object
    $classes_1 = new autoloader('controllers');
    $classes_2 = new autoloader('models');

    # register the loader functions
    spl_autoload_register(array($classes_1, 'autoload'));
    spl_autoload_register(array($classes_2, 'autoload'));

I'm not sure whether it is the best solution or not but it seems to work perfectly...

What do you think??

Qualm answered 12/3, 2011 at 17:18 Comment(0)
J
2

My version of @Mark Eirich answer:

    function myload($class) {
      $controllerDir = '/controller/';
      $modelDir = '/model/';
      if (strpos($class, 'controller') !== false) {              
        $myclass = $controllerDir . $class . '.php';
      } else {
        $myclass = $modelDir . $class . '.inc.php';
      }
          if (!is_file($myclass)) return false;
          require_once ($myclass);

    }

    spl_autoload_register("myload");

In my case only controller class have the keyword in their name, adapt it for your needs.

Jurisdiction answered 19/1, 2014 at 19:34 Comment(0)
O
2

Simpliest answer I can give you without writing down those complex codes and even without using the namespace (if this confuses you)

Sample Code. Works 100%.

function __autoload($class_name){
$file = ABSPATH . 'app/models/' . $class_name . '.php';
if(file_exists($file)){
    include $file;
}else{
    $file = ABSPATH . 'app/views/' . $class_name . '.php';
    if(file_exists($file)){
        include $file;
    }else{
        $file = ABSPATH . 'app/controllers/' . $class_name . '.php';
        include $file;
    }
}

I guess the logic is explainable itself. Cheers mate! Hope this helps :)

Onshore answered 16/8, 2015 at 3:14 Comment(0)
N
1

Here's what I'd do:

function __autoload($class_name) {
    $class_name = strtolower($class_name);
    $filename = 'class_'.$class_name.'.php';

    if (substr($class_name, 0, 5) === 'model') {
        $file = AP_SITE.'models/'.$filename;
    } else $file = AP_SITE.'controllers/'.$filename;

    if (!is_file($file)) return false;
    include $file;
}

As long you name your files consistently, like class_controller_*.php and class_model_*.php, this should work fine.

Nudibranch answered 12/3, 2011 at 4:13 Comment(3)
if (substr($class_name, 0, 5) === 'model') { $file = AP_SITE.'models/'.$filename; } else $file = AP_SITE.'controllers/'.$filename; - sorry does it only either autoload classes from model folder or from controller folder but not both?Qualm
It loads both kinds of classes. If trying to load a class whose name begins with "model", it looks in the "models" folder. Otherwise it looks in the "controllers" folder. Remember, __autoload() is called each time you try to use a class that hasn't been loaded. Is the function working correctly?Nudibranch
Got it, Mark! Thanks. it should work I think. I have just come out with another solution - please see my answer that I posted. Thanks :-)Qualm
E
0

Everyone is is coping and pasting things from code they got off the internet (With the exception of the selected answer). They all use String Replace.

String Replace is 4 times slower than strtr. You should use it instead.

You should also use full paths when including classes with autoloading as it takes less time for the OS to resolve the path.

Extinctive answered 8/8, 2012 at 14:9 Comment(2)
Everyone is is coping and pasting things from code they got off the internet that's a (wrong) assumption.Wardship
When you google their question an answer to a different question comes up on a different website with that exact code. The sites post date predates this question.Extinctive
J
0

__autoload() function should not be use because it is not encourged. Use spl_autoload(), spl_autoload_register() instead. __autoload() just can load one class but spl_autoload() can get more than 1 classes. And one thing more, in future __autoload() may deprecated. More stuff can be find on http://www.php.net/manual/en/function.spl-autoload.php

Jez answered 13/5, 2013 at 20:9 Comment(0)
K
0

Altough this script doesn't have the name convention and this thread is already a bit old, in case someone is looking of a possible answer, this is what I did:

function __autoload($name) {
    $dirs = array_filter(glob("*"), 'is_dir');

    foreach($dirs as $cur_dir) {
        dir_searcher($cur_dir, $name);
    }

}

function dir_searcher($cur_dir, $name) {

    if(is_file("$cur_dir/$name.php")) {
        require_once "$cur_dir/$name.php";
    }

    $dirs = array_filter(glob($cur_dir."/*"), 'is_dir');
    foreach($dirs as $cdir) {
        dir_searcher("$cdir", $name);
    }
}

not sure if it is really optimal, but it searches through the folders by reading dir recursively. With a creative str_replace function you can get your name cenvention.

Kerrikerrie answered 24/6, 2013 at 23:31 Comment(0)
T
0

I use this. Basically define your folder structure (MVC etc) as a constant in a serialised array. Then call the array in your autoload class. Works efficiently for me.

You could obviously create the folder array using another function but for MVC you may as well type it in manually.

For this to work you need to call your classes ...... class.classname.php

  //in your config file
    //define class path and class child folders
    define("classPath","classes");
    define("class_folder_array", serialize (array ("controller", "model", "view")));


  //wherever you have your autoload class
    //autoload classes
    function __autoload($class_name) {
    $class_folder_array = unserialize (class_folder_array);
    foreach ($class_folder_array AS $folder){
        if(file_exists(classPath."/".$folder.'/class.'.$class_name.'.php')){require_once classPath."/".$folder.'/class.'.$class_name.'.php';break;}
    }



    }
Toughminded answered 22/3, 2014 at 10:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.