fluent development with flow3 1.0

Post on 06-May-2015

3.293 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

FLOW3 is an application framework which will change the way you code PHP. It aims to back up developers with security and infrastructure while they focus on the application logic. FLOW3 is one of the first application frameworks to choose Domain-Driven Design as its major underlying concept. This approach makes FLOW3 easy to learn and at the same time clean and flexible for even complex projects. Built with PHP 5.3 in mind from the beginning, it features namespaces and has an emphasis on clean, object-oriented code.Thanks to its Doctrine 2 integration, FLOW3 gives you access to a wide range of databases while letting you forget the fact that you're using a database at all (think objects, not tables). FLOW3's unique way of supporting Dependency Injection (no configuration necessary) lets you truly enjoy creating a stable and easy-to-test application architecture. 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 provides a comprehensive overview of the main features of FLOW3 and how you can get started with your first app.

TRANSCRIPT

San Francisco, USA

Fluent Development with FLOW3

Karsten Dambekalns & Robert Lemke

San Francisco, USA

co-lead of TYPO3 5.0 and FLOW3

34 years old

lives in Lübeck, Germany

1 wife, 3 sons, 1 espresso machine

likes canoeing

Karsten Dambekalns

San Francisco, USA

chief "architect" of TYPO3 5.0 and FLOW3

co-founder of the TYPO3 Association

35 years old

lives in Lübeck, Germany

1 wife, 1 daughter, 1 espresso machine

likes drumming

Robert Lemke

San Francisco, USA

At a Glance

FLOW3 is a web application framework

• brings PHP development to a new level

• made for PHP 5.3, full namespaces support

• modular, extensible, package based

• free & Open Source (LGPL v3)

• backed by one of the largest Open Source projects

with 6000+ contributors

San Francisco, USA

Foundation for the Next Generation

TYPO3 5.0 is the all-new Enterprise CMS

• content repository, workspaces, versions, i18n, ExtJS based UI ...

• powered by FLOW3

• compatible code base

• use TYPO3 features in FLOW3 standalone apps as you like

San Francisco, USA

Work in Progress

WARNING

The current documentation of FLOW3 does not cover the version in our Git master – a lot has changed since the last alpha release!

We are currently updating the manuals and tutorials for the 1.0 beta release though.

San Francisco, USA

Check Out from Git – Stable State

$ 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.

San Francisco, USA

Set File Permissions

$ ./Packages/Framework/FLOW3/Scripts/setfilepermissions.sh robert _www _wwwFLOW3 File Permission Script

