Il Pattern di Zend Framework 2

58
© All rights reserved. Zend Technologies, Inc. Zend Framework 2 presenta Enrico Zimuel ([email protected]) Senior Software Engineer, Zend Technologies Zend Framework Core Team

description

Questo seminario web, originariamente ideato da Matthew Weier O'Phinney, Team Leader del progetto Zend Framework, fornisce una panoramica di questi pattern, li identifica e tratta le interfacce coinvolte e i casi d'uso concreti. Enrico Zimuel, Senior PHP Architect italiano, che recentemente si è unito al team di sviluppo dello Zend Framework, mostrerà come sia possibile creare le proprie implementazioni e come effettuarne lo slip-stream nelle applicazioni. Fra i pattern discussi: eventi, broker e dispatcher.

Transcript of Il Pattern di Zend Framework 2

Page 1: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Zend Framework 2

presenta Enrico Zimuel ([email protected])

Senior Software Engineer, Zend TechnologiesZend Framework Core Team

Page 2: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Sommario

● Breve storia del progetto Zend Framework

● Zend Framework 2.0

● I pre-requisiti di ZF 2

● Miglioramento delle performance

● Nuove funzionalità e design patterns

▶ Event Manager▶ Dependency Injection▶ Service Locator

Page 3: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Zend Framework

Page 4: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Breve storia di ZF

● October 2005: Annuncio del progetto

● March 2006: Prima versione (pulic review), 0.1.0

● Fall 2006: Riscrittura dell'MVC

● July 2007: Prima release stabile 1.0

● March 2008: Prima minor release 1.5.0

▶ Zend_Form, Zend_Layout

● September 2008: 1.6.0

▶ Integrazione con Dojo, PHPUnit scaffolding

● November 2008: 1.7.0

▶ Supporto AMF, miglioramento delle performance

Page 5: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Breve storia di ZF (2)● April 2009: 1.8.0

▶ Zend_Tool, Zend_Application● August 2009: 1.9.0

▶ Aggiunta di Zend_Feed_Reader

▶ Supporto di PHP 5.3● January 2010: 1.10.0

▶ Aggiunta di Zend_Feed_Writer, refactoring di Zend_Feed▶ Cambio della documentazione: adozione di PhD per la

generazione del manuale utente, aggiunta dei commenti, nuova sezione “Learning Zend Framework section”

● November 2010: 1.11.0

▶ Supporto dispositivi mobile tramiteZend_Http_UserAgent▶ Simple Cloud API tramite Zend_Cloud

Page 6: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Page 7: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Zend Framework 2.0● Nuova major release

▶ Ci ha permesso di non dover tener conto della retro-compatibilità

▶ Prerequisiti: PHP 5.3 e superiore

● Attenzione posta su:▶ Consistenza▶ Performance▶ Documentazione▶ Produttività utente

Page 8: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Primi passi verso ZF 2.0

● Conversione del codice da prefissi gestiti a mano (es. “Zend_Foo”) ai namespace nativi di PHP 5.3

● Refactoring delle Eccezioni

● Cambio di ZF per essere solo autoload

● Miglioramento e standardizzazione del sistema di plugin

Page 9: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

ZF 2.0 (dev3)

● Il 14 giugno è stata rilasciata la versione dev3 di Zend Framework 2.0

● Tra le funzionalità già implementate:

▶ Refactoring di Zend\Tool e CodeGenerator

▶ Migrazione e refactoring dei servizi LiveDocx ▶ EventManager ▶ Dependency Injection

● Maggiori info: http://bit.ly/lptIpN

Page 10: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

“Riscrivere il codice solo se ha senso”

Page 11: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

ZF2 in una slide

● Miglioramenti:

▶ Namespace (supporto nativo di PHP)▶ Exception▶ Autoloading▶ MVC▶ Plugin▶ Documentazione▶ Performance

● Nuove funzionalità:

▶ Event Manager▶ Dependency Injection / Service Locator▶ Supporto di nuovi servizi cloud▶ Molto altro ancora...

