web-dev-qa-db-fra.com

Chargement automatique des classes à partir de différents dossiers

Voici comment je charge automatiquement toutes les classes de mon dossier 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);
    }

Mais j'ai aussi des classes dans le dossier models et je veux aussi les charger automatiquement - que dois-je faire? Dois-je dupliquer le chargement automatique ci-dessus et simplement changer le chemin d'accès en models/ (mais n'est-ce pas répétitif ??)?

Merci.

MODIFIER:

ce sont mes noms de fichiers de classes dans le dossier du contrôleur:

class_controller_base.php
class_factory.php
etc

ce sont mes noms de fichiers de classes dans le dossier modèle:

class_model_page.php
class_model_parent.php
etc

c'est ainsi que je nomme ma classe de classes de contrôleur habituellement (j'utilise des traits de soulignement et des lowcaps),

class controller_base 
{
...
}

class controller_factory
{
...
}

c'est ainsi que je nomme généralement ma classe de classes de modèles (j'utilise des traits de soulignement et des lowcaps),

class model_page 
    {
    ...
    }

    class model_parent
    {
    ...
    }
42
laukok

Vous devez nommer vos classes pour que le trait de soulignement (_) Se traduise par le séparateur de répertoire (/). Quelques frameworks PHP font cela, comme Zend et Kohana.

Donc, vous nommez votre classe Model_Article Et placez le fichier dans classes/model/article.php, Puis votre chargement automatique fait ...

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

Notez également que vous pouvez utiliser spl_autoload_register() pour faire de n'importe quelle fonction une fonction de chargement automatique. Il est également plus flexible, vous permettant de définir plusieurs fonctions de type de chargement automatique.

S'il doit y avoir plusieurs fonctions de chargement automatique, spl_autoload_register () le permet. Il crée efficacement une file d'attente de fonctions de chargement automatique et parcourt chacune d'elles dans l'ordre où elles sont définies. En revanche, __autoload () ne peut être défini qu'une seule fois.

Modifier

Remarque: __ autoload a été DÉCONSEILLÉ à partir de PHP 7.2.0. cette fonctionnalité est fortement déconseillée. Veuillez vous référer à la documentation PHP pour plus de détails. http://php.net/manual/en/function.autoload.php

27
alex

Je vois que vous utilisez controller_***** Et model_***** Comme convention de dénomination de classe.

J'ai lu un fantastique article , qui suggère une convention de nommage alternative en utilisant namespace de php.

J'adore cette solution car peu importe où je mets mes cours. Le __autoload Le trouvera peu importe où il se trouve dans ma structure de fichiers. Cela me permet également d'appeler mes cours comme je veux. Je n'ai pas besoin d'une convention de dénomination de classe pour que mon code fonctionne.

Vous pouvez, par exemple, configurer votre structure de dossiers comme:

  • application/
    1. contrôleurs /
      • Base.php
      • Factory.php
    2. des modèles/
      • Page.php
      • Parent.php

Vos cours peuvent être organisés comme ceci:

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

et:

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

L'autochargeur pourrait ressembler à ceci (ou voir "une note sur le chargement automatique" à la fin):

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

Ensuite ... vous pouvez appeler des classes de trois manières:

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

ou,

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

...

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

ou,

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

...

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

EDIT - une note sur le chargement automatique:

Mon chargeur automatique principal ressemble à ceci:

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

Cet autochargeur est un mappage direct 1: 1 du nom de la classe à la structure du répertoire; l'espace de noms est le chemin du répertoire et le nom de classe est le nom de fichier. Ainsi, la classe application\controllers\Base() définie ci-dessus chargerait le fichier www/application/controllers/Base.php.

J'ai mis l'autochargeur dans un fichier, bootstrap.php, qui se trouve dans mon répertoire racine. Cela peut être soit inclus directement, soit php.ini peut être modifié en auto_prepend_file afin qu'il soit inclus automatiquement à chaque demande.

En utilisant spl_autoload_register vous pouvez enregistrer plusieurs fonctions de chargement automatique pour charger les fichiers de classe comme vous le souhaitez. C'est-à-dire, vous pouvez mettre tout ou partie de vos classes dans un répertoire, ou vous pouvez mettre tout ou partie de vos classes à espace de noms dans le même fichier . Très souple :)

61
br3nt

Je dois mentionner quelque chose sur les "bons" scripts de chargement automatique et la structure du code, alors lisez ATTENTIVEMENT ce qui suit


Garder en tete:

  • Nom de classe === Nom de fichier
  • Une seule classe par fichier

par exemple: Example.php contient

class Example {}
  • Espace de noms === Structure du répertoire

par exemple: /Path1/Path2/Example.php correspond

namespace Path1\Path2;
class Example {}
  • Il DEVRAIT y avoir un Root-Namespace pour éviter les collisions

par exemple: /Path1/Path2/Example.php avec root:

namespace APP\Path1\Path2;
class Example {}
  • N'utilisez JAMAIS de listes de chemins ou de répertoires définis manuellement, pointez simplement le chargeur vers le répertoire le plus haut
  • Gardez le chargeur LE PLUS RAPIDE POSSIBLE (car l'inclusion d'un fichier coûte assez cher)

