Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Phpne august-2012-symfony-components-friends


Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Phpne august-2012-symfony-components-friends

  1. Symfony Components & FriendsMichael Peacock, PHPNE August 2012
  2. About Me @michaelpeacockHead 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 productAuthorOccasional Speaker
  3. Symfony ComponentsRouting YAMLEvent dispatcher FinderForm Dependency InjectionProcess HttpFoundationSecurity HttpKernelConsole Locale
  4. FriendsPimple: dependency injection containerTwig: templating engineSilex
  5. Why?Solve common web application problemsIncredibly well documentedStandalone: 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 | php Run Composer php composer.phar install
  7. {Routing "require": { "symfony/routing": "dev-master" } }Looks at the users request and converts it into aController::method paidRequest Context: POST|GET|PUT|DELETELooks within a list of pre-defined routesReturns a class name and a method
  8. Defining RoutesYAMLPHP Code (A collection of Routes)Annotations
  9. comment_story_add: pattern: /news/{category}/{date}/{article} defaults: { class: CommentsController::addComment } requirements: date: "[0-9]{2}-[0-9]{2}-[0-9]{4}" _method: POST
  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 eventPass this event to the dispatcherObservers listen for specific events Observers can be ordered - some events are observed by multiple observersExample: 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. <?phpnamespace 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 NotificationSession<?phpnamespace 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
  19. Creating a form: with Silex$data = array();$data[a_hidden_field] = the value;$form = $app[form.factory]->createBuilder(form, $data) ->add(image, file) ->add(name) ->add(a_hidden_field, hidden) ->getform();return $app[twig]->render(form.twig, array(form => $form->createView())); <form action="/" method="post" {{ form_enctype(form) }}> {{ form_widget(form) }} <input type="submit" name="submit" /> </form>
  20. Process form submission if( POST == $request->getMethod()) { $form->bindRequest($request); if($form->isValid()) { $data = $form->getData(); } }
  21. File upload $uploadedFile = $form[image]->getData(); $path = $uploadedFile->getPath(); $originalName = $uploadedFile->getOriginalName(); $mimeType = $uploadedFile->getMimeType(); $uploadedFile->move( /var/www/uploads/upload.png);
  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: RequestObject-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. ParameterBagRequest properties are all ParameterBag or sub-classesProvides special methods to manage contents, including: all keys add get set has remove $value = $request->query->get(‘my_get_parameter’);
  29. Responseuse 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 engineReally 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}}: {{}}</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;
  39. Silex: Before running$app->before(function () use ($app){ $app->register(new SilexProviderTranslationServiceProvider(), array( locale_fallback => en, )); $app->register(new SilexProviderFormServiceProvider()); $app->register(new SilexProviderTwigServiceProvider(), array( twig.path => __DIR__./views, )); $app[conn] = new mysqli(localhost, root, , app);});
  40. Silex: Routes $app->get(image.{format}, function( $format ) use ($app) { $form = $app[form.factory]->createBuilder(form, array()) ->add(image, file) ->getform(); return $app[twig]->render("upload.{$format}.twig", array(title => Upload image, form => $form->createView())); })->assert(format, json|html );$app->post(/image.{format}, function( $format, Request $request) use ($app){ return $app[twig]->render("image.{$format}.twig", array));})->assert( format, json|html);
  41. Silex: Run $app->run();
  42. Silex & ComponentsSilex has a number of “providers” which allow certaincomponents to be plugged inSilex knows nothing about the componentThe component knows nothing about SilexThe 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
  44. ConclusionLots of componentsSolve lots of problemsEasy to useWhy reinvent the wheel? Use a Symfony Component orone of their friends
  45. Cheers!Thanks for listening!