2009-08-31 2 views
12

Ho appena visto la funzione autoload() di php. Sembra una buona idea, ma non sono sicuro di come gestisca più directory. Il mio attuale sviluppo ha fondamentalmente una struttura di directory di libreria che raggruppa classi in sottodirectory per operazione. Mi sto chiedendo di dichiarare un include() per ogni directory ... che spero davvero di non dover fare.autoload e più directories

Potete raccomandare - grazie

risposta

1

Purtroppo, si ha a aggiungere esplicitamente ogni directory. Questo può essere fatto a livello di programmazione in uno script che attraversa in modo ricorsivo le directory, oppure è possibile specificare un elenco.

Probabilmente il modo più efficiente è specificare un elenco di directory e sottodirectory da cercare e aggiungerle al proprio percorso "include_path" utilizzando ini_set().

17

Si potrebbe voler dare un'occhiata al PEAR Convention per i nomi di classe, che è davvero ottimo per il caricamento automatico.

Fondamentalmente, si precisa che: gerarchia di classe

la pera è anche riflette nel nome della classe, ogni livello della gerarchia separato con un singolo trattino.

Ciò significa che trovare il file da includere per un nome di classe HTML_Upload_Error consiste semplicemente nel sostituire "_" con "/"; dandovi HTML/Upload/Error.php

Per ulteriori spiegazioni, e un paio di esempi, si può dare un'occhiata agli articoli:

BTW: questa convenzione è utilizzato da molti grandi quadri/librerie ;-)
Per esempio, Zend Framework usa questa convenzione - ed è davvero utile!

+0

Ho trovato questo metodo per funzionare molto bene. Un altro metodo che ho usato è quello di utilizzare un pacchetto statico per la mappa delle cartelle, che punta alle directory root in cui viene applicato il metodo precedente. –

+2

+1 per il grande link di spiegazione che spiega __autoload molto bene: http://blog.straylightrun.net/2009/05/06/autoload-magic/ –

1

Sembri confuso :) O forse sono confuso dalla tua domanda.

Spetta completamente a te scrivere una funzione che localizzi e carichi la classe, a PHP non interessa dove/quanti livelli è profondo.

E, esaminare SPL autoload too, ha la stessa funzionalità di base, ma è possibile scrivere più funzioni di caricamento automatico e quindi concatenarle. Può essere utile, se vuoi utilizzare alcune librerie esterne, che definiscono i propri autoloader che potrebbero essere in conflitto con i tuoi.

1

Presumo che si stia parlando dell'abilità di autoload SPL di PHP, dove si scrive la propria funzione e quindi si registra con l'SPL.

Il modo in cui lo si fa dipende da come si creano le funzioni di inclusione. È possibile dichiarare più funzioni di inclusione e quindi registrarle con PHP: quante dipendono da te. L'abilità di autoload SPL ti consente semplicemente di creare la tua funzione e quindi di dire a PHP di eseguire quella funzione ogni volta che una classe ha bisogno di essere inclusa.

Uno dei vantaggi della creazione di più è la possibilità di registrarli nel loro ordine di utilizzo, la directory più utilizzata prima a quella meno utilizzata. Inoltre, se una directory viene modificata o eliminata, è sufficiente modificare e/o eliminare la funzione responsabile.

È possibile scrivere una funzione che passerà attraverso l'intera struttura della cartella (sebbene non la consiglierei per facilità di amministrazione e disaccoppiamento del codice). Non esiste un modo "tecnicamente corretto" per farlo :)

10

Ecco una classe che ho scritto qualche tempo fa per uno scopo simile. Quella volta ero ancora in fase di apprendimento, quindi potrebbero esserci idee stupide; ha funzionato tuttavia.

L'idea di base è che esegue la scansione della directory di origine una volta e crea un array che associa classi ai relativi file di origine. La classe è registrata come caricatore automatico e, quando viene richiamata, include il file richiesto. Se non viene trovato, tenta di ricostruire l'array al volo.