Dans cet esprit, j'ai produit le script suivant:

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

Où le placer ..

  • /Loader.php <- c'est le chargeur
  • / Controller/... <- mettez vos trucs ici
  • / Modèle/... <- ou ici, etc.
  • / ...

Rappelez-vous:

  • si vous utilisez un espace de noms racine, le chargeur doit également se trouver dans cet espace de noms
  • vous pouvez préfixer $ Class pour répondre à vos besoins (controller_base {} -> class_controller_base.php)
  • vous pouvez changer __DIR__ en un chemin absolu contenant vos fichiers de classe (par exemple "/ var/www/classes")
  • si vous n'utilisez pas d'espaces de noms, tous les fichiers doivent être dans le même répertoire avec le chargeur (mauvais!)

Bon codage ;-)


Un petit examen des autres réponses: CECI IS JUSTE MON AVIS PERSONNEL - AUCUNE INFRACTION VISÉE!

https://stackoverflow.com/a/5280353/626731 @alex bonne solution, mais ne vous faites pas payer les noms de classe pour les mauvaises structures de fichiers ;-) c'est du travail pour les espaces de noms

https://stackoverflow.com/a/5280510/626731 @ Mark-Eirich ça marche, mais son style assez méchant/laid/lent/raide [..] pour le faire de cette façon ..

https://stackoverflow.com/a/5284095/626731 @tealou pour que son problème soit résolu, c'est l'approche la plus claire à ce jour :-) ..

https://stackoverflow.com/a/9628060/626731 @ br3nt cela reflète mon point de vue, mais s'il vous plaît (!) .. n'utilisez pas strtr !! .. ce qui m'amène à:

https://stackoverflow.com/a/11866307/626731 @Iscariot .. à vous, un petit "benchmark de vous-savez-bullshit:"

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.

Questions? .. (Mais il a en fait raison sur le chemin complet, y compris)

https://stackoverflow.com/a/12548558/626731 @ Sunil-Kartikey https://stackoverflow.com/a/17286804/626731 @jurrien

NE JAMAIS boucler dans un environnement à temps critique! Ne recherchez pas de fichiers sur os! - LENT

https://stackoverflow.com/a/21221590/626731 @sagits .. bien mieux que Marks ;-)

7
DerDu
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');
4
Sunil Kartikey

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

Je ne sais pas si c'est la meilleure solution ou non mais cela semble fonctionner parfaitement ...

Qu'est-ce que tu penses??

3
laukok

Ma version de @Mark Eirich répond:

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

Dans mon cas, seule la classe de contrôleur a le mot-clé dans son nom, adaptez-le à vos besoins.

2
sagits

Réponse la plus simple que je puisse vous donner sans écrire ces codes complexes et même sans utiliser l'espace de noms (si cela vous embrouille)

Exemple de code. Fonctionne à 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;
    }
}

Je suppose que la logique est explicable en elle-même. Vive mec! J'espère que cela t'aides :)

2

Voici ce que je ferais:

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

Tant que vous nommez vos fichiers de manière cohérente, comme class_controller_*.php et class_model_*.php, cela devrait fonctionner correctement.

1
Mark Eirich

La fonction __autoload () ne doit pas être utilisée car elle n'est pas encouragée. Utilisez plutôt spl_autoload (), spl_autoload_register (). __autoload () ne peut charger qu'une classe mais spl_autoload () peut obtenir plus de 1 classes. Et une chose de plus, à l'avenir, __autoload () pourrait devenir obsolète. Plus de choses peuvent être trouvées sur http://www.php.net/manual/en/function.spl-autoload.php

0
asi_x

Même si ce script n'a pas la convention de nom et ce fil est déjà un peu vieux, au cas où quelqu'un chercherait une réponse possible, voici ce que j'ai fait:

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

je ne sais pas si c'est vraiment optimal, mais il recherche dans les dossiers en lisant dir récursivement. Avec une fonction créative str_replace, vous pouvez obtenir votre convention de nom.

0
jurrien

J'utilise ça. Définissez fondamentalement votre structure de dossiers (MVC, etc.) comme une constante dans un tableau sérialisé. Appelez ensuite le tableau dans votre classe de chargement automatique. Fonctionne efficacement pour moi.

Vous pouvez évidemment créer le tableau de dossiers à l'aide d'une autre fonction, mais pour MVC, vous pouvez également le saisir manuellement.

Pour que cela fonctionne, vous devez appeler vos 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;}
    }



    }
0
Andy RS

Tout le monde copie et colle des choses à partir du code qu'ils ont obtenu sur Internet (à l'exception de la réponse sélectionnée). Ils utilisent tous String Replace.

Le remplacement de chaîne est 4 fois plus lent que strtr. Vous devriez l'utiliser à la place.

Vous devez également utiliser des chemins d'accès complets lorsque vous incluez des classes avec le chargement automatique car le système d'exploitation met moins de temps à résoudre le chemin d'accès.

0
Case