Autoloading classi in PHP, con un occhio ai namespace ed allo standard di programmazione psr-4.
Vediamo come caricare in modo automatico le classi all'interno di uno script PHP.
Per istanziare un oggetto di una determinata classe dobbiamo prima importare il file che contiene la classe, ad esempio con un require_once, e poi andiamo ad istanziare uno o più oggetti di quella classe.
Siamo nella directory "/var/www/html/test". Qui creiamo una "index.php" con questo contenuto:
<?php
spl_autoload_register('autoCaricamento');
function autoCaricamento($nome){
echo "stai provando ad istanziare un oggetto della classe ".$nome;
}
$obj=new Test();
?>
Al suo interno abbiamo istanziato un oggetto di classe "Test". Lanciamo questo file e vedremo un fatal error, e non solo:
stai provando ad istanziare un oggetto della classe Test
Fatal error: Uncaught Error: Class 'Test' not found in /var/www/html/test/index.php:8 Stack trace: #0 {main} thrown in /var/www/html/test/index.php on line 8
L'errore fatale è dovuto al fatto che non viene trovata la classe "Test", ma appare anche la frase "stai provando ad istanziare un oggetto della classe Test", e questo grazie alla funzione PHP spl_autoload_register che, quando cerchiamo di istanziare un oggetto di una classe non presente, accede automaticamente alla funzione indicata come suo parametro e racchiuso tra apici. Senza apici PHP la considererebbe una costante.
Alla nostra funzione di caricamento automatico, che in questo caso si chiama "autoCaricamento", PHP passa in automatico il nome della classe che stiamo provando ad istanziare cioè "Test". Ecco perchè appare "Test" nella frase.
Fatta questa premessa, creiamo una cartella "Lib" e dentro mettiamo un file "Test.php" con dentro un classe "Test" ed un metodo costruttore che sappiamo viene automaticamente lanciato quando la classe viene istanziata.
<?php
class Test{
public function __construct()
{
echo "ciao dalla classe ".__CLASS__." nel metodo ".__METHOD__;
}
}
?>
Adesso modifichiamo il file index, ed in particolare la funzione "autoCaricamento" in questo modo
<?php
spl_autoload_register('autoCaricamento');
function autoCaricamento($nome){
require_once "Lib/$nome.php";
}
$obj=new Test();
?>
In base a quanto detto prima, visto che il file si chiama esattamente come la classe, la funzione di autoCaricamento includerà il file della classe!
Lancia lo script index e vedrai come risposta il testo contenuto nel metodo costruttore
ciao dalla classe Test nel metodo Test::__construct
Bene, adesso nella cartella "Lib" crea un nuovo file "Test2.php" e dentro la classe "Test2" identica alla precedente.
Nella index, istanziamo anche un oggetto di classe "Test2"
<?php
spl_autoload_register('autoCaricamento');
function autoCaricamento($nome){
require_once "Lib/$nome.php";
}
$obj=new Test();
echo "<br>";
$obj=new Test2();
?>
Ecco il risultato
ciao dalla classe Test nel metodo Test::__construct
ciao dalla classe Test2 nel metodo Test2::__construct
Questa procedura, come puoi capire, è molto utile quando hai molte classi da importare all'interno del nostro script.
Miglioriamo il codice di caricamento in questo modo: nella index, invece di passare alla funzione spl_autoload_register il parametro "autoCaricamento", definitamo come parametro una funziona anonima, cioè una funzione a cui non dobbiamo dare un nome:
<?php
spl_autoload_register(
function ($nome){
require_once "Lib/$nome.php";
}
);
$obj=new Test();
echo "<br>";
$obj=new Test2();
?>
Il risultato sarà identico.
Sempre per ottimizzare il nostro lavoro, se hai molti file come la "index.php", invece di riscrivere in ogni file questa funzione spl_autoload_register, crea un nuovo file chiamato ad esempio "autoCaricamento.php" e sposta dentro la funzione
<?php
spl_autoload_register(
function ($nome){
require_once "Lib/$nome.php";
}
);
?>
Nella index, e negli altri files che lo utilizzano, lo includi così
<?php
include "autoCaricamento.php";
$obj=new Test();
echo "<br>";
$obj=new Test2();
?>
Andiamo vediamo come gestire correttamente il caricamento automatico delle classi anche quando definiamo nelle nostre classi i namespace, argomento già discusso in un precedente articolo che ti invito a leggere qualora tu non sappia cosa sono.
Namespace ed autoloading delle classi
Vediamo come implementare il caricamento automatico delle classi e per ognuna di queste abbiamo definito il namespace.
Nel nostro progetto abbiamo una "index.php" ed un file "autocaricamento.php" visto nel paragrafo precedente, in cui però, per adesso facciamo un semplice print al suo interno
<?php
spl_autoload_register(
function ($nome){
echo $nome;
}
);
?>
Poi abbiamo una alberatura di directory e files
index.php
autocaricamento.php
Lib
Admin
Ruoli.php
User
Info.php
Nella cartella "Lib" abbiamo una sottodirectory "Admin" che contiene un file "Ruoli.php", con una classe "Ruoli" e con un namespace uguale al path in cui si trova cioè "Lib\Admin"
<?php
namespace Lib\Admin;
class Ruoli{
public function __construct()
{
echo "ciao dalla classe ".__CLASS__;
}
}
?>
Sempre nella cartella "Lib" abbiamo una sottodirectory "User" che contiene un file "Info.php", con una classe "Info" e con un namespace uguale al path in cui si trova cioè "Lib\User"
<?php
namespace Lib\User;
class Info{
public function __construct()
{
echo "ciao dalla classe ".__CLASS__;
}
}
?>
Nella "index.php" includiamo la funzione di autocaricamento ed istanziamo un oggetto per ognuna delle due classi utilizzando un QUALIFIED NAME:
<?php
include "autocaricamento.php";
$obj=new Lib\Admin\Ruoli();
echo "<br>";
$obj=new Lib\User\Info();
?>
Lanciamo lo script, ed avremo un fatal error, ma non solo.
Lib\Admin\Ruoli
Fatal error: Uncaught Error: Class 'Lib\Admin\Ruoli' not found in /web/htdocs/www.webarea.it/home/demo/test/index.php:4 Stack trace: #0 {main} thrown in /web/htdocs/www.webarea.it/home/demo/test/index.php on line 4
Appare anche la stringa "Lib\Admin\Ruoli": PHP passa alla nostra funzione di autocaricamento non solo il nome della classe "Ruoli" ma anche il relativo namespace "Lib\Admin".
Vediamo come sfruttare questa informazione. Sappiamo che il file "Ruoli.php" che contiene la classe "Ruoli" si trova nel percorso "Lib/Admin/Ruoli.php", per cui nella funzione di autocaricamento, per includere il file "Ruoli.php", dobbiamo:
- sostituire ai backslash, cioè ai namespace separator, le slash così da costruire un reale path di un filesystem, utilizzando la funzione str_replace e sfruttando così la similitudine tra namespace e percorso nel filesystem
- alla fine aggiungere l'estensione .php
Infine al posto dell'echo mettiamo un require_once
<?php
spl_autoload_register(
function ($nome){
require_once str_Replace("\\","/",$nome).".php";
}
);
?>
Rilanciamo e vedremo come le nostre classi saranno state caricate correttamente. La risposta sarà
ciao dalla classe Lib\Admin\Ruoli
ciao dalla classe Lib\User\Info
In questo esempio abbiammo creato l'alberatura, i nomi dei file e delle classi utilizzando uno standard di programmazione PHP, chiamato psr-4, utilizzato specificatamente per la generazione di autoloading dei file. Ce ne occuperemo dettagliatamente in un prossimo articolo.
Stay tuned!
Potrebbe interessarti
- Le interfacce in PHP
- Le classi astratte e i metodi astratti, in PHP
- Le costanti di una classe in PHP
- Come estendere una classe in PHP ed il concetto di ereditarietà.
- Le proprietà e i metodi STATICI in PHP
- Il metodo costruttore in PHP
- Effettuare il caricamento automatico di classi con Composer, il noto gestore di dipendenze PHP
- Cos'è il type hinting, la sua evoluzione nelle varie versioni di PHP, ed il controllo sulla tipologia di dato con la modalità strict_mode.
- I metodi magici in PHP.
- Cosa sono i namespace in PHP. La guida completa, con esempi.
- Cosa sono i trait in PHP. La guida completa con esempi.
- Le classi in PHP. introduzione alla programmazione in stile OOP, metodi e proprietà di una classe.