Quick start with
Zend Framework 2
Ilko Kacharov
Installation
Requirements
PHP 5.3.3
Apache/Nginx
Git
Composer
cd my/projects/dir
git clone git://github.com/zendframework/ZendSkeletonApplication.git
cd ZendSkeletonApplication
php composer.phar self-update
php composer.phar install
Installation with Composer
Install Skeleton Application
{
"name": "project-name",
"description": "Project Description",
"require": {
"php": ">=5.3.3",
"zendframework/zendframework": "~2.4"
}
}
php composer.phar self-update
php composer.phar install
composer.json
Configurations
Entry script
<?php
/**
* This makes our life easier when dealing with paths. Everything is relative
* to the application root now.
*/
chdir(dirname(__DIR__));
// Setup autoloading
require 'init_autoloader.php';
// Run the application!
Zend\Mvc\Application::init(require 'config/application.config.php')->run();
public/index.php
Current directory is the project root.
Everything is relative.
(to /project/, not /project/public/)
application.config.php
<?php
return array(
// This should be an array of module namespaces used in the application.
'modules' => array(
'Application',
'Example',
),
'module_listener_options' => array(
// This should be an array of paths in which modules reside.
// If a string key is provided, the listener will consider that a module
// namespace, the value of that key the specific path to that module's
// Module class.
'module_paths' => array(
'./module',
'./vendor',
),
// An array of paths from which to glob configuration files after
// modules are loaded. These effectively override configuration
// provided by modules themselves. Paths may use GLOB_BRACE notation.
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
),
);
config.global.php
<?php
return array(
'db' => array(
'driver' => 'PdoMysql',
'hostname' => 'localhost',
'database' => '',
'username' => '',
'password' => '',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'",
),
),
'view_manager' => array(
'display_not_found_reason' => true,
'display_exceptions' => false,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
),
);
<?php
return array(
'db' => array(
'hostname' => '111.222.333.444',
'database' => 'db-name',
'username' => 'db-user',
'password' => 'db-password',
),
'view_manager' => array(
'display_exceptions' => true,
),
);
config.local.php
Modules
Structure of Skeleton App
├───config
│ │ application.config.php
│ │
│ └───autoload
│ global.php
│ local.php
.......
├───public
│ │ .htaccess
│ │ index.php
│ │
│ ├───css
│ │ bootstrap-theme.css
│ │ bootstrap.css
│ │ style.css
│ │
│ ├───fonts
│ │ glyphicons-halflings.ttf
│ │
│ ├───img
│ │ favicon.ico
│ │ zf2-logo.png
│ │
│ └───js
│ bootstrap.min.js
│ jquery.min.js
│ respond.min.js
└───vendor
│
├───bin
│ classmap_generator.php
│ classmap_generator.php.bat
│ pluginmap_generator.php
│ pluginmap_generator.php.bat
│ templatemap_generator.php
│ templatemap_generator.php.bat
│
├───composer
│ autoload_classmap.php
│ autoload_namespaces.php
│ autoload_psr4.php
│ autoload_real.php
│ ClassLoader.php
│ installed.json
│
└───zendframework
├───zendframework
└───...
Autoloader
Zend\Loader\StandardAutoloader is designed as a PSR-0-compliant autoloader.
It assumes a 1:1 mapping of the namespace+classname to the filesystem
Namespace separators and underscores are translated to directory separators
Structure of Skeleton App
├───config
│ └───autoload
├───data
│ └───cache
├───module
│ └───Application
│ ├───config
│ ├───language
│ ├───src
│ │ └───Application
│ │ └───Controller
│ └───view
│ ├───application
│ │ └───index
│ ├───error
│ └───layout
├───public
│ ├───css
│ ├───fonts
│ ├───img
│ └───js
└───vendor
├───bin
├───composer
└───zendframework
└───zendframework
└───bin
├───module
│ └───Application // namespace
│ │ Module.php
│ │
│ ├───config
│ │ module.config.php
│ │
│ ├───language
│ │ en_US.mo
│ │ en_US.po
│ │
│ ├───src
│ │ └───Application // namespace
│ │ └───Controller
│ │ IndexController.php
│ │
│ └───view
│ ├───application
│ │ └───index
│ │ index.phtml
│ │
│ ├───error
│ │ 404.phtml
│ │ index.phtml
│ │
│ └───layout
│ layout.phtml
Structure of Skeleton App
<?php
namespace MyModule;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
Routing and controllers
Controller config
<?php
return array(
'controllers' => array(
'invokables' => array(
'Account\Controller\Index' => 'Account\Controller\IndexController',
'Account\Controller\Profile' => 'Account\Controller\ProfileController',
'Account\Controller\Settings' => 'Account\Controller\SettingsController',
),
),
'view_manager' => array(
'template_path_stack' => array(
__DIR__ . '/../view',
),
),
);
Templates folder is relative to config
/modules/MyModule/view/controller-name/view-name.php
Controller
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\JsonModel;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function indexAction()
{
$view = new ViewModel();
$view->title = 'index content';
$view->tags = ['eat', 'sleep', 'code', 'reapat'];
$view->setTemplate('controller-name/view-name');
return $view;
}
public function ajaxAction()
{
$view = new JsonModel([
'id' => 12345,
'title' => 'content',
'description' => 'more stuff',
]);
return $view;
}
}
Router Types
Routing is the act of matching a request
to a given controller.
Last rules are with highest priority.
Literal
Segment
Regex
Wildcard
Scheme
Routing type Literal
<?php
return array(
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
),
),
),
),
),
);
Routing type Segment
<?php
return array(
'router' => array(
'routes' => array(
'application' => array(
'type' => 'Literal',
'options' => array(
// http://hostname.com/application/:controller/:action
'route' => '/application',
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:controller[/:action]]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
),
),
RouteMatch
'route-name' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:id[/:type[/:sort]]]',
'constraints' => array(
'controller' => 'TestController',
'action' => 'test',
'id' => '[0-9]*',
'type' => '[a-zA-Z0-9_-]*',
'sort' => '[asc|desc]',
),
'defaults' => array(
'sort' => 'asc',
),
),
),
public function testAction()
{
$match = $this->getRouteMatch();
$id = $match->getParam('id');
$params = $match->getParams();
}
// Using controller plugin `params`
public function testAction()
{
$id = $this->params()->fromRoute('id');
$params = $this->params()->fromRoute();
}
if (!$event->getRouteMatch() instanceof RouteMatch) {
// No route match, this is a 404 page
}
Controller Plugins
Redirect
Url
Params
Layout
Forward
Identity
FlashMessenger
Params, Url, Redirect
// Using controller plugin `params`
public function testAction()
{
$id = $this->params()->fromRoute('id');
$params = $this->params()->fromRoute();
}
// Using controller plugin `redirect`
public function testAction()
{
$this->redirect()->toRoute('home');
}
// Using controller plugin `params`
public function testAction()
{
$url = $this->url('application/route-name', [
'id' => $this->params()->fromRoute('id'),
'type' => 'read',
'sort' => 'desc',
]);
}
Services
What is a Service?
A Service is an object that executes
complex application logic.
Part of the application that wires all difficult stuff together and gives you easy to understand results.
Services are created on demand and their instance is kept in the memory until needed.
Service dependencies
One service may require other services to work normally.
There are several ways to handle dependencies:
Constructor injection
ServiceLocatorAwareInterface
Dependency injection (DI)
$object = new B(new A());
$this->getServiceLocator()->get('ClassAlias');
$di = new Zend\Di\Di;
$b = $di->get('ClassAlias');
Service definition
'UserInputFiler' => 'SomeModule\InputFilter\User',
Keys are the service names
Values are valid class names to instantiate.
Interfaces
Best practice is to use Interfaces for the services.
<?php
namespace Module\Service;
class DemoService implements DemoServiceInterface
{
public function find($id);
{
}
public function findAll()
{
}
}
<?php
namespace Module\Service;
interface DemoServiceInterface
{
public function find($id);
public function findAll();
}
Service Locator
Service Locator
The Service Locator is a service/object locator, tasked with retrieving other objects.
Service Types
Invocable services
Factories
Abstract Factories
Module Definitions
Shared
Aliases
Invocable services
<?php
return array(
'service_manager' => array(
'invokables' => array(
'SomeService' => 'MyModule\Service\SomeService',
'AuthListener' => 'MyModule\Listener\AuthListener',
),
),
);
No constructor dependencies.
Zend will automatically set ServiceLocator
if they implement the AwareInterface.
Zend may throw exceptions if config is not correct.
Factories
<?php
return array(
'service_manager' => array(
'factories' => array(
'OtherService' => 'MyModule\Factory\OtherServiceFactory',
'AuthListener' => function ($sm) {
return new AuthListener($sm->get('db'));
},
),
),
'some_service_specific' => array('title'=>'Intro to ZF2'),
);
<?php
namespace User\Factory;
use User\Service\OtherService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class OtherServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sm)
{
$db = $sm->get('db');
$config = $sm->get('Config')['some_service_specific'];
$service = new OtherService($db, $config);
return $service;
}
}
Abstract Factories
<?php
return array(
'service_manager' => array(
'abstract_factories' => array(
// Valid values include names of classes implementing
// AbstractFactoryInterface, instances of classes implementing
// AbstractFactoryInterface, or any PHP callbacks
'SomeModule\Factory\CommonControlAppAbstractFactory',
),
),
);
class CommonControlAppAbstractFactory implements AbstractFactoryInterface
{
public function canCreateServiceWithName($serviceLocator, $name, $requestedName)
{
if (class_exists($requestedName.'Controller')){
return true;
}
return false;
}
public function createServiceWithName($serviceLocator, $name, $requestedName)
{
$class = $requestedName.'Controller';
return new $class;
}
}
Modules as Service Providers
Modules may act as service configuration providers
<?php
namespace MyModule;
use Zend\Mvc\MvcEvent;
class Module
{
public function getConfig()
{
return [
'service_locator' => [/*...*/]
];
}
public function getAutoloaderConfig() {/*common code*/}
public function getControllerConfig() {/*common code*/}
public function getControllerPluginConfig() {/*common code*/}
public function getFilterConfig() {/*common code*/}
public function getRouteConfig() {/*common code*/}
public function getServiceConfig() {/*common code*/}
public function getViewHelperConfig() {/*common code*/}
// ...
}
Shared
Indicating whether or not a service should be shared.
By default all services are shared - one instance.
If you specify a boolean false value
a new instance will be returned every time.
<?php
return array(
'service_manager' => array(
'shared' => array(
'OtherService' => false,
),
),
);
Aliases
<?php
return array(
'service_manager' => array(
'aliases' => array(
// Another alias
'translator' => 'MvcTranslator',
// Namespace + Class
'OtherService' => 'MyModule\Super\Long\Factory\Name\OtherService',
// Override zend service
'Zend\Authentication\AuthenticationService' => 'zfcuser_auth_service',
),
),
);
ServiceLocatorAwareTrait
<?php
namespace Zend\ServiceManager;
trait ServiceLocatorAwareTrait
{
/**
* @var ServiceLocatorInterface
*/
protected $serviceLocator = null;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
Usage in services
<?php
namespace MyModule\Service;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
class SampleService implements ServiceLocatorAwareInterface
{
use ServiceLocatorAwareTrait;
protected $anotherService;
public function doSomething()
{
$this->anotherService = $this->getServiceLocator()->get('AnotherService');
$this->anotherService->doMoreThings();
}
// No need to call setServiceLocator()
}
Events and Event Manager
Events and Event Manager
An Event is a named action.
A Listener is any PHP callback that reacts to an event.
An EventManager aggregates listeners for one or more named events, and triggers events.
Event
interface EventInterface
{
public function getName();
public function getTarget();
public function getParams();
public function getParam($name, $default = null);
public function setName($name);
public function setTarget($target);
public function setParams($params);
public function setParam($name, $value);
public function stopPropagation($flag = true);
public function propagationIsStopped();
}
interface EventManagerInterface
{
public function trigger($event, $target = null, $argv = array(), $callback = null);
public function triggerUntil($event, $target, $argv = null, $callback = null);
public function attach($event, $callback = null, $priority = 1);
public function detach($listener);
public function getEvents();
public function getListeners($event);
public function clearListeners($event);
// ...
public function attachAggregate(ListenerInterface $aggregate, $priority = 1);
public function detachAggregate(ListenerInterface $aggregate);
}
Attach and Trigger events
use Zend\EventManager\EventManager;
$events = new EventManager();
$events->attach('event-name', function ($e) {
$event = $e->getName();
$params = $e->getParams();
printf(
'Handled event "%s", with parameters %s',
$event,
json_encode($params)
);
});
$params = array('foo' => 'bar', 'baz' => 'bat');
$events->trigger('event-name', null, $params);
Trigger events
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerAwareTrait;
class Baz implements EventManagerAwareInterface
{
use EventManagerAwareTrait;
public function get($id)
{
$params = ['id' => $id, 'title' => 'test 1234'];
$results = $this->getEventManager()->trigger('Bar.pre', $this, $params);
// If an event stopped propagation, return the value
if ($results->stopped()) {
return $results->last();
}
// do some work...
// fetch data from database
$this->getEventManager()->trigger('Bar.post', $this, $params);
}
}
Attach listeners
use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\EventInterface;
class Bar implements ListenerAggregateInterface
{
protected $listeners = array();
public function attach(EventManagerInterface $e)
{
$this->listeners[] = $e->attach('Bar.pre', array($this, 'load'));
$this->listeners[] = $e->attach('Bar.post', array($this, 'save'));
}
public function load(EventInterface $e)
{
echo 'load...';
}
public function save(EventInterface $e)
{
echo 'save...';
}
}
In Zend 2
Everything is an event!
The MvcEvent
The MvcEvent gives access to the following:
-
Application object.
-
Request object.
-
Response object.
-
Router object.
-
RouteMatch object.
-
Result - the result of dispatching a controller.
-
ViewModel object, representing the layout.
The MvcEvent
MvcEvent::EVENT_BOOTSTRAP
MvcEvent::EVENT_ROUTE
MvcEvent::EVENT_DISPATCH
MvcEvent::EVENT_DISPATCH_ERROR
MvcEvent::EVENT_RENDER
MvcEvent::EVENT_RENDER_ERROR
MvcEvent::EVENT_FINISH
The MvcEvent
Bootstrap the application by creating the ViewManager.
Perform all the route work (matching...).
Dispatch the matched route to a controller/action.
Event triggered in case of a problem during dispatch
Prepare the data and delegate the rendering to the view.
Event triggered in case of a problem during the rendering
Perform any task once everything is done.
Database and models
Database adapter
Attributes of an adapter
Driver
Database
Username
Password
Hostname
Port
Charset
Mysqli, Sqlsrv, Pdo_Sqlite, Pdo_Mysql
the name of the database (schema)
the connection username
the connection password
the IP address or hostname to connect to
the port to connect to (if applicable)
the character set to use
Adapter types
SQLite
MySQL - Mysqli
MySQL - PDO
PostgreSQL
Oracle OCI8
SQLServer
IBM DB2
Query Execution
$adapter = $this->getServiceLocator()->get('db');
// Position params
$adapter->query('SELECT * FROM `records` WHERE `id` = ?', [5]);
// Named params
$result = $adapter->query('SELECT * FROM `records` WHERE `id` = :id', [':id' => 5]);
// Get current row
$row = $result->current();
// Get all rows
$row = $result->toArray();
use Zend\Db\Sql\Sql;
$sql = new Sql($adapter);
$select = $sql->select(['col_1','col_2','b.col_3']);
$select->from(['f' => 'foo']);
$select->join(['b' => 'bar'], 'f.foo_id = b.foo_id');
$select->where(['f.id' => 5]);
$selectString = $sql->getSqlStringForSqlObject($select);
$results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE);
Filters and Validators
Filters
The Zend\Filter component provides
a set of commonly needed data filters.
Filter chaining mechanism
$filter = new Zend\Filter\HtmlEntities();
echo $filter->filter('&'); // &
echo $filter->filter('"'); // "
$filter = new Zend\Filter\StripTags();
echo $filter->filter('<strong>My content</strong>'); // My content
$filter = new Zend\Filter\UriNormalize(['enforcedScheme' => 'https']);
echo $filter->filter('www.example.com'); // https://www.example.com
// Create a filter chain and add filters to the chain
$filterChain = new Zend\Filter\FilterChain();
$filterChain->attach(new Zend\I18n\Filter\Alpha())
->attach(new Zend\Filter\StringToLower());
// Filter the username
$username = $filterChain->filter($_POST['username']);
BaseName
Blacklist
Boolean
Callback
Compress
DataUnitFormatter
DateSelect
DateTimeFormatter
DateTimeSelect
Decompress
Decrypt
Digits
Dir
Encrypt
HtmlEntities
Inflector
Int
MonthSelect
Null
PregReplace
RealPath
StaticFilter
StringToLower
StringToUpper
StringTrim
StripNewlines
StripTags
ToInt
ToNull
UpperCaseWords
UriNormalize
Whitelist
Always check if a given filter exists.
Validators
The validator may provide information about which requirement(s) the input did not meet.
$validator = new Zend\Validator\EmailAddress();
if ($validator->isValid($email)) {
// email appears to be valid
} else {
// email is invalid; print the reasons
foreach ($validator->getMessages() as $messageId => $message) {
echo "Validation failure '$messageId': $message\n";
}
}
A validator examines its input with respect to some requirements and produces a boolean result.
Alnum
Alpha
Barcode
Between
Callback
CreditCard
Date
Db\RecordExists
Db\NoRecordExists
Digits
EmailAddress
GreaterThan
Hex
Hostname
Iban
Identical
InArray
Ip
Isbn
LessThan
NotEmpty
PostCode
Regex
Sitemap
Step
StringLength
Always check if a given validator exists.
Standard classes
ArrayObject
ArrayUtils
Hydrator
Extractor
PriorityQueue
Request
Response
Standard classes
Namespace Zend\Stdlib
https://slides.com/kachar/zf2-tech-exchange
Presentation at:
Quick start with Zend Framework 2
By Ilko Kacharov
Quick start with Zend Framework 2
- 951