2. About Me
@michaelpeacock
Head Developer @ Ground Six
Leading the development team and managing the
development process
We are a tech investment company: you bring ideas, we
partner and build the product
Author
Occasional Speaker
3. Symfony Components
Routing YAML
Event dispatcher Finder
Form Dependency Injection
Process HttpFoundation
Security HttpKernel
Console Locale
5. Why?
Solve common web application problems
Incredibly well documented
Standalone: use them how you want
[Components] Ideal for:
Refactoring
6. Installing
Create a composer.json file in the root of your project
{
"require": {
"company/project": "version",
}
}
Download Composer
curl -s https://getcomposer.org/installer | php
Run Composer
php composer.phar install
7. {
Routing
"require": {
"symfony/routing": "dev-master"
}
}
Looks at the users request and converts it into a
Controller::method paid
Request Context: POST|GET|PUT|DELETE
Looks within a list of pre-defined routes
Returns a class name and a method
10. // look in our routes folder
$locator = new SymfonyComponentConfigFileLocator(array(__DIR__ . '/../../'));
$loader = new SymfonyComponentRoutingLoaderYamlFileLoader($locator);
// the URL the user requested / is visiting
$request = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
// combine it with the request method to create a request context
$requestContext = new SymfonyComponentRoutingRequestContext($request,
$_SERVER['REQUEST_METHOD']);
// Create a router
$router = new SymfonyComponentRoutingRouter($locator, 'routes.yml',
array('cache_dir' => null), $requestContext);
try {
$requestURL = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
$requestURL = (strlen($requestURL) > 1) ? rtrim($requestURL, '/') :
$requestURL;
$route = $this->router->match($requestURL);
// explode the resulting route
$usersRoute = explode('::', $route['class']);
$controller = new $usersRoute[0]();
$variables = $route;
unset($variables['name']);
unset($variables['class']);
$action = $controller->$usersRoute[1]($container, $variables);
} catch (SymfonyComponentRoutingExceptionResourceNotFoundException $e) {
header('HTTP/1.0 404 Not Found');
die('Page not found.');
}
11. Event {
"require": {
"symfony/event-dispatcher": "dev-master"
Dispatcher }
}
At key points in your application you create an event
Pass this event to the dispatcher
Observers listen for specific events
Observers can be ordered - some events are observed by
multiple observers
Example: Displaying a “flash notification”
Set some sessions containing the notification
Redirect the user
12. Event: Notify
<?php
namespace ProjectFrameworkEvents;
class Notify extends RequestRedirection implements NotifiableMessageInterface
{
protected $notification;
protected $class = 'notice';
public function __construct($url = null, $notification = null, $class = 'notice')
{
parent::__construct($url);
$this->class = $class;
$this->notification = $notification;
}
public function getNotification()
{
return $this->notification;
}
public function getClass()
{
return $this->class;
}
}
13. <?php
namespace ProjectFrameworkEvents;
use SymfonyComponentEventDispatcherEvent;
class RequestRedirection extends Event
{
protected $url;
public function __construct($url = null)
{
$this->url = $url;
}
public function getURL()
{
return $this->url;
}
}
<?php
namespace ProjectFrameworkEvents;
interface NotifiableMessageInterface
{
public function getNotification();
public function getClass();
}
14. Listener: Set Notification
Session
<?php
namespace ProjectFrameworkListeners;
use ProjectFrameworkEvents;
use SymfonyComponentEventDispatcherEvent;
class SetPersistantNotification
{
public function setNotification( EventsNotifiableMessageInterface $event )
{
$_SESSION['system_notification'] = $event->getNotification();
$_SESSION['system_notification_class'] = $event->getClass();
}
}
15. Listener: Redirect
<?php
namespace ProjectFrameworkListeners;
use ProjectFrameworkEvents;
use SymfonyComponentEventDispatcherEvent;
class Redirect
{
public function redirectUser( EventsRequestRedirection $event )
{
header("Location: " . $event->getURL() );
exit();
}
}
16. Dispatcher
Create an event dispatcher
Create instance of listener
Add the listener
Event name
Callable: e.g. Object > Method array combo, Closure (event is passed)
Priority: for multiple listeners listening for the same event
$dispatcher = new EventDispatcher();
// Notification (Success, Warning, Error)
$setPersistantNotification = new ListenersSetPersistantNotification();
$dispatcher->addListener('notify', array($setPersistantNotification, 'setNotification'), 10);
// Redirect
$redirectUser = new ListenersRedirect();
$dispatcher->addListener('notifiy', array($redirectUser, 'redirectUser'), 0);
17. Raise and Dispatch Event
$url = $baseUrl . 'account';
$message = 'Your password was changed successfuly.';
$event = new EventsRedirectableNotification($url, $message, 'success');
$dispatcher->dispatch('notify', $event);
18. {
Forms
"require": {
"symfony/form": "dev-master"
}
}
A little fiddly to get running in a stand-alone mode
READ: I didn’t have time to figure it out for this talk :-(
Supports:
Creating forms programmatically
Processing form submissions
Uploading Files
Validating submissions with the Validator
22. {
Validator
"require": {
"symfony/validator": "dev-master"
}
}
Takes a series of constraints
Checks an input against these constraints
Returns a collection of violations
23. Validation Constraints
Constraints define the rule that an input must satisfy
Examples:
Min/Max Length
Email
Regex
Date
Min / Max / Null / NotNull / Empty / Not Empty
24. Documentation Example
<?php
use SymfonyComponentValidatorValidation;
use SymfonyComponentValidatorConstraints as Assert;
$validator = Validation::createValidator();
$constraint = new AssertCollection(array(
'name' => new AssertCollection(array(
'first_name' => new AssertMinLength(101),
'last_name' => new AssertMinLength(1),
)),
'email' => new AssertEmail(),
'simple' => new AssertMinLength(102),
'gender' => new AssertChoice(array(3, 4)),
'file' => new AssertFile(),
'password' => new AssertMinLength(60),
));
$violations = $validator->validateValue($input, $constraint);
25. {
Security
"require": {
"symfony/security": "dev-master"
}
}
Provides a framework for:
Authentication
Authorisation
Firewall: who can access which areas e.g. “edit”
Access Control: what data the user can
manipulate e.g. edit home page
26. HTTP {
"require": {
"symfony/http-foundation": "dev-master"
Foundation
}
}
Abstracts core HTTP functions
Request: Super Globals ($_POST, $_GET, etc)
Response: Status Codes, Cache, Cookies, Sessions
27. HTTPFoundation: Request
Object-Oriented wrapper for SuperGloabls
use SymfonyComponentHttpFoundationRequest;
$request = Request::createFromGlobals();
Property Purpose
request store $_POST
query store $_GET
cookies store $_COOKIE
attributes Application specific
files $_FILE
server $_SERVER
headers subset of $_SERVER
28. ParameterBag
Request properties are all ParameterBag or sub-classes
Provides special methods to manage contents, including:
all
keys
add
get
set
has
remove
$value = $request->query->get(‘my_get_parameter’);
29. Response
use SymfonyComponentHttpFoundationResponse;
$response = new Response();
$response->setContent('Hello PHPNE');
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/plain');
// alternatively...
$response = new Response('Hello PHPNE', 200, array('content-type', 'text/plain'));
$response->prepare();
// send the response to the user
$response->send();
30. Pimple
{
"require": {
"pimple/pimple": "dev-master"
}
}
Dependency Injection Container
Use it to store and pass objects, and other things
your code depends on
What is dependency injection?
public function __construct()
{
Not injected $this->database = new mysqli();
}
public function __construct($database)
{
Injected $this->database = $database;
}
31. Pimple: Lazy Loading
We can use anonymous functions to prevent
(dependent) objects being instantiated until they are
needed
$container['database'] = $container->share(function($container) {
try {
$db = new
PDO("mysql:host={$container['database_host']};port={$container['database_port']};dbname={$container['d
atabase_name']}", $container['database_user'], $container['database_pass'], array(PDO::ATTR_PERSISTENT
=> true, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'",PDO::MYSQL_ATTR_USE_BUFFERED_QUERY =>
true));
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $db;
}
catch (PDOException $e) {
echo $e->getMessage();
exit();
}
});
32. Parameters
$container['my_parameter'] = 'Some Value';
Objects
$container['my_object'] = function($container){
//will return this each and every time $container['my_object'] is accessed
return new MyObject();
};
33. Sharing Objects
$container['my_object'] = $container->share(function($container){
// will return a new instance first time accessed
// same object returned every other time
return new MyObject();
});
Protecting Parameters
$container['my_object'] = $container->protect(function($container){
// lets you return the result of an anonymous function as a parameter
return some_function();
});
A warning: You can’t modify a parameter
34. Twig
{
"require": {
"twig/twig": "dev-master"
}
}
Lightweight template engine
Really easy to extend and use
// create a twig filesystem loader so it can access templates
$loader = new Twig_Loader_Filesystem('templates');
// create a new twig environment and pass it the loader
$twig = Twig_Environment($loader);
// load the template
$twig->loadTemplate('index.twig');
// render it
$twig->render(array('title' => 'variable'));
35. Twig Template Syntax
{{ some_variable }}
{# some comment #}
{% set list_of_items = variable.getItems() %}
{% for item in list_of_items %}
<li>{{loop.index}}: {{item.name}}</li>
{% else %}
<li>Empty :-(</li>
{% endfor %}
36. Silex
{
"require": {
"silex/silex": "dev-master"
}
}
A “micro-framework” based off some of these
components and pimple
Designed for single-page PHP apps
37. Silex: A note on closures
Anonymous function: created without a name
Can accept parameters
Can use variables from compile time scope if defined
$objectToInject = new stdClass();
$test = function($parameter) use ($objectToInject){
// here we can use $parameter and $objectToInject
};
38. Setup
require_once '../vendor/autoload.php';
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentProcessProcess;
$app = new SilexApplication();
// Enable debugging.
$app['debug'] = true;
42. Silex & Components
Silex has a number of “providers” which allow certain
components to be plugged in
Silex knows nothing about the component
The component knows nothing about Silex
The provider bridges the gap
43. Silex Providers:
Doctrine: ORM
URL Generator
Monolog: Sessions
Validator
SwiftMailer
HTTP Cache
Session
Form
Twig
Any that you create
Translation
Problem with not using an event for this is that code is littered with SESSION setting and Header Redirects. What if we change how this needs to work? Lots of find and replaces\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
Dot syntax is very special. It can be used to access object properties, object methods, array elements, and even object getter methods. Templates can also be extended to chain multiple templates together.\n
\n
\n
\n
\n
Request parameter is optional, the Type Hint is used to work out if its used or not\n