Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting Into FLOW3 (IPCSE12)

Getting Into FLOW3 (IPCSE12)

From the International PHP Conference (Spring Edition) 2012, in Berlin, Germany.

FLOW3 is a web application platform which uses Domain-Driven Design as its major underlying concept. This approach makes FLOW3 easy to learn and at the same time clean and flexible for complex projects. It features namespaces, has an emphasis on clean, object-oriented code and provides a seemless Doctrine 2 integration. FLOW3 incorporates Dependency Injection in a way which lets you truly enjoy creating a stable and easy-to-test application architecture (no configuration necessary). Being the only Aspect-Oriented Programming capable PHP framework, FLOW3 allows you to cleanly separate cross-cutting concerns like security from your main application logic. This tutorial takes you through an imaginary project from scratch. During the journey we’ll visit all important areas of the framework.

Avatar for Robert Lemke

Robert Lemke

June 06, 2012
Tweet

More Decks by Robert Lemke

Other Decks in Technology

Transcript

  1. project founder of FLOW3 and TYPO3 “Phoenix” co-founder of the

    TYPO3 Association coach, coder, consultant 36 years old lives in Lübeck, Germany 1 wife, 2 daughters, 1 espresso machine likes drumming Robert Lemke
  2. Starters Installation Kickstart & Hello World! Controller, Actions, Arguments &

    HTTP Domain-Driven Design Doctrine Forms, Validation
  3. ?

  4. At a Glance FLOW3 is a web application platform •

    holistic concept for your apps • modular, extensible, package based • pedantically clean with focus on quality • puts a smile on developer’s faces • free & Open Source (LGPL v3) • backed by one of the largest Open Source projects
  5. Foundation for the Next Generation CMS TYPO3 “Phoenix” is the

    all-new Enterprise CMS • content repository, workspaces, versions, i18n, modular UI ... • powered by FLOW3 • compatible code base • use TYPO3 features in FLOW3 standalone apps as you like
  6. Git Clone $ git clone --recursive git://git.typo3.org/FLOW3/Distributions/Base.git . Cloning into

    .... remote: Counting objects: 3837, done. remote: Compressing objects: 100% (2023/2023), done. remote: Total 3837 (delta 2007), reused 2721 (delta 1465) Receiving objects: 100% (3837/3837), 3.49 MiB | 28 KiB/s, done. Resolving deltas: 100% (2007/2007), done.
  7. Set File Permissions $ sudo ./flow3 core:setfilepermissions robert _www _www

    FLOW3 File Permission Script Checking permissions from here upwards. Making sure Data and Web/_Resources exist. Setting file permissions, trying to set ACLs via chmod ... Done. $ sudo usermod -a -G www-data robert $ sudo dscl . -append /Groups/_www GroupMembership robert Linux: Mac OS X:
  8. Set Up Database Connection Configuration/Settings.yaml # # # Global Settings

    # # # TYPO3: FLOW3: persistence: backendOptions: dbname: 'demo' user: 'demo' password: 'password' host: '127.0.0.1' # only on Windows: core: phpBinaryPathAndFilename: 'C:/path/to/php.exe'
  9. Set Up Virtual Host Apache Virtual Host <VirtualHost *:80> DocumentRoot

    /opt/local/apache2/htdocs/Talks/FLOW3/Web/ ServerName dev.flow3.rob SetEnv FLOW3_CONTEXT Development </VirtualHost> <VirtualHost *:80> DocumentRoot /opt/local/apache2/htdocs/Talks/FLOW3/Web/ ServerName flow3.rob SetEnv FLOW3_CONTEXT Production </VirtualHost>
  10. Tackling the Heart of Software Development Domain-Driven Design A methodology

    which ... • results in rich domain models • provides a common language across the project team • simplify the design of complex applications FLOW3 is the first PHP framework tailored to Domain-Driven Design /** * A Book * * @FLOW3\Scope(“prototype”) * @FLOW3\Entity */ class Book { /** * @var string */ protected $title; /** * @var string */ protected $isbn; /** * @var string */ protected $description; /** * @var integer */ protected $price;
  11. Domain-Driven Design Domain activity or business of the user Domain-Driven

    Design is about • focussing on the domain and domain logic • accurately mapping the concepts to software • forming a ubiquitous language among the project members
  12. Domain-Driven Design Ubiquitous Language • important prerequisite for successful collaboration

    • use the same words for • discussion • modeling • development • documentation
  13. Domain-Driven Design Building Blocks • Entity: An object that is

    not defined by its attributes, but rather by a thread of continuity and its identity. • Value Object: An object that contains attributes but has no conceptual identity. They should be treated as immutable. • Aggregate: A collection of objects that are bound together by a root entity, otherwise known as an aggregate root. The aggregate root guarantees the consistency of changes being made within the aggregate by forbidding external objects from holding references to its members.
  14. Domain-Driven Design Building Blocks • Service: When an operation does

    not conceptually belong to any object. Following the natural contours of the problem, you can implement these operations in services. • Repository: methods for retrieving domain objects should delegate to a specialized Repository object such that alternative storage implementations may be easily interchanged.
  15. Object Management FLOW3's take on Dependency Injection • one of

    the first PHP implementations (started in 2006, improved ever since) • object management for the whole lifecycle of all objects • no unnecessary configuration if information can be gatered automatically (autowiring) • intuitive use and no bad magical surprises • fast! (like hardcoded or faster)
  16. namespace Acme\Demo\Controller; use TYPO3\FLOW3\Mvc\Controller\ActionController; use Acme\Demo\Service\GreeterService; class DemoController extends ActionController

    { /** * @var \Acme\Demo\Service\GreeterService */ protected $greeterService; /** * @param \Acme\Demo\Service\GreeterService */ public function __construct(GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return $this->greeterService->greet($name); } } Constructor Injection
  17. namespace Acme\Demo\Controller; use TYPO3\FLOW3\MVC\Controller\ActionController; use Acme\Demo\Service\GreeterService; class DemoController extends ActionController

    { /** * @var \Acme\Demo\Service\GreeterService */ protected $greeterService; /** * @param \Acme\Demo\Service\GreeterService */ public function injectGreeterService(GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return $this->greeterService->greet($name); } } Setter Injection
  18. namespace TYPO3\Demo\Controller; use TYPO3\FLOW3\Annotations as FLOW3; use TYPO3\FLOW3\MVC\Controller\ActionController; use Acme\Demo\Service\GreeterService;

    class DemoController extends ActionController { /** * @var \TYPO3\Demo\Service\GreeterService * @FLOW3\Inject */ protected $greeterService; /** * @param string $name */ public function helloAction($name) { return $this->greeterService->greet($name); } } Property Injection
  19. class Customer { /** * @FLOW3\Inject * @var \Acme\CustomerNumberGenerator */

    protected $customerNumberGenerator; ... } $customer = new Customer(); $customer->getCustomerNumber(); Object Management
  20. Object Management <?php declare(ENCODING = 'utf-8'); namespace TYPO3\Conference\Domain\Model\Conference; use TYPO3\FLOW3\Annotations

    as FLOW3; /** * Autogenerated Proxy Class * @FLOW3\Scope(“prototype”) * @FLOW3\Entity */ class Paper extends Paper_Original implements \TYPO3\FLOW3\Object \TYPO3\FLOW3\Persistence\Aspect\PersistenceMagicInterface { /** * @var string * @ORM\Id * @ORM\Column(length="40") * introduced by TYPO3\FLOW3\Persistence\Aspect\PersistenceMa */ protected $FLOW3_Persistence_Identifier = NULL; private $FLOW3_AOP_Proxy_targetMethodsAndGroupedAdvices = arra private $FLOW3_AOP_Proxy_groupedAdviceChains = array(); private $FLOW3_AOP_Proxy_methodIsInAdviceMode = array(); /** * Autogenerated Proxy Method */ public function __construct() FLOW3 creates proxy classes for realizing DI and AOP magic • new operator is supported • proxy classes are created on the fly • in production context all code is static
  21. Persistence Object Persistence in the Flow • based on Doctrine

    2 • seamless integration into FLOW3 • provides all the great Doctrine 2 features • uses UUIDs • low level persistence API • allows for own, custom persistence backends (instead of Doctrine 2) • e.g. CouchDB, Solr
  22. Basic Object Persistence // Create a new customer and persist

    it: $customer = new Customer("Robert"); $this->customerRepository->add($customer); // Find an existing customer: $otherCustomer = $this->customerRepository->findByFirstName("Karsten"); // and delete it: $this->customerRepository->remove($otherCustomer);
  23. Validation and Doctrine Annotations namespace TYPO3\Blog\Domain\Model; /** * A Blog

    object * * @Entity */ class Blog { /** * @var string * @validate Text, StringLength(minimum = 1, maximum = 80) * @Column(length="80") */ protected $title; /** * @var \Doctrine\Common\Collections\Collection<\TYPO3\Blog\Domain\Model\Post> * @OneToMany(mappedBy="blog") * @OrderBy({"date" = "DESC"}) */ protected $posts; ... }
  24. Persistence-related Annotations @Entity Declares a class as "entity" @Column Controls

    the database column related to the class property. Very useful for longer text content (type="text" !) @ManyToOne @OneToMany @ManyToMany @OneToOne Defines relations to other entities. Unlike with vanilla Doctrine targetEntity does not have to be given but will be reused from the @var annotation. cascade can be used to cascade operation to related objects.
  25. @var Defines the type of a property, collections can be

    typed using angle brackets: \Doctrine\Common\Collections\Collection<\TYPO3\Conference\Domain\Model\Comment> @transient The property will be ignored, it will neither be persisted nor reconstituted @identity Marks the property as part of an objects identity Persistence-related Annotations
  26. Custom Queries using the Query Object Model /** * A

    PostRepository */ class PostRepository extends \TYPO3\FLOW3\Persistence\Repository { /** * Finds posts by the specified tag and blog * * @param \TYPO3\Blog\Domain\Model\Tag $tag * @param \TYPO3\Blog\Domain\Model\Blog $blog The blog the post must refer to * @return \TYPO3\FLOW3\Persistence\QueryResultInterface The posts */ public function findByTagAndBlog(\TYPO3\Blog\Domain\Model\Tag $tag, \TYPO3\Blog\Domain\Model\Blog $blog) { $query = $this->createQuery(); return $query->matching( $query->logicalAnd( $query->equals('blog', $blog), $query->contains('tags', $tag) ) ) ->setOrderings(array( 'date' => \TYPO3\FLOW3\Persistence\QueryInterface::ORDER_DESCENDING) ) ->execute(); } }
  27. Schema Management Doctrine 2 Migrations • Migrations allow schema versioning

    and change deployment • Migrations are the recommended way for DB updates • Tools to create and deploy migrations are integrated with FLOW3
  28. Schema Management $ ./flow3 doctrine:create $ ./flow3 doctrine:update Manual database

    updates • for simple situations this can be good enough: • useful when • you are just starting a project and have never released
  29. Schema Management $ ./flow3 doctrine:migrationgenerate Generated new migration class to

    "…/Version20110608074324.php" from schema differences. $ Generating migrations • Generated migrations can contain errors and should be checked and adjusted as needed • Migrations need to be moved to their “owning” package manually
  30. Validation Validation is about various … • incoming data needs

    to be validated for security reasons • no evil markup in submitted content • domain model integrity needs to be ensured • an email needs to be (syntactically) valid • credit card numbers should consist only of digits
  31. Validation Validation in FLOW3 • you do not want to

    code checks into your controllers • FLOW3 separates validation from your controller’s concerns • no PHP code needed for validation • declared through annotations
  32. Validation Validation Models • BaseProperties rules defining the minimum requirements

    on individual properties of a model • BaseModel rules or custom validators enforcing the minimum requirements on the combination of properties of a model • Supplemental rules defining additional requirements on a model for a specific situation (e.g. a certain action method)
  33. Validation Base Properties • Validation rules defined directly at the

    properties /** * @var string * @validate StringLength(minimum = 10, maximum = 100) */ protected $title; /** * @var string * @validate StringLength(minimum = 1, maximum = 50) */ protected $author;
  34. Validation Validators • validators provided by FLOW3 can be used

    through their short name • Count, Float, NotEmpty, RegularExpression, Uuid, DateTime, NumberRange, StringLength, Alphanumeric, Integer, Number, String, EmailAddress, Label, Raw, Text • custom validators need to implement the ValidatorInterface • use them by specifying the fully qualified class name /** * @var \Dambekalns\Stuff\Domain\Model\Stuff * @validate \Dambekalns\Stuff\Domain\Validator\StuffValidator */ protected $stuff;
  35. Property Mapper Transfer properties from A to B • Allows

    for complete or partial copying of objects and object graphs • Is used by the MVC framework for the mapping of raw GET and POST data to Argument objects
  36. Property Mapper $articleArray = array( 'headline' => 'Hello World!', 'story'

    => 'Just a demo ...' ); $article = $mapper->convert($sourceArray, 'Acme.Demo\Domain\Model\Article');
  37. Resource Management <f:form method="blog" action="update" object="{blog}" name="blog" enctype="multipart/form-data"> <f:if condition="{blog.authorPicture}">

    <img src="{f:uri.resource(resource: blog.authorPicture)}" /> </f:if> <label for="authorPicture">Author picture</label> <f:form.upload property="authorPicture" id="authorPicture" /> <f:form.submit value="Update"/> </f:form> Image Upload Resources are handled like other properties in a form:
  38. Property Mapper /** * @return void */ public function initializeUpdateAction()

    { $this->arguments['article']->getPropertyMappingConfiguration() ->allowCreationForSubProperty('picture'); $this->arguments['article']->getPropertyMappingConfiguration() ->allowModificationForSubProperty('picture'); } Allow nested object structures For security reasons the creation of nested structure through the property mapper is disabled by default
  39. The Zen of Templating FLOW3 comes with an elegant, flexible

    and secure templating engine: Fluid • templates are valid HTML • templates contain no PHP code • object access, control structures, loops ... • designer-friendly • extensible (view helpers, widgets)
  40. Fluid Example for assigning a string to a Fluid variable:

    <!-- in the Fluid template: --> <head> <title>{title}</title> </head> // in the action controller: $this->view->assign('title', 'Welcome to Fluid');
  41. Fluid Variables can also be objects: <!-- in the Fluid

    template: --> <div class="venue"> <p>Venue Street: {conference.venue.street}</p> </div> // in the action controller: $this->view->assign('conference', $conference);
  42. Fluid if-then-else: <!-- in the Fluid template: --> <f:if condition="{post.comments}">

    <f:then>There are some comments.</f:then> <f:else>There are no comments.</f:else> </f:if> // in the action controller: $this->view->assign('post', $blogPost);
  43. Fluid for-each: <!-- in the Fluid template: --> <ul> <f:for

    each="{ages}" as="age" key="name"> <li>{name} is {age} year old.</li> </f:for> </ul> // in the action controller: $this->view->assign('ages', array("Karsten" => 34, "Robert" => 35));
  44. Fluid for-each: <!-- in the Fluid template: --> <f:if condition="{post.comments}">

    <ul> <f:for each="{post.comments}" as="comment" > <li>{post.title}</li> </f:for> </ul> </f:if> // in the action controller: $this->view->assign('post', $blogPost);
  45. Fluid View helpers – in this case the link.action view

    helper: <!-- in the Fluid template: --> {namespace f=TYPO3\Fluid\ViewHelpers} <f:link.action action="delete" arguments="{post: post, really: 'yes'}"> Delete this post </f:link.action>
  46. Security Touchless Security, Flow-Style • security is handled at a

    central place (through AOP) • third-party code is as secure as possible by default • modeled after our experiences in the TYPO3 project and Spring Security (Java framework) • provides authentication, authorization, validation, filtering ... • can intercept arbitrary method calls • transparently filters content through query-rewriting • extensible for new authentication or authorization mechanisms
  47. Security Cross-Site Request Forgery • enables an attacker to execute

    privileged operations without being authenticated • the risk lies in using malicious links or forms while still being authenticated • imagine a link coming in through an URL shortener...
  48. Security Avoiding Cross-Site Request Forgery • add a (truly!) random

    string token to each link or form • make sure this token is correct before executing anything • change the token as often as possible to make it impossible to send you a working malicious link while you’re logged in • in most cases, we can assume that it should be enough to generate one token when you log in – that’s the default
  49. Security CSRF Protection in FLOW3 • you must not forget

    to add that token to any link • FLOW3 automatically adds the CSRF token to each • link you generate • each form you create with Fluid • and checks it for every call to a protected action • the protection can be disabled using @skipCsrfProtection on an action
  50. AOP Aspect-Oriented Programming • programming paradigm • separates concerns to

    improve modularization • OOP modularizes concerns into objects • AOP modularizes cross-cutting concerns into aspects • FLOW3 makes it easy (and possible at all) to use AOP in PHP
  51. AOP FLOW3 uses AOP for ... • persistence magic •

    logging • debugging • security /** * @aspect * @introduce TYPO3\FLOW3\Persistence\Aspect\PersistenceMagicInterface, TYP */ class PersistenceMagicAspect { /** * @pointcut classTaggedWith(entity) || classTaggedWith(valueobject) */ public function isEntityOrValueObject() {} /** * @var string * @Id * @Column(length="40") * @introduce TYPO3\FLOW3\Persistence\Aspect\PersistenceMagicAspect->isE */ protected $FLOW3_Persistence_Identifier; /** * After returning advice, making sure we have an UUID for each and every * * @param \TYPO3\FLOW3\AOP\JoinPointInterface $joinPoint The current join * @return void * @before classTaggedWith(entity) && method(.*->__construct()) */ public function generateUUID(\TYPO3\FLOW3\AOP\JoinPointInterface $joinPoin $proxy = $joinPoint->getProxy(); \TYPO3\FLOW3\Reflection\ObjectAccess::setProperty($proxy, 'FLOW3_Persi }
  52. Signal-Slot Event Handling Signal • can be fired on any

    event • can be freely defined by the developer Slot • is invoked when a signal is emitted • any method can be used as a slot any signal can be wired to any slot
  53. Signal-Slot Event Handling /** * @param \TYPO3\Blog\Domain\Model\Post $post * @param

    \TYPO3\Blog\Domain\Model\Comment $newComment * @return void */ public function createAction(\TYPO3\Blog\Domain\Model\Post $post, \TYPO3\Blog\Domain\Model\Comment $newComment) { $post->addComment($newComment); $this->emitCommentCreated($newComment, $post); … } /** * @param \TYPO3\Blog\Domain\Model\Comment $comment * @param \TYPO3\Blog\Domain\Model\Post $post * @return void * @signal */ protected function emitCommentCreated(\TYPO3\Blog\Domain\Model\Comment $comment, \TYPO3\Blog\Domain\Model\Post $post) {}
  54. Signal-Slot Event Handling /** * Invokes custom PHP code directly

    after the package manager has been * initialized. * * @param \TYPO3\FLOW3\Core\Bootstrap $bootstrap The current bootstrap * @return void */ public function boot(\TYPO3\FLOW3\Core\Bootstrap $bootstrap) { $dispatcher = $bootstrap->getSignalSlotDispatcher(); $dispatcher->connect( 'TYPO3\Blog\Controller\CommentController', 'commentCreated', 'TYPO3\Blog\Service\Notification', 'sendNewCommentNotification' ); } Signals are wired to Slots in a package’s bootstrap:
  55. Signal-Slot Event Handling /** * @param \TYPO3\Blog\Domain\Model\Comment $comment * @param

    \TYPO3\Blog\Domain\Model\Post $post * @return void */ public function sendNewCommentNotification(\TYPO3\Blog\Domain\Model\Comment $comment, \TYPO3\Blog\Domain\Model\Post $post) { $mail = new \TYPO3\SwiftMailer\Message(); $mail ->setFrom(array('[email protected] ' => 'John Doe')) ->setTo(array('[email protected] ' => 'Karsten Dambekalns')) ->setSubject('New comment on blog post "' . $post->getTitle() . '"') ->setBody($comment->getContent()) ->send(); } Any method can be a slot:
  56. Social Media Suite •central hub for social media activities for

    potentially thousands of travel agencies •advanced form engine •various detail improvements •uses an early version of TYPO3 Phoenix by AKOM360, Munich
  57. “Our senior developers are extremely happy with FLOW3 – it

    is definitely the most capable PHP framework we have come across so far.” Fabian Pfütze Project Lead
  58. ?