Автозагрузка классов из разных папок - PullRequest
42 голосов
/ 12 марта 2011

Вот так я автоматически загружаю все классы в моей папке controllers,

# 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);
    }

Но у меня также есть классы в папке models, и я тоже хочу их автоматически загрузить - что мне делать? Должен ли я продублировать автозагрузку выше и просто изменить путь на models/ (но разве это не повторение ??)?

Спасибо.

EDIT:

это имена файлов моих классов в папке контроллера:

class_controller_base.php
class_factory.php
etc

это имена файлов моих классов в папке модели:

class_model_page.php
class_model_parent.php
etc

это то, как я обычно называю классы моего контроллера (я использую подчеркивания и нижние колпачки),

class controller_base 
{
...
}

class controller_factory
{
...
}

так я обычно называю класс моей модели (я использую подчеркивание и нижний колонтитул),

class model_page 
    {
    ...
    }

    class model_parent
    {
    ...
    }

Ответы [ 12 ]

60 голосов
/ 09 марта 2012

Я вижу, вы используете controller_***** и model_***** в качестве соглашения об именах классов.

Я прочитал фантастическую статью , в которой предлагается альтернативное соглашение об именах с использованием php's namespace.

Мне нравится это решение, потому что не имеет значения, куда я ставлю свои занятия. __autoload найдет его независимо от того, где он находится в моей файловой структуре. Это также позволяет мне называть мои занятия, как я хочу. Мне не нужно соглашение об именах классов, чтобы мой код работал.

Например, вы можете настроить структуру папок следующим образом:

  • применение /
    1. Контроллеры /
      • Base.php
      • Factory.php
    2. модель /
      • page.php
      • Parent.php

Ваши классы могут быть настроены так:

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

и

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

Автозагрузчик может выглядеть следующим образом (или посмотреть «примечание по автозагрузке» в конце):

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

Тогда ... вы можете вызывать классы тремя способами:

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

или

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

...

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

или

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

...

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

РЕДАКТИРОВАТЬ - заметка об автозагрузке:

Мой основной автозагрузчик выглядит так:

// 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;
});

Этот автозагрузчик является прямым отображением имени класса 1: 1 в структуру каталогов; пространство имен - это путь к каталогу, а имя класса - это имя файла. Таким образом, класс application\controllers\Base(), определенный выше, загрузит файл www/application/controllers/Base.php.

Я поместил автозагрузчик в файл bootstrap.php, который находится в моем корневом каталоге. Это может быть либо включено напрямую, либо php.ini может быть изменен на auto_prepend_file , чтобы он включался автоматически при каждом запросе.

Используя spl_autoload_register , вы можете зарегистрировать несколько функций автозагрузки для загрузки файлов классов любым удобным для вас способом. То есть, вы можете поместить некоторые или все ваши классы в один каталог, или вы можете поместить некоторые или все ваши классы пространств имен в один файл . Очень гибкий:)

27 голосов
/ 12 марта 2011

Вы должны назвать свои классы так, чтобы подчеркивание (_) преобразовывалось в разделитель каталогов (/). Это делают несколько фреймворков PHP, например Zend и Kohana.

Итак, вы называете свой класс Model_Article и помещаете файл в classes/model/article.php, а затем ваша автозагрузка делает ...

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;
}

Также обратите внимание, что вы можете использовать spl_autoload_register(), чтобы сделать любую функцию функцией автозагрузки. Это также более гибкий инструмент, позволяющий определять несколько функций типа автозагрузки.

Если должно быть несколько функций автозагрузки, spl_autoload_register () допускает это. Он эффективно создает очередь функций автозагрузки и проходит через каждую из них в порядке их определения. В отличие от этого, __autoload () может быть определен только один раз.

Редактировать

Примечание: __ автозагрузка УСТАРЕЛО с PHP 7.2.0. Полагаться на эту функцию крайне не рекомендуется. Пожалуйста, обратитесь к документации PHP для более подробной информации. http://php.net/manual/en/function.autoload.php

7 голосов
/ 29 января 2014

Я должен упомянуть кое-что о «хороших» сценариях автозагрузки и структуре кода, поэтому внимательно прочитайте следующее


Имейте в виду:

  • Имя класса === Имя файла
  • Только ОДИН класс на файл

Например: Example.php содержит

class Example {}
  • Пространство имен === Структура каталогов

Например: /Path1/Path2/Example.php соответствует

namespace Path1\Path2;
class Example {}
  • ДОЛЖНО быть пространство корневых имен, чтобы избежать коллизий

