Автозагрузка классов из разных папок

Вот как я загружаю все классы в моем 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/ (но разве это не повторяющееся??)?

Благодарю.

РЕДАКТИРОВАТЬ:

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

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 ответов

Решение

Вы должны назвать ваши классы, чтобы подчеркнуть (_) переводит в разделитель каталогов (/). Это делают несколько 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() может быть определен только один раз.

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

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

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

Я прочитал фантастическую статью, которая предлагает альтернативное соглашение об именах с использованием PHP 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, вы можете зарегистрировать несколько функций автозагрузки для загрузки файлов классов любым удобным для вас способом. То есть, вы можете поместить некоторые или все ваши классы в один каталог, или вы можете поместить некоторые или все ваши классы пространств имен в один файл. Очень гибкий:)

Я должен упомянуть кое-что о "хороших" сценариях автозагрузки и структуре кода, поэтому прочитайте следующее ВНИМАТЕЛЬНО


Иметь ввиду:

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

Например: 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 <- идет загрузчик
  • / Контроллер /... <- положи сюда свои вещи
  • / Модель /... <- или здесь и т. Д.
  • /...

Remeber:

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

Удачного кодирования;-)


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

/questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121430#39121430 @alex хорошее решение, но не заставляйте имена классов платить за плохие файловые структуры;-) это работа для пространств имен

/questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121421#39121421 @ Mark-Eirich это работает, но его довольно противный / уродливый / медленный / жесткий [..] стиль, чтобы сделать это таким образом..

/questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121420#39121420 @tealou для решения его проблемы, это пока самый ясный подход:-)..

/questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121427#39121427 @ br3nt это отражает мою точку зрения, но, пожалуйста (!) .. не используйте strtr!! .. что приводит меня к:

/questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121423#39121423 @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

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

/questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121422#39121422 @ Sunil-Kartikey /questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121419#39121419 @jurrien

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

/questions/39121414/avtozagruzka-klassov-iz-raznyih-papok/39121428#39121428 @sagits.. намного лучше, чем Маркс;-)

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

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

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

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

Как вы думаете??

Моя версия ответа @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");

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

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

Образец кода. Работает на 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;
    }
}

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

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

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, это должно работать нормально.

Все копируют и вставляют вещи из кода, который они вышли из Интернета (за исключением выбранного ответа). Все они используют String Replace.

String Replace в 4 раза медленнее, чем strtr. Вы должны использовать это вместо этого.

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

Функция __autoload () не должна использоваться, потому что она не защищена. Используйте взамен spl_autoload (), spl_autoload_register (). __autoload () просто может загрузить один класс, но spl_autoload () может получить более 1 класса. И еще одно, в будущем __autoload () может устареть. Больше материала можно найти на http://www.php.net/manual/en/function.spl-autoload.php

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

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 вы можете получить свое имя.

Я использую это. По сути, определите структуру вашей папки (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;}
    }



    }
Другие вопросы по тегам