Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Zend Framework 2, tutte le novità

Più che una nuova versione di Zend siamo dinanzi ad un nuovo framework, che sfrutta appieno le potenzialità Object Oriented di PHP e implementa i pattern più attuali
Più che una nuova versione di Zend siamo dinanzi ad un nuovo framework, che sfrutta appieno le potenzialità Object Oriented di PHP e implementa i pattern più attuali
Link copiato negli appunti

Zend Framework è uno dei framework open source più utilizzati dai programmatori PHP. Con oltre 15 milioni di download la versione 1.x ha avuto un notevole successo ed è stata scelta come piattaforma di base in molti progetti business critical e in molti prodotti software famosi, come il sistema di e-commerce Magento.

Le novità introdotte dalla versione 2.0 (attualmente è disponibile la versione 2.0.0beta4) sono tante ed importanti, le principali sono:

  • il pieno supporto al PHP 5.3 con le ultime novità del linguaggio: namespace, funzioni lambda e closure, late static binding, etc;
  • una nuova architettura MVC, basata sulla gestione di eventi;
  • performance migliorate, grazie, e non solo, ad un nuovo sistema di autoloading delle classi;
  • utilizzo di nuovi design pattern come la Dependency Injection, l'Event Manager ed il Service Locator;
  • gestione nativa dei moduli all'interno dell'architettura MVC;
  • una nuova gestione delle viste tramite un modello ad oggetti gerarchico;
  • un nuovo sistema di packaging basato su pyrus e composer.

Possiamo tranquillamente affermare che anche se la versione 2.0 è un'evoluzione della versione 1.0, è di fatto un nuovo progetto.

Le idee principali sono rimaste quelle della versione precedente ma l'architettura interna è stata completamente ridisegnata. Visto che si è deciso di supportare le novità della versione 5.3 del PHP la retrocompatibilità non poteva essere garantita con la versione precedente, perchè basata su PHP 5.2. Per questo motivo si è optato per una riscrittura completa dell'architettura del progetto, al fine di garantire una maggiore solidità e migliori performance.

Oltre alle novità tecniche, il progetto Zend Framework 2 presenta anche alcune novità nella sua gestione. In particolare è stata semplificata la procedura di collaborazione al progetto. Non è più necessario sottoscrivere il documento Contributor License Agreement (CLA). Inoltre la gestione dei sorgenti avviene tramite github, ciò significa che chi vuole collaborare al progetto può farlo tramite una semplice pull request (una richiesta di aggiunta o modifica al progetto), tutto qui.

Dopo aver parlato in generale delle novità vediamo in dettaglio alcune di queste, iniziando dal nuovo sistema di autoloading delle classi.

Il nuovo sistema di autoloading

Zend Framework 2 offre un nuovo sistema di autoloading delle classi basato su tre opzioni:

  • autoloading PSR-0 in stile Zend Framework 1;
  • autoloading basato su Namespace o Prefissi di classe;
  • autoloading tramite classmap.

Una novità importante del progetto Zend Framework 2 è la totale assenza di require_once. L'unica inclusione esplicita presente in un progetto Zend Framework 2 è tipicamente quella relativa al sistema di autoloading, tutte le altre classi possono essere utilizzate senza doverle precedentemente includere.

Il primo sistema di autoloading è simile a quello utilizzato nel progetto Zend Framework 1. Un esempio di utilizzo è riportato di seguito:

require_once 'Zend/Loader/StandardAutoloader.php';
$loader = new ZendLoaderStandardAutoloader(array(
    'fallback_autoloader' => true,
));
$loader->register();

Questo sistema consente di caricare automaticamente le classi seguendo lo standard PSR-0. Per individuare il file PHP contenente una specifica classe viene utilizzato il suo namespace, sostituendo il carattere "" con il separatore di cartella "/". Ad esempio, la classe ZendConfigConfig è memorizzata nel file Zend/Config/Config.php.

La classe ZendLoaderStandardAutoloader associa il namespace Zend al percorso del file ../../StandardAutoloader. Inoltre è previsto un meccanismo di fallback che consente di invocare il sistema di inclusione di default del PHP (stream_resolve_include_path) per l'inclusione di file non appartenenti al namespace Zend.