Page 12: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Namespace

Page 13: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

L'approccio di ZF2 ai namespace

● Formalizzare i prefissi utilizzati in ZF1

▶ Separatore di namespace correllato con il separatore di directory

● Aiutare ad identificare le dipendenze (imports)

▶ Abilitare il refactoring utilizzazando diverse implementazioni

▶ Facilitare il sistema di packaging

Page 14: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Namespace

namespace Zend\EventManager;

use Zend\Stdlib\CallbackHandler;

class EventManager implements EventCollection{ /* ... */}

namespace Zend\EventManager;

use Zend\Stdlib\CallbackHandler;

class EventManager implements EventCollection{ /* ... */}

Page 15: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Namespace● Interfacce come namespace

▶ I nomi d'interfaccia sono aggettivi o sostantivi

▶ Implementazione concreta in sub-namespace denominati dopo l'interfaccia

▶ Paradigma Contract-Oriented

Page 16: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Interfacce come Namespace

Zend/Session|-- Storage.php`-- Storage |-- ArrayStorage.php `-- SessionStorage.php

namespace Zend\Session;interface Storage { /* ... */}

namespace Zend\Session;interface Storage { /* ... */}

namespace Zend\Session\Storage;use ArrayObject, Zend\Session\Storage, Zend\Session\Exception;class ArrayStorage extends ArrayObject implements Storage{ /* ... */ }

namespace Zend\Session\Storage;use ArrayObject, Zend\Session\Storage, Zend\Session\Exception;class ArrayStorage extends ArrayObject implements Storage{ /* ... */ }

Page 17: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

ZF2 approccio ai namespace

● Ogni file di classe dichiara un namespace

● Un namespace per file

● Ogniclasse utilizzata che non fa parte del namespace attuale è importata (tipicamante tramite un alias)

● L'uso di riferimenti globali di classe è scoraggiato, eccetto nel caso di classi referenziate tramite stringhe

Page 18: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Autoloading

Page 19: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

ZF2 Autoloading● Non più chiamate require_once!● Differenti approcci:

▶ Stile ZF1 con include_path autoloader▶ Per-namespace/prefix autoloading▶ Class-map autoloading

Page 20: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Stile ZF1 di autoloading

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

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

Page 21: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

ZF2 NS/Prefix Autoloading

require_once 'Zend/Loader/StandardAutoloader.php';$loader = new Zend\Loader\StandardAutoloader();$loader->registerNamespace( 'My', __DIR__ . '/../library/My') ->registerPrefix( 'Phly_', __DIR__ . '/../library/Phly');$loader->register();

require_once 'Zend/Loader/StandardAutoloader.php';$loader = new Zend\Loader\StandardAutoloader();$loader->registerNamespace( 'My', __DIR__ . '/../library/My') ->registerPrefix( 'Phly_', __DIR__ . '/../library/Phly');$loader->register();

Page 22: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

ZF2 Class-Map Autoloading

return array( 'My\Foo\Bar' => __DIR__ . '/Foo/Bar.php',);

return array( 'My\Foo\Bar' => __DIR__ . '/Foo/Bar.php',);

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

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

● .classmap.php:

Page 23: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Class-Maps richiede più lavoro?

● Si, ma abbiamo già rilasciato un tool a linea di comando: bin/classmap_generator.php

● L'utilizzo è immediato:

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

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

● Class-Map verrà creato in .classmap.php

Page 24: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Perchè?

● Class-Maps evidenzia un miglioramento di performance del 25% rispetto all'autoloader del ZF1 (senza acceleratore di opcode)

▶ e un miglioramento del 60-85% con un acceleratore di bytecode PHP

● L'utilizzo di prefissi e namespace con percorsi specifici evidenzia un miglioramento del 10% sulle performance (senza acceleratore di opcode)

▶ e un miglioramento del 40% con un acceleratore di bytecode PHP

Page 25: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Strategie di autoloading

● Con strategie di autoloading differenti c'è la necessità di un factory