Checking permissions from here upwards ... (if a password prompt appears it's from sudo)Password:

Making sure Data and Web/_Resources exist ...Setting file permissions, this might take a minute ...

$

San Francisco, USA

Set Up Database

Configuration/Settings.yaml

# ## Global Settings ## #

FLOW3: persistence: backendOptions: driver: 'pdo_mysql' dbname: 'blog' user: 'bloguser' password: 'blogpassword' host: '127.0.0.1' path: '127.0.0.1' port: 3306 doctrine: dbal: sessionInitialization: 'SET NAMES utf8 COLLATE utf8_unicode_ci'

San Francisco, USA

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>

San Francisco, USA

Final Check

San Francisco, USA

Update from Git to Latest State

$ git submodule foreach "git checkout master"

-✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-

$ git submodule foreach "git pull --rebase"

Entering 'Build/Common'First, rewinding head to replay your work on top of it...Fast-forwarded master to 6f27f1784240b414e966ce0e5a12e23cb2f7ab02.Entering 'Packages/Application/TYPO3'First, rewinding head to replay your work on top of it...Fast-forwarded master to 5187430ee44d579ae2bac825e2a069c4cd3f38a4.Entering 'Packages/Application/TYPO3CR'First, rewinding head to replay your work on top of it...Fast-forwarded master to b1f5331aa51d390fa3d973404f31b9fd773f7059.Entering 'Packages/Application/Twitter'Current branch master is up to date.…

San Francisco, USA

Hello World!

Package.php

<?phpnamespace F3\Demo;

use \F3\FLOW3\Package\Package as BasePackage;

class Package extends BasePackage {}

$ ./flow3_dev flow3:package:create --package-key Demo

$ ./flow3_dev flow3:package:create Demo

Soon:

Today:

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 11

Live Hacking

San Francisco, USA

Hello World!

<?phpnamespace F3\Demo\Controller;

use \F3\FLOW3\MVC\Controller\ActionController;

class StandardController extends ActionController { /** * @param string $name * @return string */ public function indexAction($name) { return "Hello $name!"; }}

?>

StandardController.php

San Francisco, USA

Hello World!

http://dev.flow3.rob/demo/standard/index?name=Robert

Hello Robert!

San Francisco, USA

Tackling the Heart of Software Development

Domain-Driven DesignA 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

/** * Paper submitted by a speaker * * @scope prototype * @entity */class Paper {

/** * @var Participant */ protected $author;

/** * @var string */ protected $title;

/** * @var string */ protected $shortAbstract;

/** * @var string */ protected $abstract;

/** * @var \SplObjectStorage */ protected $materials;

/** * @var \F3\Conference\Domain\Model\SessionType * @validate NotEmpty */ protected $proposedSessionType;

/** * Constructs a new Paper * * @author Robert Lemke <robert@typo3.org> */ public function __construct() { $this->materials = new \SplObjectStorage; }

/** * Sets the author of this paper * * @param \F3\Conference\Domain\Model\Participant $author * @return void * @author Robert Lemke <robert@typo3.org> */ public function setAuthor(\F3\Conference\Domain\Model\Participant $author) { $this->author = $author; }

/** * Getter for the author of this paper * * @return \F3\Conference\Domain\Model\Participant * @author Robert Lemke <robert@typo3.org> */ public function getAuthor() { return $this->author; }

/** * Setter for title * * @param string $title The title of this paper * @return void * @author Robert Lemke <robert@typo3.org> */ public function setTitle($title) { $this->title = $title; }

/** * Getter for title * * @return string The title of this paper * @author Robert Lemke <robert@typo3.org> */ public function getTitle() { return $this->title; }

/** * Setter for the short abstract * * @param string $shortAbstract The short abstract for this paper * @return void * @author Robert Lemke <robert@typo3.org> */ public function setShortAbstract($shortAbstract) { $this->shortAbstract = $shortAbstract; }

/** * Getter for the short abstract * * @return string The short abstract * @author Robert Lemke <robert@typo3.org> */ public function getShortAbstract() { return $this->shortAbstract; }

/** * Setter for abstract * * @param string $abstract The abstract of this paper * @return void * @author Robert Lemke <robert@typo3.org> */ public function setAbstract($abstract) { $this->abstract = $abstract; }

/** * Getter for abstract * * @return string The abstract * @author Robert Lemke <robert@typo3.org> */ public function getAbstract() { return $this->abstract; }

/** * Returns the materials attached to this paper * * @return \SplObjectStorage The materials * @author Robert Lemke <robert@typo3.org> */ public function getMaterials() { return $this->materials; }

/** * Setter for the proposed session type * * @param \F3\Conference\Domain\Model\SessionType $proposedSessionType The proposed session type * @return void * @author Robert Lemke <robert@typo3.org> */ public function setProposedSessionType(\F3\Conference\Domain\Model\SessionType $proposedSessionType) { $this->proposedSessionType = $proposedSessionType; }

/** * Getter for the proposed session type * * @return \F3\Conference\Domain\Model\SessionType The proposed session type * @author Robert Lemke <robert@typo3.org> */ public function getProposedSessionType() { return $this->proposedSessionType; }}?>

San Francisco, USA

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

San Francisco, USA

Domain-Driven Design

Ubiquitous Language

• important prerequisite for successful collaboration

• use the same words for

• discussion

• modeling

• development

• documentation

San Francisco, USA

Domain: Conference

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 12

Extreme Modeling

San Francisco, USA

Example Model: Paper

/** * A Paper * * @scope prototype * @entity */class Paper {

/** * @var \F3\Conference\Domain\Model\Account\Participant * @ManyToOne(cascade={"persist"}) * @validate NotEmpty */ protected $author;

/** * @var string * @validate StringLength(minimum = 1, maximum = 50) */ protected $title;

San Francisco, USA

Example Model: Paper

/** * @var string * @Column(type="text") * @validate StringLength(minimum = 30, maximum = 150) */ protected $shortAbstract;

/** * @var string * @Column(type="text") * @validate StringLength(minimum = 100, maximum = 1000) */ protected $abstract;

/** * @var \F3\Conference\Domain\Model\Conference\SessionType * @ManyToOne(cascade={"persist"}) * @validate NotEmpty */ protected $proposedSessionType;

San Francisco, USA

Example Model: Paper

/** * @var \F3\Conference\Domain\Model\Conference\Track * @ManyToOne(cascade={"persist"}) * @validate NotEmpty */ protected $proposedTrack;

/** * @var \Doctrine\Common\Collections\ArrayCollection<\F3\Conference\Domain\Model\Comment> * @ManyToMany(cascade={"persist", "remove"}) */ protected $comments;

/** * @var string */ protected $slideShareUrl;

/** * @var string */ protected $tags;

San Francisco, USA

Example Model: Paper

/** * @var string * @Column(type="text", nullable=true) */ protected $links;

/** * @var \Doctrine\Common\Collections\ArrayCollection<\F3\Conference\Domain\Model\Account\Participant> * @ManyToMany(cascade={"all"}) */ protected $speakers;

/** * one of the STATUS_* constants * @var string */ protected $status = self::STATUS_DRAFT;

San Francisco, USA

Example Model: Paper

/** * STATUS_* constants */ const STATUS_DRAFT = 'draft'; const STATUS_SUBMITTED = 'submitted'; const STATUS_REJECTED = 'rejected'; const STATUS_ACCEPTED = 'accepted'; const STATUS_SCHEDULED = 'scheduled';

public function __construct() { $this->comments = new \Doctrine\Common\Collections\ArrayCollection(); $this->speakers = new \Doctrine\Common\Collections\ArrayCollection(); }

/** * @param \F3\Conference\Domain\Model\Account\Participant $author * @return void */ public function setAuthor(\F3\Conference\Domain\Model\Account\Participant $author) { $this->author = $author; }

/** * @return \F3\Conference\Domain\Model\Account\Participant */ public function getAuthor() {

San Francisco, USA

Domain-Driven Design

San Francisco, USA

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)

• CouchDB is supported natively

San Francisco, USA

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);

San Francisco, USA

Advanced Queries

/** * Finds most recent posts excluding the given post * * @param \F3\Blog\Domain\Model\Post $post Post to exclude from result * @param integer $limit The number of posts to return at max * @return array All posts of the $post's blog except for $post */ public function findRecentExceptThis(\F3\Blog\Domain\Model\Post $post, $limit = 20) { $query = $this->createQuery(); $posts = $query->matching($query->equals('blog', $post->getBlog())) ->setOrderings(array('date' => \F3\FLOW3\Persistence\QueryInterface::ORDER_DESCENDING)) ->setLimit($limit) ->execute() ->toArray(); unset($posts[array_search($post, $posts)]); return $posts;

// this is an alternative way of doing this when extending the Doctrine 2 // specific repository and using DQL. return $this->entityManager ->createQuery('SELECT p FROM \F3\Blog\Domain\Model\Post p WHERE p.blog = :blog' .

'AND NOT p = :excludedPost ORDER BY p.date DESC') ->setMaxResults($limit) ->execute(array('blog' => $post->getBlog(), 'excludedPost' => $post)); }