La seconda modalità di autoloading offerta da Zend Framework 2 consente di specificare il percorso di inclusione dei file PHP tramite namespace o tramite un prefisso prestabilito. Ad esempio è possibile specificare il percorso per il namespace "Foo" e per tutte le classi che iniziano con il prefisso "Bar_" nel modo seguente:

require_once 'Zend/Loader/StandardAutoloader.php';
$loader = new ZendLoaderStandardAutoloader();
$loader->registerNamespace('Foo', __DIR__ . '/../library/Foo')
       ->registerPrefix('Bar_', __DIR__ . '/../library/Bar');
$loader->register();

La terza modalità offerta da Zend Framework 2 per l'autoloading delle classi è basata sul concetto di class map. La class map è una mappa che associa ogni classe PHP il suo rispettivo file tramite percorso assoluto. In Zend Framework 2 una tipica class map è costituita da un file PHP (di solito denominato .classmap.php) che restituisce un array associativo contenente il nome della classe come chiave ed il percorso del file come valore:

<?php
return array(
    'MyFooBar' => __DIR__ . '/Foo/Bar.php',
);

Questo file viene utilizzato per configurare il componente ZendLoaderClassMapAutoloader nel modo seguente:

require_once 'Zend/Loader/ClassMapAutoloader.php';
$loader = new ZendLoaderClassMapAutoloader();
$loader->registerAutoloadMap(__DIR__ . '/../library/.classmap.php');
$loader->register();

Attraverso il file .classmap.php è possibile caricare le classi i maniera più veloce, poichè viene meno la ricerca del file da caricare, il percorso assoluto del file è prestabilito nella class map.

Il punto di forza di questo metodo è anche quello dolente, ossia la creazione della class map. Dover aggiornare a mano la class map è sicuramente un'operazione poco agevole. Per questo motivo abbiamo creato uno script (classmap_generator.php) che consente di creare automaticamente la class map per qualsiasi progetto PHP.

È sufficiente eseguire lo script da linea di comando, posizionandosi nella directory root del vostro progetto e specificando il parametro -w.

$ cd your/library
$ php /path/to/classmap_generator.php -w

Lo script genererà il file .classmap.php con l'elenco di tutte le classi presenti nel vs. progetto con i relativi file PHP.

L'utilizzo di questo script automatico è particolarmente indicato per il deploy di applicazioni PHP in ambienti di produzione. Infatti, solitamente il passaggio in produzione di un'applicazione web è gestito tramite un processo automatizzato di deploy. Inserendo l'esecuzione dello script classmap_generator.php nel processo di deploy si otterrà un sistema di autoloading dei file PHP performante per il proprio ambiente di produzione.

Giusto per darvi un'idea delle differenze di performance tra i vari sistemi di autoloading appena esposti abbiamo riscontrato che utilizzando l'autoloader basato su class map si possono ottenere incrementi di performance attorno al 20% rispetto allo standard autoloader. Utilizzando un acceleratore di codice, come ad esempio APC (), si possono ottenere incrementi di performance fino all'80%; ciò è dovuto al fatto che i file .classmap.php sono dei file statici e quindi possono sfruttare a pieno il sistema di cache degli acceleratori di codice PHP.

Il nuovo sistema di autoloading di Zend Framework 2 è stato adattato anche nel branch della versione precedente e sarà disponibile con la prossima versione 1.12, prevista nei prossimi giorni.

Dependency Injection

Durante lo sviluppo di Zend Framework 2 abbiamo utilizzato diversi design pattern cercando di seguire il principio dell'Inversion of Control (IoC):

"Per Inversion of Control (IOC - inversione di controllo) si intende un pattern di programmazione, secondo il quale si tende a tener disaccoppiati i singoli componenti di un sistema, in cui le eventuali dipendenze non vengono scritte all'interno del componente stesso, ma gli vengono iniettate dall'esterno". (Wikipedia)