/* register ClassLoader as class loader */ 
spl_autoload_register(array(ClassLoader::getInstance(), 'loadClass')); 


class ClassLoader { 

    private static $SAVE_FILE = 'ClassLoader.save.php'; 

    /* singleton */ 
    private static $instance; 

    /* stores a className -> filePath map */ 
    private $classList; 
    /* tells whether working from saved file */ 
    private $refreshed; 


    public static function getInstance() { 
     if (!isset(self::$instance)) { 
      self::$instance = new ClassLoader(); 
     } 
     return self::$instance; 
    } 

    private function __construct() { 
     $this->initClassList(); 
    } 

    public function loadClass($className) { 
     if (!array_key_exists($className, $this->classList) && !$this->refreshed) { 
      $this->refreshClassList(); 
     } 
     require_once($this->classList[$className]); 
    } 

    private function initClassList() { 
     if (file_exists(INCLUDES_DIR . self::$SAVE_FILE)) { 
      require_once(INCLUDES_DIR . self::$SAVE_FILE); 
      $this->refreshed = FALSE; 
     } else { 
      $this->refreshClassList(); 
     } 
    } 

    private function refreshClassList() { 
     $this->classList = $this->scanDirectory(INCLUDES_DIR); 
     $this->refreshed = TRUE; 

     $this->saveClassList(); 
    } 

    private function saveClassList() { 
     $handle = fopen(INCLUDES_DIR . self::$SAVE_FILE, 'w'); 
     fwrite($handle, "<?php\r\n"); 

     foreach($this->classList as $class => $path) { 
      $line = '$this->classList' . "['" . $class . "'] = '" . $path . "';\r\n"; 
      fwrite($handle, $line); 
     } 

     fwrite($handle, '?>'); 
     fclose($handle); 
    } 

    private function scanDirectory ($directory) { 
     // strip closing '/' 
     if (substr($directory, -1) == '/') { 
      $directory = substr($directory, 0, -1); 
     } 

     if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) { 
      return array(); 
     } 

     $dirH = opendir($directory); 
     $scanRes = array(); 

     while(($file = readdir($dirH)) !== FALSE) { 

      // skip pointers 
      if (strcmp($file , '.') == 0 || strcmp($file , '..') == 0) { 
       continue; 
      } 

      $path = $directory . '/' . $file; 

      if (!is_readable($path)) { 
       continue; 
      } 

      // recursion 
      if (is_dir($path)) { 
       $scanRes = array_merge($scanRes, $this->scanDirectory($path)); 

      } elseif (is_file($path)) { 
       $className = explode('.', $file); 
       if (strcmp($className[1], 'class') == 0 && strcmp($className[2], 'php') == 0) { 
        $scanRes[$className[0]] = $path; 
       } 
      } 
     } 

     return $scanRes; 
    } 

} 
+0

Amazing ClassLoader. – Jerska

1

Come già accennato, caricamento automatico SPL è funzionalmente una struttura sulla quale si deve innestare attuazione pratica - di attraversamento delle directory e di denominazione delle convenzioni fanno parte di tali considerazioni.

Prendiamo un esempio pratico sotto forma di Zend Loader: alla base, si tratta di un singleton che utilizza una convenzione di correlazione degli spazi dei nomi alle directory registrate con il percorso di inclusione di PHP. Esempio pratico:

set_include_path(get_include_path(). PATH_SEPARATOR. 'App/'); //Concat the "App" directory onto the existing include paths 
$loader = Zend_Loader::getInstance(); //because the autoloader is a singleton, we get a reference to it without assuming we need to first create it 
$loader->registerNamespace('App_'); //Tell autoloader it can look in the app directory to find classes if it can't find them in the default Zend directory. 

Ovviamente specifiche preoccupazioni di attuazione varierà da progetto a progetto, ma può essere meglio, sia come un esercizio di comprensione e per il riutilizzo del codice, per provare a programmare un caricatore automatico in grado di analizzare una formato di classe specifico (ad esempio 'directory_classname') in una mappa di directory, quindi caricare e convalidare la classe.