PostRepository.php

San Francisco, USA

Purely Doctrine 2

<?phpnamespace My\Example;

/** * @Entity(repositoryClass="BugRepository") */class Bug {

/** * @var integer * @Id * @Column(type="integer") * @GeneratedValue */ public $id;

/** * @var string * @Column(type="string") */ public $description;

/** * @var \DateTime * @Column(type="datetime") */ public $created;

/** * @var User * @ManyToOne(targetEntity="User", inversedBy="assignedBugs") */ private $engineer;

/** * @var \Doctrine\Common\Collections\ArrayCollection<Product> * @ManyToMany(targetEntity="Product") */ private $products;}

?>

San Francisco, USA

Doctrine 2 in FLOW3

<?phpnamespace My\Example;

/** * @Entity(repositoryClass="BugRepository") */class Bug {

/** * @var integer * @Id * @Column(type="integer") * @GeneratedValue */ public $id;

/** * @var string * @Column(type="string") */ public $description;

/** * @var \DateTime * @Column(type="datetime") */ public $created;

/** * @var User * @ManyToOne(targetEntity="User", inversedBy="assignedBugs") */ private $engineer;

/** * @var \Doctrine\Common\Collections\ArrayCollection<Product> * @ManyToMany(targetEntity="Product") */ private $products;}