In particolare abbiamo cercato di disaccoppiare il codice utilizzando la dependency injection, ossia una tecnica che consiste nell'iniettare le dipendenze tra classi attraverso il passaggio di oggetti. In questo modo le dipendenze possono essere definite, ad esempio, tramite dei file di configurazione e gestite quindi durante il run-time dell'applicazione.

Facciamo un esempio:

class Foo {
  protected $bar;
  public function __construct() {
    $this->bar= new Bar();
  }
}

In questo esempio, la classe Foo dipende dalla classe Bar, nel senso che per implementare la class Foo è necessario utilizzare la classe Bar. Il codice presenta una dipendenza stretta tra classi che è esplicita. Ad esempio, nel caso in cui volessimo modificare la dipendenza tra classi dovremmo modificare il codice sorgente della classe Foo.
Un modo più efficace per gestire la dipendenza è quello di iniettare un oggetto della classe Bar all'interno della classe Foo tarmite, ad esempio, il passaggio di un parametro in fase di inizializzazione della classe:

class Foo {
   protected $bar;
   public function __construct(Bar $bar) {
      $this->bar = $bar;
   }
}

In questo modo la dipendenza può essere gestita in fase di runtime ed è possibile cambiare l'oggetto Bar, utilizzato all'interno della classe Foo, senza dover modificare il codice sorgente.

Questo è un esempio di dependency injection. L'oggetto $bar poteva anche essere passato tramite un metodo setBar($bar) all'interno della classe Foo:

class Foo
{
   protected $bar;
   public function setBar(Bar $bar) {
      $this->bar = $bar;
   }
}

In generale, ci sono tre modalità per gestire la dependency injection:

  • in fase di inizializzazione della classe (come parametro del metodo __construct);
  • tramite un setter (setBar($bar) dell'esempio precedente);
  • tramite un'interfaccia (dove si utilizza un'interfaccia come type hinting al posto della classe).

L'ultimo metodo è quello più generico possibile perchè rende possibile la modifica della tipologia di classe da iniettare nel codice (a patto che implementi l'interfaccia richiesta).

Immaginate di dover gestire la dipendenza tra componenti in un'applicazione web che utilizza un centinaio di classi. La gestione manuale delle dipendenze, anche utilizzando il principio della dependency injection, potrebbe rilevarsi particolarmente complessa. Per questo motivo molto spesso si utilizza un Dependency Injection Container, ossia un componente in grado di gestire le dipendenze tra classi a runtime tramite l'utilizzo di un file di configurazione.

In Zend Framework 2 abbiamo sviluppato questo Di Container attraverso il componente ZendDi. ZendDi è in grado di gestire le tre differenti tipologie di dependency injection esposte in precedenza (construct, setter, interfaccia) attraverso l'utilizzo di semplici file di configurazione (gestiti tramite array PHP).

Ecco un esempio basato sulla classe Foo precedentemente esposta.
Di seguito è riportato il file di configurazione (di-config.php) delle dipenze della classe Foo:

<?php
return array(
     'Foo' => array(
         'setBar' => array(
             'bar' => array(
                 'type'     => 'Bar',
                 'required' => true,
             ),
         ),
     ),
 );

In questo array viene definita la dipendenza della classe Foo con la classe Bar attraverso il metodo setBar che è obbligatorio per il funzionamento della classe ('required' => true). Utilizzando il componente ZendDi è possibile gestire le dipendenze della classe Foo tramite questo file di configurazione:

use ZendDiDi,
    ZendDiConfiguration;
$config = require 'di-config.php';
$di     = new Di;
$config = new Configuration(array(
    'definition' => array('class' => $config)
));
$config->configure($di);
$foo = $di->get('Foo'); // contiene l'oggetto della classe Bar

Utilizzando il metodo get() di ZendDi è possibile ottenere un'instanza della classe Foo dopo aver eseguito i vincoli riportati nel file di configurazione (ossia dopo l'esecuzione del metodo setBar).