например: /Path1/Path2/Example.php с правами root:

namespace APP\Path1\Path2;
class Example {}
  • НИКОГДА не используйте заданные вручную пути или списки каталогов, просто укажите загрузчику самый верхний каталог
  • Храните загрузчик как можно быстрее (поскольку включение файла достаточно дорого)

Имея это в виду, я создал следующий скрипт:

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;
    }
}

Где его разместить ..

  • / Loader.php <- идет загрузчик </li>
  • / Контроллер / ... <- положить сюда свои вещи </li>
  • / Модель / ... <- или здесь и т. Д. </li>
  • / ...

Помните:

  • если вы используете корневое пространство имен, загрузчик также должен находиться в этом пространстве имен
  • вы можете добавить префикс $ Class в соответствии с вашими потребностями (controller_base {} -> class_controller_base.php)
  • вы можете изменить __DIR__ на абсолютный путь, содержащий ваши файлы классов (например, "/ var / www / classes")
  • если вы не используете пространства имен, все файлы должны находиться в одном каталоге вместе с загрузчиком (плохо!)

Счастливого кодирования; -)


Небольшой обзор на другие ответы: ЭТО ТОЛЬКО МОЕ ЛИЧНОЕ МНЕНИЕ - НАМЕРЕНИЕ НЕ ПРЕДНАЗНАЧЕНО!

https://stackoverflow.com/a/5280353/626731 @alex хорошее решение, но не заставляйте имена классов платить за плохие файловые структуры ;-) это работа для пространств имен

https://stackoverflow.com/a/5280510/626731 @ Марк-Эйрих это работает, но его довольно противный / уродливый / медленный / жесткий [..] стиль, чтобы сделать это таким образом ..

https://stackoverflow.com/a/5284095/626731 @ tealou для решения его проблемы, это пока самый ясный подход :-) ..

https://stackoverflow.com/a/9628060/626731 @ br3nt это отражает мою точку зрения, но, пожалуйста (!) .. не используйте strtr !! .. что приводит меня к:

https://stackoverflow.com/a/11866307/626731 @Iscariot .. для вас, небольшой "бейк-тест", который вы знаете:

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

Источник: http://www.simplemachines.org/community/index.php?topic=175031.0

Вопросы? .. (Но на самом деле он прав насчет полного пути, включая)

https://stackoverflow.com/a/12548558/626731 @ Сунил-Картикей https://stackoverflow.com/a/17286804/626731 @ jurrien

НИКОГДА не зацикливаться на критических по времени условиях! Не ищите файлы на ОС! - МЕДЛЕННО

https://stackoverflow.com/a/21221590/626731 @sagits .. намного лучше, чем у Маркса; -)

4 голосов
/ 23 сентября 2012

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');
3 голосов
/ 12 марта 2011

Вот мое решение,

/**
     * 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'));

Я не уверен, является ли это лучшим решением или нет, но, похоже, оно отлично работает ...

Что вы думаете?

2 голосов
/ 16 августа 2015

Самый простой ответ, который я могу вам дать, не записывая эти сложные коды и даже не используя пространство имен (если это вас смущает)

Пример кода. Работает 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;
    }
}

Я думаю, логика сама по себе объяснима. Ура, приятель! Надеюсь, это поможет:)

2 голосов
/ 19 января 2014

Моя версия ответа @Mark Eirich:

    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");

В моем случае только у класса контроллера есть ключевое слово в названии, адаптируйте его под свои нужды.

1 голос
/ 12 марта 2011

Вот что я бы сделал:

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;
}

Пока вы постоянно называете свои файлы, как class_controller_*.php и class_model_*.php, это должно работать нормально.

0 голосов
/ 22 марта 2014

Я использую это.По сути, определите структуру вашей папки (MVC и т. Д.) Как константу в сериализованном массиве.Затем вызовите массив в вашем классе автозагрузки.Работает эффективно для меня.

Очевидно, что вы можете создать массив папок, используя другую функцию, но для MVC вы также можете ввести его вручную.

Чтобы это работало, вам нужно вызвать ваши классы ...... 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;}
    }



    }
0 голосов
/ 25 июня 2013

Хотя у этого сценария нет соглашения об именах, и этот поток уже немного устарел, на тот случай, если кто-то ищет возможный ответ, вот что я сделал:

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);
    }
}

не уверен, еслиэто действительно оптимально, но он просматривает папки, рекурсивно читая dir.С творческой функцией str_replace вы можете получить свое имя cenvention.

...