?>

San Francisco, USA

Purely Doctrine 2

/** * @var \DateTime * @Column(type="datetime") */ public $created;

/** * @var User * @ManyToOne(targetEntity="User", inversedBy="assignedBugs") */ private $engineer;

/** * @var \Doctrine\Common\Collections\ArrayCollection<Product> * @ManyToMany(targetEntity="Product") */ private $products;}

?>

San Francisco, USA

Doctrine 2 in FLOW3

/** * @var \DateTime * @Column(type="datetime") */ public $created;

/** * @var User * @ManyToOne(targetEntity="User", inversedBy="assignedBugs") */ private $engineer;

/** * @var \Doctrine\Common\Collections\ArrayCollection<Product> * @ManyToMany(targetEntity="Product") */ private $products;}

?>

San Francisco, USA

Object Management

Dependency Injection

• a class doesn't create or retrieve the instance of another class but get's it injected

• fosters loosely-coupling and high cohesion

‣ more stable, reusable code

San Francisco, USA

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 gathered automatically (autowiring)

• intuitive use and no bad magical surprises

• fast! (like hardcoded or faster)

San Francisco, USA

<?phpnamespace Acme\DemoBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\RedirectResponse;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;use Acme\DemoBundle\GreeterService;

class DemoController extends Controller { /** * @var \Acme\DemoBundle\GreeterService */ protected $greeterService;

/** * @param \Acme\DemoBundle\GreeterService */ public function __construct($greeterService = NULL) { $this->greeterService = $greeterService; } /** * @Route("/hello/{name}", name="_demo_hello") */ public function helloAction($name) { return new Response('Hello ' . $name, 200, array('Content-Type' => 'text/plain')); }}

Constructor Injection: Symfony 2Warning: might contain errors