● Scegliere tra differenti strategie:

▶ Class-Map per performance migliori▶ Prefissi/Namespace per esigenze standard▶ Autoloader “classico” (in stile ZF1) per

ambienti di sviluppo

Page 26: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Migrare a ZF2

● Potete utilizzare il nuovo autoloader ZF2 da subito, anche per progetti ZF1

● Iniziare a migrare ora!

● “Backported ZF2 Autoloaders”by Matthew Weier O'Phinney http://bit.ly/mq4UAh

Page 27: Il Pattern di Zend Framework 2

27 © All rights reserved. Zend Technologies, Inc.

Exception

Page 28: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Problema● Tutte le eccezioni derivano da una classe

comune● Nessuna possibilità di espandere la

semantica delle eccezioni tramite SPL

Page 29: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

L'approccio di ZF2● Eliminare Zend_Exception● Ogni componente definisce una propria

interfaccia di eccezioni● Eccezioni addizionali vengono create in

un subnamespace specifico▶ Queste eccezioni estendono le funzionalità

SPL ed implementato le interfacce specifiche dei componenti

Page 30: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Vantaggi● Intercetttare specifiche eccezioni● Intercettare eccezioni di tipo SPL● Intercettare eccezioni a livello di

componenti● Intercettare basandosi su un tipo di

eccezione globale

Page 31: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Esempio di ExceptionZend/EventManager|-- Exception.php`-- Exception `-- InvalidArgument- Exception.php

Zend/EventManager|-- Exception.php`-- Exception `-- InvalidArgument- Exception.php

namespace Zend\EventManager;

interface Exception {}

namespace Zend\EventManager;

interface Exception {}

namespace Zend\EventManager\Exception;

use Zend\EventManager\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements Exception{}

namespace Zend\EventManager\Exception;

use Zend\EventManager\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements Exception{}

Page 32: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Esempio di Exception (2)

namespace Zend\EventManager\Exception;use Zend\EventManager\Exception;try { $events->trigger('foo.bar', $object);} catch (InvalidArgumentException $e) {} catch (Exception $e) {} catch (\InvalidArgumentException $e) {} catch (\Exception $e) {}

namespace Zend\EventManager\Exception;use Zend\EventManager\Exception;try { $events->trigger('foo.bar', $object);} catch (InvalidArgumentException $e) {} catch (Exception $e) {} catch (\InvalidArgumentException $e) {} catch (\Exception $e) {}

Page 33: Il Pattern di Zend Framework 2

33 © All rights reserved. Zend Technologies, Inc.

Nuove funzionalità

Page 34: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Nuove funzionalità● Zend\EventManager

● Zend\Di

● Nuovi servizi cloud:▶ Zend\Rackspace

▶ Zend\Service\GoGrid

▶ Zend\Cloud\Infrastructure

● Amazon S3● Rackspace● GoGrid

● E molto altro...

Page 35: Il Pattern di Zend Framework 2

35 © All rights reserved. Zend Technologies, Inc.

Event manager

Page 36: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Il problema● Come inserire sistemi di logging/debug in un progetto Zend

Framework?

● Come offrire la possibilità di utilizzare un sistema di caching senza estendere il codice del framework?

● Come offrire la possibilità di validare, filtrare, gestire un ACL, etc, senza estendere il codice del framework?

● Come offrire la possibilità di decidere l'ordine di un plugin, di intercettare un filtro, un evento, un trigger, etc?

● Come offrire uno strumento in grado di soddisfare queste esigenze?

Page 37: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

ZF2 Event Manager● Summa di diversi design patterns: PubSub,

SignalSlot, ed Intercepting Filters

● Non risolvono completamente il problema di composizione/statici

▶ Possiamo risolverlo in PHP 5.4 via Traits▶ Ci sono alcuni modi eleganti per gestire

componenti statici

Page 38: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Interfaccia EventCollection