Per modificare la dipendenza tra la classe Foo e la classe Bar è possibile modificare il file di configurazione di-config.php, senza dover modificare il codice sorgente dell'applicazione. Possiamo affermare che ZendDi semplifica la gestione delle dipendenze tra classi facilitando il lavoro del programmatore soprattutto in progetti software molto complessi che utilizzano diverse classi.

Oltre alla gestione tramite un array di configurazione è possibile utilizzare anche un sistema di annotazione che consente di esplicitare le dipendenze tra classi direttamente nel codice sorgente tramite appositi tag, in standard phpdoc, presenti nei commenti.

Di seguito è riportato un esempio:

use ZendDiDefinitionAnnotation as Di;
class Foo
{
   protected $bar;
	/**
    * @DiInject()
    */
   public function setBar(Bar $bar) {
      $this->bar = $bar;
   }
}

La dipendenza è gestita attraverso la notazione @DiInject() inserita nel commento del metodo setBar. Per poter utilizzare questa notazione è necessario effettuare una compilazione del sorgente attraverso l'utilizzo del componente ZendDiDefinitionCompilerDefinition. Ecco un esempio d'utilizzo:

$compiler = new ZendDiDefinitionCompilerDefinition();
$compiler->addDirectory('Percorso della classe Foo');
$compiler->compile();
$definitions = new ZendDiDefinitionList($compiler);
$di = new ZendDiDi($definitions);
$bar = $di->get('Foo'); // contiene l'oggetto della classe Bar