(I'm no Symfony expert ...)

San Francisco, USA

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services> <service id="acme.demo.greeterservice" class="Acme\DemoBundle\GreeterService" public="false" /> <service id="acme.demo.democontroller" class="Acme\DemoBundle\Controller\DemoController"> <argument type="service" id="acme.demo.greeterservice" /> </service> </services></container>

Constructor Injection: Symfony 2Warning: might contain errors

(I'm no Symfony expert ...)

San Francisco, USA

<?php namespace F3\Demo\Controller;

use F3\FLOW3\MVC\Controller\ActionController;use F3\Demo\Service\GreeterService;

class DemoController extends ActionController { /** * @var \F3\Demo\Service\GreeterService */ protected $greeterService;

/** * @param \F3\Demo\Service\GreeterService */ public function __construct(\F3\Demo\Service\GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return 'Hello ' . $name; }}

Constructor Injection

San Francisco, USA

Constructor Injection

San Francisco, USA

<?php namespace F3\Demo\Controller;

use F3\FLOW3\MVC\Controller\ActionController;use F3\Demo\Service\GreeterService;

class DemoController extends ActionController { /** * @var \F3\Demo\Service\GreeterService */ protected $greeterService;

/** * @param \F3\Demo\Service\GreeterService */ public function injectGreeterService(\F3\Demo\Service\GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return 'Hello ' . $name; }}

Setter Injection

San Francisco, USA

<?php namespace F3\Demo\Controller;

use F3\FLOW3\MVC\Controller\ActionController;use F3\Demo\Service\GreeterService;

class DemoController extends ActionController { /** * @var \F3\Demo\Service\GreeterService * @inject */ protected $greeterService; /** * @param string $name */ public function helloAction($name) { return 'Hello ' . $name; }}

Property Injection

San Francisco, USA

F3\FLOW3\Security\Cryptography\RsaWalletServiceInterface: className: F3\FLOW3\Security\Cryptography\RsaWalletServicePhp scope: singleton properties: keystoreCache: object: factoryObjectName: F3\FLOW3\Cache\CacheManager factoryMethodName: getCache arguments: 1: value: FLOW3_Security_Cryptography_RSAWallet

Objects.yaml

San Francisco, USA

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)

San Francisco, USA

class Customer {

/** * @inject * @var CustomerNumberGenerator */ protected $customerNumberGenerator;

...}

$customer = new Customer();$customer->getCustomerNumber();

Object Management

San Francisco, USA

Object Management

<?phpdeclare(ENCODING = 'utf-8');namespace F3\Conference\Domain\Model\Conference;/** * Autogenerated Proxy Class * @scope prototype * @entity */class Paper extends Paper_Original implements \F3\FLOW3\Object\Proxy\ProxyInterface, \F3\FLOW3\Persistence\Aspect\PersistenceMagicInterface { /** * @var string * @Id * @Column(length="40") * introduced by F3\FLOW3\Persistence\Aspect\PersistenceMagicAspect */ protected $FLOW3_Persistence_Identifier = NULL; private $FLOW3_AOP_Proxy_targetMethodsAndGroupedAdvices = array(); private $FLOW3_AOP_Proxy_groupedAdviceChains = array(); private $FLOW3_AOP_Proxy_methodIsInAdviceMode = array();

/** * Autogenerated Proxy Method */ public function __construct() { $this->FLOW3_AOP_Proxy_buildMethodsAndAdvicesArray(); if (isset($this->FLOW3_AOP_Proxy_methodIsInAdviceMode['__construct'])) { parent::__construct(); } else {

FLOW3 creates proxy classesfor realizing DI and AOP magic

• new operator is supported

• proxy classes are created on the fly

• in production context all code is static

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 13

Birth of a Blog

San Francisco, USA

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)

San Francisco, USA

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');

San Francisco, USA

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);

San Francisco, USA

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);

San Francisco, USA

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));

San Francisco, USA

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);

San Francisco, USA

Fluid

View helpers – in this case the link.action view helper:

<!-- in the Fluid template: --> {namespace f=F3\Fluid\ViewHelpers}

<f:link.action action="delete" arguments="{post: post, really: 'yes'}"> Delete this post </f:link.action>

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 18

Fluent Fluid

San Francisco, USA

Forms

<?phpnamespace F3\Blog\Domain\Model;

