Se i frammenti di tipo PHP abbondano su Internet, allora perché scriverne un altro? Beh, i frammenti PHP trovati al giro sono generalmente zoppi. Frammenti che generano una stringa casuale o un ritorno di $_SERVER["REMOTE_ADDR"] per un indirizzo IP da un client in realtà non sono così interessanti e sono di utilità modesta. Invece, ecco alcuni frammenti che senza dubbio troverete interessanti e utili e che mi hanno tolto dai guai in più occasioni.

Generare i CSV
Troppo spesso vediamo del codice che tenta di convertire un array multi-dimensionale di dati in formato CSV che assomiglia a quanto segue:
<?php
$csv = "";
foreach ($data as $row) {
    $csv .= join(",", $row) . "\n";
}
echo $csv;
?>         
Il problema è che i singoli elementi non hanno la sequenza di carattere di escape adeguata, e un valore singolo con le virgolette o con gli apici può buttare fuori un parser che più tardi userà come dati CSV. È molto meglio utilizzare la funzione nativa (built-in) fputcsv(), che esegue il tutto molto più velocemente anche perché la sua implementazione è in codice C e gestisce il necessario quotando/escapando i caratteri per noi.
Il codice seguente avvolge la logica per costruire l’output in CSV da un array di dati. Ha dei parametri opzionali per consentire di aggiungere le colonne di intestazione e per svuotare il CSV direttamente al browser o per restituire l’output sotto forma di stringa. L’eleganza del codice è l’uso dei flussi con fputcsv() che, come funzione, richiede un file aperto per gestire l’operazione.
<?php
function creaCSV($dati, $intestazioni = array(), $comeStringa = false) { 
    $ritorno = ''; 
	$flusso = $comeStringa
	    ? fopen("php://temp/maxmemory", "w+")
	    : fopen("php://output", "w");  

	if (!empty($intestazioni)) {
		fputcsv($flusso, $intestazioni);  
	}

	foreach ($dati as $elemento) {
		fputcsv($flusso, $elemento);
	}

	if ($comeStringa) {
		rewind($flusso);  
		$ritorno = stream_get_contents($flusso);  
		fclose($flusso);  
		return $ritorno;  
	} else {
		fclose($flusso);  
	}  
}
?>
            
Con la funzione creaCSV() nel nostro arsenale, generare i files di tipo CSV diventa semplice e facile.
Autocarica di classi
Caricare automaticamente i files contenenti delle classi è una prassi di normale amministrazione, ma forse non ci piacciono alcuni autoloader pesanti forniti da vari framework per PHP. Fortunatamente, è possibile creare un piccolo loader compatibile con lo standard PSR-0 adottato da PHP Standards Working Group.
Lo standard non descrive quali sono le funzionalità supportate, che dovrebbero essere forniti da un autoloader PSR-0 compatibile (metodi di registrazione, opzioni di configurazione, ecc.). Se è in grado di trovare automaticamente una definizione di classe nella cartella con il modello \\(\), allora è PSR-0 compliant. Inoltre, non specifica la directory principale per . L’extra, o il contorno, della maggior parte delle implementazioni autoloader è conveniente se è necessario specificare la posizione tramite il codice, ma è inutile se si usa semplicemente una directory già dentro il percorso di inclusione di PHP.
<?php
spl_autoload_register(function ($nomeClasse) {
    $nomeClasse = ltrim($nomeClasse, "\\");
    preg_match('/^(.+)?([^\\\\]+)$/U', $nomeClasse, $match);
    $nomeClasse = str_replace("\\", "/", $match[1])
        . str_replace(["\\", "_"], "/", $match[2])
        . ".php";
    include_once $nomeClasse;
});
?>
        