Il vantaggio dell'utilizzo dell'annotazione è evidente, non c'è bisogno di utilizzare un file di configurazione esterno, è possibile specificare la dipendenza come commento, direttamente nel codice sorgente della classe. Lo svantaggio è dato dalla fase di compilazione che rende meno performante l'esecuzione dello script PHP. Quest'ultimo punto è facilmente risolvibile memorizzando l'output del processo di compilazione in un sistema di cache (ad esempio memorizzando la variabile $compiler dell'esempio precedente).

Un altro componente nuovo del progetto Zend Framework 2 è l'Event Manager, ossia un sistema di gestione degli eventi. Questo componente è stato utilizzato per l'implementazione della nuova architettura Model View Controller (MVC).

Il nuovo MVC di Zend Framework 2 è completamente basato su eventi. Fondamentalmente, il flusso di un'applicazione è guidato da tre step principali:

  • bootstrap, nel quale vengono inizializzati i moduli dell'applicazione con i vari file di configurazione;
  • route, dove vengono definite le "rotte" dell'applicazione, ossia le corrispondenze tra URL e codice da eseguire (in pratica quale modulo, controller e action eseguire);
  • dispatch, la fase di esecuzione vera e propria dell'applicazione nella quale viene eseguita la business logic e viene restituito l'output attraverso un template di gestione della vista.
Figura 1. Il flusso della nuova architettura MVC

Ad ognuna di queste tra fasi sono associati degli eventi predefiniti attraverso un componente di gestione dell'applicazione ZendServiceManager (che sostituisce il componente ZendApplication della versione 1.x).

È possibile aggiungere, eliminare o modificare un evento in qualsiasi punto dell'applicazione, ciò significa che è possibile cambiare il flusso di un'applicazione in qualsiasi momento. Questa architettura rende molto flessibile la gestione di un'applicazione web e da la possibilità di gestire il flusso in contesti differenti con nuovi interessanti casi d'uso.

Di seguito è illustrato un esempio su come creare ed eseguire un evento tramite il componente ZendEventManager.

use ZendEventManagerEventManager;
$events = new EventManager();
$events->attach('do', function($e) {
    $event  = $e->getName();
    $params = $e->getParams();
    printf('Handled event "%s", with parameters %s',
           $event,
           json_encode($params));
}, 100);
$params = array('foo' => 'bar', 'baz' => 'bat');
$events->trigger('do', null, $params);

Gli eventi vengono aggiunti all'EventManager attraverso l'utilizzo del metodo attach(). Questo metodo prevede la specifica del nome dell'evento, della funzione di callback per l'esecuzione e di un parametro opzionale che indica la priorità dell'evento, tramite un numero intero (il valore di default è 1). Gli eventi con priorità più alta vengono eseguiti per prima.

Nel nostro esempio abbiamo creato un evento denominato "do" che esegue, tramite una funzione anonima, una stampa del nome dell'evento eseguito e dei suoi parametri.

L'evento viene eseguito richiamando il metodo trigger() dell'EventManager. I parametri di questo metodo prevedono il nome dell'evento da eseguire, il contesto nel quale eseguire l'evento (tipicamente l'istanza dell'oggetto contenente l'evento; nel nostro caso nessuna poiché l'evento è creato tramite una funzione anonima) ed infine i parametri da passare all'evento.

Sistema di packaging

Con la nuova versione di Zend Framework è stato introdotto un nuovo sistema di packaging per la distribuzione e l'installazione del framework.

La distribuzione del framework può avvenire in tre modalità:

  • tramite il classico download dal sito http://packages.zendframework.com/;
  • tramite l'utilizzo del modulo PEAR2_Pyrus, http://pear2.php.net/PEAR2_Pyrus;
  • tramite composer, http://getcomposer.org/;

Utilizzando PEAR2_Pyrus si possono installare anche singoli componenti del framework. Ad esempio, nel caso si voglia utilizzare soltanto il componente ZendHttp di Zend Framework 2, si devono eseguire i seguenti comandi (in un ambiente GNU/Linux):

Installazione di pyrus:

$ wget http://packages.zendframework.com/pyrus.phar
$ pyrus.phar .
$ pyrus.phar . channel-discover packages.zendframework.com

Installazione del componente ZendHttp

$ pyrus.phar . install zf2/Zend_Http-beta

Le prime tre istruzioni devono essere eseguite soltanto una volta per l'installazione di pyrus. L'ultima istruzione è quella che effettua l'installazione del componente Zend_Http-beta. Pyrus è in grado di gestire le dipendenze tra componenti, ciò vuol dire che se la classe ZendHttp utilizza altri componenti, anch'essi verranno scaricati ed installati correttamente.

Oltre al sistema pyrus, Zend Framework 2 supporta anche il recente progetto composer. Composer è un sistema di gestione delle dipendenze di componenti e librerie PHP tramite un file di configurazione in formato JSON.
Ad esempio, utilizzando il seguente file di configurazione composer.json:

{
	"require": {
		"zendframework/zendframework": "2.0.0beta4"
	}
}

È possibile installare Zend Framework 2 nella sua versione 2.0.0beta4 tramite i seguenti comandi (in ambiente GNU/Linux):

$ curl -s http://getcomposer.org/installer | php
$ php composer.phar install

Il primo comando effettua il download di composer (nel formato phar) e il secondo comando esegue l'installazione dello Zend Framework 2 leggendo il file di configurazione precedentemente creato, composer.json.

Composer installerà la libreria all'interno della directory vendor. Inoltre, viene creato il file vendor/autoload.php per l'autoloading delle librerie scaricate. È sufficiente includere questo file di autoload nel proprio progetto per iniziare ad utilizzare Zend Framework 2.

Conclusioni

Il progetto Zend Framework 2 presenta numerose novità rispetto alla versione 1.0. L'architettura MVC è stata completamente riscritta ed è basata interamente su eventi. L'utilizzo di nuovi design pattern come la Dependency Injection, l'Event Manager, ed il Service Locator, hanno consentito di creare componenti disaccoppiati che possono essere facilmente utilizzati come componenti stand-alone. Il nuovo sistema di autoloading consente di ottenere un notevole aumento delle performance. Inoltre, grazie al nuovo sistema di packaging è più semplice distribuire ed installare l'intero framework o singoli componenti.

La versione 2.0 stabile di Zend Framework verrà rilasciata entro l'estate 2012. Attualmente è disponibile la versione 2.0.0beta4. Vi invito a provare questa versione beta scaricando ad esempio l'applicazione dimostrativa Zend Skeleton Application dal scaricandola da github

Questa applicazione è lo scheletro di partenza per sviluppare un'applicazione web basata su Zend Framework 2.

Ti consigliamo anche