/** * A blog post * * @scope prototype * @entity */class Post {

/** * @var string * @validate StringLength(minimum = 1, maximum = 100) */ protected $title;

/** * @var string * @validate StringLength(minimum = 1, maximum = 50) */ protected $author;

/** * @var string * @validate Html */ protected $content;

/** * @var F3\Blog\Domain\Model\Image */ protected $image;

San Francisco, USA

Forms

<h2>Create a new post</h2>

<f:form action="create" object="{newPost}" name="newPost" enctype="multipart/form-data"> <label for="title">Title</label><br /> <f:form.textbox property="title" id="title" /><br />

<label for="content">Content</label><br /> <f:form.textarea property="content" rows="5" cols="40" id="content" /><br />

<label for="image">Image resource</label><br /> <f:form.textbox property="image.title" value="My image title" /> <f:form.upload property="image.originalResource" /></f:form>

San Francisco, USA

Forms

<?phpnamespace F3\Blog\Controller;use \F3\FLOW3\MVC\Controller\ActionController;

class PostController extends ActionController {

/** * @inject * @var \F3\Blog\Domain\Repository\PostRepository */ protected $postRepository;

/** * Creates a new post * * @param \F3\Blog\Domain\Model\Post $newPostadded to the repository * @return void */ public function createAction(\F3\Blog\Domain\Model\Post $newPost) { $this->blog->addPost($newPost); $this->flashMessageContainer->add('Your new post was created.'); $this->redirect('index'); }

}?>

San Francisco, USA

Fluent Development with FLOW3 (PART TWO)

Karsten Dambekalns & Robert Lemke

San Francisco, USA

Validation

Validation is about different things

• 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

San Francisco, USA

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

San Francisco, USA

Validation

Validation Models

• BasePropertiesrules defining the minimum requirements on individual properties of a model

• BaseModelrules or custom validators enforcing the minimum requirements on the combination of properties of a model

• Supplementalrules defining additional requirements on a model for a specific situation (e.g. a certain action method)

San Francisco, USA

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;

San Francisco, USA

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;

San Francisco, USA

Schema Management

Automatic database updates

• when first running FLOW3 the schema will be created

• when a model has changed, the schema is updated

• be careful with existing data, updates can be destructive

• for production you should manually manage schema changes

San Francisco, USA

Schema Management

Manual database updates

• for simple situations this can be good enough:

• useful when

• you need to use an existing database dump

• using SQLite, due to limited schema change functionality

$ ./flow3_dev flow3:doctrine:create

$ ./flow3_dev flow3:doctrine:update

San Francisco, USA

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

San Francisco, USA

Schema Management

Migrations Workflow

• use schema auto update in early development until your model is ready for a first “freeze”, then switch off schema auto update and drop your tables

• create migration diff and customize it

• migrate to create the tables

$ ./flow3_dev flow3:doctrine:migrationdiffGenerated new migration class to "…/Version20110608074324.php" from schema differences.$ vi …/Version20110608074324.php

$ ./flow3_dev flow3:doctrine:migrate

San Francisco, USA

Schema Management

Migrations Workflow

$ ./flow3_dev flow3:doctrine:migrationstatus

== Configuration >> Name: Doctrine Database Migrations >> Database Driver: pdo_mysql >> Database Name: blog >> Configuration Source: manually configured >> Version Table Name: flow3_doctrine_migrationstatus >> Migrations Namespace: F3\FLOW3\Persistence\Doctrine\Migrations >> Migrations Directory: /Users/karsten/Sites/blog/Configuration/Doctrine/Migrations >> Current Version: 2011-06-08 07:43:24 (20110608074324) >> Latest Version: 2011-06-08 07:43:24 (20110608074324) >> Executed Migrations: 1 >> Available Migrations: 1 >> New Migrations: 0

== Migration Versions >> 2011-06-08 07:43:24 (20110608074324) migrated

San Francisco, USA

Schema Management

Migrations Workflow

• rinse and repeat: from now on create a new migration whenever you changed your model classes

• generated migrations most probably need to be adjusted

• e.g. renaming a model means renaming a table, not dropping and creating

• data migration needs to be added

• remember: good migrations make your user’s day

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 18

Schematic Demo

San Francisco, USA

Command Line Support

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 18

Shebang!

San Francisco, USA

Command Line Support

<?phpnamespace F3\FLOW3\Command;

/** * Package command controller to handle packages from CLI (create/activate/deactivate packages) * * @scope singleton */class PackageCommandController extends \F3\FLOW3\MVC\Controller\CommandController {

/** * @inject * @var F3\FLOW3\Package\PackageManagerInterface */ protected $packageManager;

/** * Creates a new package * * Creates a new package with the given package key. The package key * should be the vendor namespace segments. * * @param string $packageKey The package key of the package to create * @return string */ public function createCommand($packageKey) { if (!$this->packageManager->isPackageKeyValid($packageKey)) { $this->response->appendContent('The package key "' . $packageKey . '" is not valid.');

San Francisco, USA

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

San Francisco, USA

Security Policy

San Francisco, USA

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...

San Francisco, USA

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

San Francisco, USA

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

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 18

Users and Login

San Francisco, USA

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

San Francisco, USA

AOP

FLOW3 uses AOP for ...

• persistence magic

• logging

• debugging

• security

/** * @aspect * @introduce F3\FLOW3\Persistence\Aspect\PersistenceMagicInterface, F3\FLOW3\Persistence\Aspect\

*/class PersistenceMagicAspect { /** * @pointcut classTaggedWith(entity) || classTaggedWith(valueobject) */ public function isEntityOrValueObject() {} /** * @var string * @Id * @Column(length="40") * @introduce F3\FLOW3\Persistence\Aspect\PersistenceMagicAspect->isEntityOrValueObject && filter

*/ protected $FLOW3_Persistence_Identifier; /** * After returning advice, making sure we have an UUID for each and every entity.

* * @param \F3\FLOW3\AOP\JoinPointInterface $joinPoint The current join point

* @return void * @before classTaggedWith(entity) && method(.*->__construct()) */ public function generateUUID(\F3\FLOW3\AOP\JoinPointInterface $joinPoint) {

$proxy = $joinPoint->getProxy(); \F3\FLOW3\Reflection\ObjectAccess::setProperty($proxy, 'FLOW3_Persistence_Identifier',

}

K. Dambekalns & R. LemkeD.P. Fluxtr

time();

5 1 18

The Wizard of AOP

San Francisco, USA

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

San Francisco, USA

Signal-Slot Event Handling

/** * @param \F3\Blog\Domain\Model\Post $post * @param \F3\Blog\Domain\Model\Comment $newComment * @return void */ public function createAction(\F3\Blog\Domain\Model\Post $post, \F3\Blog\Domain\Model\Comment $newComment) { $post->addComment($newComment); $this->emitCommentCreated($newComment, $post); … }

/** * @param \F3\Blog\Domain\Model\Comment $comment * @param \F3\Blog\Domain\Model\Post $post * @return void * @signal */ protected function emitCommentCreated(\F3\Blog\Domain\Model\Comment $comment, \F3\Blog\Domain\Model\Post $post) {}

San Francisco, USA

Signal-Slot Event Handling

/** * Invokes custom PHP code directly after the package manager has been * initialized. * * @param \F3\FLOW3\Core\Bootstrap $bootstrap The current bootstrap * @return void */ public function boot(\F3\FLOW3\Core\Bootstrap $bootstrap) { $dispatcher = $bootstrap->getSignalSlotDispatcher(); $dispatcher->connect( 'F3\Blog\Controller\CommentController', 'commentCreated', 'F3\Blog\Service\Notification', 'sendNewCommentNotification' ); }

Signals are wired to Slots in a package’s bootstrap:

San Francisco, USA

Signal-Slot Event Handling

/** * @param \F3\Blog\Domain\Model\Comment $comment * @param \F3\Blog\Domain\Model\Post $post * @return void */ public function sendNewCommentNotification(\F3\Blog\Domain\Model\Comment $comment, \F3\Blog\Domain\Model\Post $post) { $mail = new \F3\SwiftMailer\Message(); $mail ->setFrom(array('john@doe.org ' => 'John Doe')) ->setTo(array('karsten@typo3.org ' => 'Karsten Dambekalns')) ->setSubject('New comment on blog post "' . $post->getTitle() . '"') ->setBody($comment->getContent()) ->send(); }

Any method can be a slot:

San Francisco, USA

Speed and Performance

For the snappy user experience:

• multi-layered, tagged caches

• various cache backends (file, Memcached, APC, Redis, PDO, ...)

• reverse-proxy support (Varnish, ESI) in the works

• code compilation

• regular benchmarks

• focus on good scalability

San Francisco, USA

More Features

• Resource Management (CDNs, private resources, ...)

• Logging

• File Monitoring

• Configuration Management

• Routing

• REST / SOAP

• ...

San Francisco, USA

Roadmap

http://forge.typo3.org/projects/flow3-distribution-base/roadmap

San Francisco, USA

Discover the source code of the conference app

git://git.typo3.org/TYPO3v5/Distributions/Conference.git

San Francisco, USA

Try out the blog app

git://git.typo3.org/FLOW3/Applications/Blog.git

San Francisco, USA

Thank You!

• These slides: http://slideshare.net/robertlemke

• Download FLOW3: http://flow3.typo3.org

• Follow us on Twitter: @t3rob (Robert) @k_fish (Karsten)

• Give us feedback:

• robert@typo3.org / karsten@typo3.org

• http://joind.in/3535

top related