La magia la fa regex che divide il nome in entrata nelle sue parti costituenti, il nome della classe sarà sempre in $match[2], e $match[1] avrà il nome del namespace, che può essere una stringa vuota. Questo è necessario per identificare le parti perché la sottolineatura non ha alcun significato particolare nella porzione di spazio dei nomi facendo una sostituzione alla cieca con la sottolineatura e il backslash corretto
Dati a lunghezza fissa con unpack()
C’è chi sostiene che nel mondo moderno di oggi è pieno di XML e JSON e si potrebbe pensare che i formati a lunghezza fissa sono ormai estinti, ma ci deve essere uno sbaglio. C’è ancora una grande quantità di dati a lunghezza fissa, come ad esempio alcune voci di registro, MARC 21, NACHA, ecc. Detto tra noi, ho ancora un debole per i dati a lunghezza fissa.
I dati a lunghezza fissa sono relativamente facili da elaborare in linguaggi come C, perché i dati, una volta caricati in memoria, si allineano perfettamente con la struttura dell’accesso ai dati. Ma per alcuni, lavorando con i dati a lunghezza fissa in un linguaggio dinamico come PHP può essere una lotta: la mancanza di tipizzazione del linguaggio rende l’accesso alla memoria impossibile. E come risultato, spesso vediamo del codice che assomiglia a quanto segue:
<?php
// analisi di una intestazione NACHA
$row = fread($fp, 94);
$header = array();
$header["type"] = substr($row, 0, 1);
$header["priority"] = substr($row, 1, 2);
$header["immDest"] = substr($row, 3, 10);
$header["immOrigin"] = substr($row, 13, 10);
$header["date"] = substr($row, 23, 6);
$header["time"] = substr($row, 29, 4);
$header["sequence"] = substr($row, 33, 1);
$header["size"] = substr($row, 34, 3);
$header["blockFactor"] = substr($row, 37, 2);
$header["format"] = substr($row, 39, 1);
$header["destName"] = substr($row, 40, 23);
$header["originName"] = substr($row, 63, 23);
$header["reference"] = substr($row, 86, 8);
print_r($header);
?>
Probabilmente è strisciante. Va tutto bene, ma non vorrei che tale codice ci sia nella mia applicazione! È prolisso e l’indicizzazione è soggetta ad errori. Per fortuna c’è un’alternativa migliore: unpack().
La documentazione per unpack() nel manuale PHP dice: “spacchetta una stringa binaria in una matrice in base al formato dato” e mostra l’utilizzo con degli esempi con i caratteri di escape usando dati binari. Ciò che non può essere immediatamente evidente è che la funzione può essere utilizzata per analizzare stringhe a lunghezza fissa grazie allo specificatore del formato di A, che rappresenta un carattere (dopo tutto, non è una stringa seguita da una serie di bits e bytes?).
Utilizzando unpack(), l’esempio precedente può essere riscritto in modo più elegante come segue:
<?php
// analisi di una intestazione NACHA
$row = fread($fp, 94);
$header = unpack("A1type/A2priority/A10immDest/"
               . "A10immOrigin/A6date/A4time/"
               . "A1sequence/A3size/A2blockFactor/"
               . "A1format/A23destName/"
               . "A23originName/A8reference", $row);
print_r($header);
?>
Il formato della stringa in questo caso è semplicemente una serie di A che specifica i dati di tipo carattere, il conteggio dei caratteri per il campo specifico, nonché il nome della chiave che sarà assegnato nell’array finale, separati da barre. A6date per esempio analizza i 6 caratteri e li rende disponibili in $header["date"].
Modelli per l'HTML
Non c’è mai stato un grande consenso sui templating nella comunità PHP. Siamo tutti d’accordo che il mantenimento di HTML e PHP separati è auspicabile, ma si scontrano sulla convenienza nell’utilizzare librerie per templates (modelli) come Smarty o Twig. Alcuni fanno notare che PHP è sé stesso un motore di template, argomentando contro la velocità di una biblioteca, la sintassi, ecc; altri sostengono di aver beneficiato ampiamente utilizzando il DSL che forniscono sistemi di templating. Un compromesso è quello di modellare il codice HTML per mantenere le cose pulite utilizzando una classe molto picola scritta in PHP.
<?php
class Template {
    protected $dir;
    protected $vars;
    
    public function __construct($dir = "") {
        $this->dir = (substr($dir, -1) == "/") ? $dir : $dir . "/";
        $this->vars = array();
    }

    public function __set($var, $value) {
        $this->vars[$var] = $value;
    }

    public function __get($var) {
        return $this->vars[$var];
    }

    public function __isset($var) {
        return isset($this->vars[$var]);
    }

    public function set() {
        $args = func_get_args();
        if (func_num_args() == 2) {
            $this->__set($args[0], $args[1]);
        } else {
            foreach ($args[0] as $var => $value) {
                $this->__set($var, $value);
            }
        }
    }