namespace Zend\EventManager;use Zend\Stdlib\CallbackHandler;interface EventCollection{public function trigger($event, $context, $argv = array());public function triggerUntil($event, $context, $argv, $callback);public function attach($event, $callback, $priority = 1);public function detach(CallbackHandler $handle);public function getEvents();public function getHandlers($event);public function clearHandlers($event);}

namespace Zend\EventManager;use Zend\Stdlib\CallbackHandler;interface EventCollection{public function trigger($event, $context, $argv = array());public function triggerUntil($event, $context, $argv, $callback);public function attach($event, $callback, $priority = 1);public function detach(CallbackHandler $handle);public function getEvents();public function getHandlers($event);public function clearHandlers($event);}

Page 39: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Triggering di eventi

use Zend\EventManager\EventManager;$events = new EventManager();$events->trigger($eventName, $object, $params);

use Zend\EventManager\EventManager;$events = new EventManager();$events->trigger($eventName, $object, $params);

● Dove:▶ $eventName è il nome dell'evento, di solito il nome

del metod

▶ $object è l'oggetto triggering dell'evento

▶ $params sono i parametri di cui l'handler necessita, di solito gli argomenti del metodo

Page 40: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

CallbackHandler

$handler = $events->attach(’some-event’, function($e) use ($log) {

$event = $e->getName();$context = get_class($e->getTarget());$params = json_encode($e->getParams());$log->info(sprintf("%s: %s: %s", $event,

$context, $params));});

$handler = $events->attach(’some-event’, function($e) use ($log) {

$event = $e->getName();$context = get_class($e->getTarget());$params = json_encode($e->getParams());$log->info(sprintf("%s: %s: %s", $event,

$context, $params));});

Page 41: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Comporre un Event Manager

use Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager;

class Foo{

protected $events;public function events(Events $events = null) {

if (null !== $events) {$this->events = $events;

} elseif (null === $this->events) {$this->events = new EventManager(__CLASS__);

}return $this->events;

}public function doSomething($param1, $param2) {

$params = compact('param1', 'param2');$this->events()->trigger(__FUNCTION__, $this, $params);

}}

use Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager;

class Foo{

protected $events;public function events(Events $events = null) {

if (null !== $events) {$this->events = $events;

} elseif (null === $this->events) {$this->events = new EventManager(__CLASS__);

}return $this->events;

}public function doSomething($param1, $param2) {

$params = compact('param1', 'param2');$this->events()->trigger(__FUNCTION__, $this, $params);

}}

Page 42: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Utilizzo dei Trait!

use Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager;

trait Eventful{

public function events(Events $events = null) {if (null !== $events) {

$this->events = $events;} elseif (null === $this->events) {

$this->events = new EventManager(__CLASS__);}return $this->events;

}}class Foo{

use Eventful;protected $events;

}

use Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager;

trait Eventful{

public function events(Events $events = null) {if (null !== $events) {

$this->events = $events;} elseif (null === $this->events) {

$this->events = new EventManager(__CLASS__);}return $this->events;

}}class Foo{

use Eventful;protected $events;

}

Page 43: Il Pattern di Zend Framework 2

43 © All rights reserved. Zend Technologies, Inc.

Dependency Injection

Page 44: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Il problema

● Come gestire le dipendeze tra oggetti?▶ In particolare, come gestire le dipendenze

tra Controller?

Page 45: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

L'approccio di ZF2 ● Service Locator

▶ Schema di base: ● set($name, $service)● get($name)

▶ Formalizzazione dell'application service(mailer, logger, profiler, etc.)

▶ Buone interfacce con il typehinting

Page 46: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Service Locator

use Zend\Di\ServiceLocator, Zend\EventManager\EventManager;

class MyLocator extends ServiceLocator{ protected $events; protected $map = array('events' => 'getEvents');

public function getEvents() { if (null !== $this->events) { return $this->events; } $this->events = new EventManager(); return $this->events; }}

use Zend\Di\ServiceLocator, Zend\EventManager\EventManager;