    public function out($template, $comeStringa = false) {
        ob_start();
        require $this->dir . $template . ".php";
        $content = ob_get_clean();

        if ($comeStringa) {
            return $content;
        } else {
            echo $content;
        }
    }
}
?>     
Non è un vero e proprio motore per i templates, piuttosto è una classe di supporto che agisce come un “secchiello” per raccogliere le coppie chiave/valore dei dati di cui è possibile accedere ai files inclusi e designati come modelli. In primo luogo si crea un’istanza della classe Template, eventualmente si passa un nome di directory utilizzato per cercare il file del template. Poi, si passano i valori che dovrebbero popolare il modello con metodo il metodo set() o come nuda proprietà. Una volta che tutti i valori sono stati specificati, si chiama il metodo out() per il rendering del template.
<?php
$t = new Template();
// imposta un valore come se fosse una proprietà
$t->saluti = "Ciao Mondo!";
// imposta un valore con set()
$t->set("numero", 42);
// imposta valori multipli con set()
$t->set(array(
    "foo" => "Pippo",
    "bar" => "Pluto"
));
// esegue il rendering del template
$t->out("miotemplate");
?>    
Il file miotemplate.php per l’esempio potrebbe essere simile a questo:
<!DOCTYPE html>
<html lang="it">
 <head>
  <meta charset="utf-8">
...
 </head>
 <body>
  <div role="main">
   <h1><?=$this->saluti;?></h1>
...
  </div>
 </body>
</html>
All’interno dei file del modello si ha accesso alla gamma completa di funzionalità di PHP con valori di formato, valori di filtro, ecc, a nostro piacimento.
Con il secondo parametro opzionale per out() è possibile specificare se restituire il contenuto del modello come una stringa, invece di scaricarlo direttamente al browser, che è possibile sfruttare per sostituire il segnaposto in un modello con il risultato di un modello già compilato.
file_get_contents come alternativa a cURL
cURL è una robusta libreria per la comunicazione che utilizza diversi protocolli. È sicuramente molto completa, e ci sono casi in cui nessun’altra libreria sarà capace di fare ciò che fa cURL. Se si ha bisogno di funzionalità in modo esplicito, come esposto da cURL per realizzare il vostro compito, utilizzate cURL! Ma normalmente la maggior parte delle volte l’utilizzo cURL in PHP ruota attorno a richieste HTTP di tipo GET e POST, qualcosa che può essere fatto facilmente con le funzioni native (built-in) di PHP.
Il problema che basarsi su cURL per le richieste HTTP è duplice:

  • ci sono spesso molte opzioni che devono essere impostate, anche per la più semplice delle operazioni;
  • si tratta di un’estensione che potrebbe non essere disponibile a seconda dello hosting che ospita il nostro sito web; è una estensione comune, ma non è abilitato per default.

file_get_contents() e stream_context_create() sono due funzioni native di PHP e sono disponibili a partire dalla versione 4.3. Insieme, possono essere utilizzati per eseguire molte delle stesse richieste comunemente effettuate tramite cURL.
Per le richieste del tipo GET, file_get_contents() può essere usato da solo:

<?php
$html = file_get_contents("http://sitoweb.com/prodotto/33");
?>     
Per richieste in cui è necessario specificare le intestazioni HTTP, sia GET o uno qualsiasi degli altri metodi HTTP, è possibile creare un contesto passando una speciale chiavetta come array per lo stream_context_create() e quindi passare il contesto file_get_contents().
<?php
$context = stream_context_create(array(
    "http" => array(
        "method" => "POST",
        "header" => "Content-Type: multipart/form-data; boundary=--foo\r\n",
        "content" => "--foo\r\n"
            . "Content-Disposition: form-data; name=\"mioFile\"; filename=\"immagine.jpg\"\r\n"
            . "Content-Type: image/jpeg\r\n\r\n"
            . file_get_contents("image.jpg") . "\r\n"
            . "--foo--"
    )
));

$html = file_get_contents("http://example.com/upload.php", false, $context);
?>
L’esempio di sopra mostra il caricamento di un file via POST, con l’array come contesto, con le informazioni necessarie per l’operazione con le chiavi: “method“, “header” e “content“.
Quando si utilizza file_get_contents() per le richieste complesse come il caricamento di un file, può essere utile fare prima un finto web form ed eseguirlo tramite Firefox con firebug abilitato o qualcosa di simile per poi esaminare ciò che è stato incluso nella richiesta. Da lì si può dedurre gli elementi di intestazione importanti da includere.
Riassunto
Appunti su alcuni frammenti utili. Sono una vetrina per la risoluzione di problemi in modo creativo, usando funzionalità native di PHP. Spero che siano stati utili come sono stati per me quando cercavo queste risposte. Se avete altri frammenti che utilizza funzioni native, non esitate a condividerli nei commenti qui sotto.