class MyLocator extends ServiceLocator{ protected $events; protected $map = array('events' => 'getEvents');

public function getEvents() { if (null !== $this->events) { return $this->events; } $this->events = new EventManager(); return $this->events; }}

Page 47: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

L'approccio di ZF2 ● Dependency Injection Container

▶ Injection in costruzione (construct) e setters

▶ Via codice o tramite configurazione▶ Tipicamente utilizzato per iniettare un

service locator

Page 48: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Dependency Injection

$db = new Definition('My\Db\Adapter\Sqlite');$db->setParam('name', __DIR__ . '/../data/db/users.db');

$mapper = new Definition('My\Mapper\Db');$mapper->addMethodCall( 'setAdapter', array(new Reference('db')));

$service = new Definition('My\Resource\Users');$service->setParam('mapper', new Reference('mapper'));

$di = new DependencyInjector;$di->setDefinitions(array( 'db' => $db, 'mapper' => $mapper, 'users' => $service,));

$users = $di->get('users'); // My\Resource\Users

$db = new Definition('My\Db\Adapter\Sqlite');$db->setParam('name', __DIR__ . '/../data/db/users.db');

$mapper = new Definition('My\Mapper\Db');$mapper->addMethodCall( 'setAdapter', array(new Reference('db')));

$service = new Definition('My\Resource\Users');$service->setParam('mapper', new Reference('mapper'));

$di = new DependencyInjector;$di->setDefinitions(array( 'db' => $db, 'mapper' => $mapper, 'users' => $service,));

$users = $di->get('users'); // My\Resource\Users

Page 49: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Controller come servizi● Risolve il problema della dipendenza dei

controller● Ogni richiesta istanzia soltanto lo stretto

necessario● Migliore testabilità dei controller

Page 50: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Controller come servizi: esempio

$userController = new Definition('Site\Controller\User');$userController->setParam('service',

new Reference('users'));$di->setDefinition($userController, 'controller-user');

// Inside dispatcher:$controller = $di->get($controllerName);$result = $controller->dispatch($request, $response);

$userController = new Definition('Site\Controller\User');$userController->setParam('service',

new Reference('users'));$di->setDefinition($userController, 'controller-user');

// Inside dispatcher:$controller = $di->get($controllerName);$result = $controller->dispatch($request, $response);

Page 51: Il Pattern di Zend Framework 2

51 © All rights reserved. Zend Technologies, Inc.

Nuovi servizi cloud

Page 52: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Zend\Cloud● Supporto di nuovi servizi cloud:

▶ Rackspace ▶ GoGrid

● Supporto di Rackspace in Zend\Cloud\StorageService

▶ Rackspace

● Zend\Cloud\Infrastructure per la gestione delle infrastrutture di cloud computing:

▶ Amazon EC2▶ Rackspace Cloud Servers▶ GoGrid▶ Windows Azure

Page 53: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Zend\Cloud\Infrastructure● Zend\Cloud\Infrastructure (alpha version):

▶ Attualmente supporta soltanto Amazon EC2▶ A breve disponibili adapter per Rackspace

Servers e GoGrid▶ Download: http://bit.ly/imQLzB

● Zend\Service\Rackspace\Files (beta version):

▶ Download: http://bit.ly/muC6AT

Page 54: Il Pattern di Zend Framework 2

54 © All rights reserved. Zend Technologies, Inc.

Partecipare al progetto

Page 55: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Contribuire a ZF2

● ZF2 wiki:

▶ http://bit.ly/zf2wiki● zf-contributors mailing list:

[email protected]● IRC:

▶ #zftalk.dev su Freenode

Page 56: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Risorse● Git guide:

▶ http://bit.ly/zf2gitguide● GitHub:

▶ http://github.com/zendframework/zf2● Official repo:

▶ git://git.zendframework.com/zf.git▶ http://git.zendframework.com/

Page 57: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Domande?

Page 58: Il Pattern di Zend Framework 2

© All rights reserved. Zend Technologies, Inc.

Grazie!

Maggiori informazioni:http://www.zend.comhttp://framework.zend.com/