Download - FLOW3 Tutorial - T3CON11 Frankfurt
/** * Conference controller for the Acme.Demo package * * @scope singleton */class ConferenceController extends ActionController {
/** * @inject * @var \Acme\Demo\Domain\Repository\ConferenceRepository */ protected $conferenceRepository;
/** * Shows a list of conferences * * @return void */ public function indexAction() { $this->view->assign('conferences', $this->conferenceRepository->findAll()); }
/** * Shows a single conference object * * @param \Acme\Demo\Domain\Model\Conference $conference The conference to show * @return void */ public function showAction(Conference $conference) { $this->view->assign('conference', $conference); }
/** * Shows a form for creating a new conference object * * @return void */ public function newAction() { }
/** * Adds the given new conference object to the conference repository * * @param \Acme\Demo\Domain\Model\Conference $conference A new conference to add * @return void */ public function createAction(Conference $newConference) { $this->conferenceRepository->add($newConference); $this->flashMessageContainer->add('Created a new conference.'); $this->redirect('index'); }
/** * Shows a form for editing an existing conference object * * @param \Acme\Demo\Domain\Model\Conference $conference The conference to edit * @return void */ public function editAction(Conference $conference) { $this->view->assign('conference', $conference); }
FLOW3 Tutorial
Bastian Waidelich & Robert Lemke
Hanau, Germany
chief "architect" of TYPO3 5.0 and FLOW3
co-founder of the TYPO3 Association
35 years old
lives in Lübeck, Germany
1 wife, 2 daughters, 1 espresso machine
likes drumming
Robert Lemke
Hanau, Germany
FLOW3 core team member since 2008
co-creator of Fluid
30 years old
lives in Cologne, Germany
0 wifes, ? daughters, 1 cafetera
likes climbing & guitar playing
Bastian Waidelich
Hanau, Germany
This Workshop
Morning
• Installation
• Kickstart & Hello World!
• Commands
• Depencency Injection
• Persistence, Doctrine and Domain-Driven Design
• Modelling of an example App
• Kickstarting the example App
Hanau, Germany
This Workshop
Afternoon
• Routing
• Validation
• Property Mapper
• Migrations
• Security / Login
• A Glimpse on TYPO3 Phoenix
Hanau, Germany
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
Hanau, Germany
Foundation for the Next Generation CMS
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
Hanau, Germany
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.
Hanau, Germany
Set File Permissions
$ sudo ./flow3 core:setfilepermissions robert _www _wwwFLOW3 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:
Hanau, Germany
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'
Hanau, Germany
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>
Hanau, Germany
Final Check
Hanau, Germany
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 5187430ee44d579ae2bac825e2a069c4cd3Acme8a4.Entering 'Packages/Application/TYPO3CR'First, rewinding head to replay your work on top of it...Fast-forwarded master to b1f5331aa51d390fa3d973404Acme1b9fd773f7059.Entering 'Packages/Application/Twitter'Current branch master is up to date.…
Hanau, Germany
Command Line Use
$ ./flow3 helpFLOW3 1.0.0 ("Development" context)usage: ./flow3 <command identifier>
The following commands are currently available:
PACKAGE "TYPO3.FLOW3":-------------------------------------------------------------------------------* flow3:cache:flush Flush all caches cache:warmup Warm up caches
* flow3:core:setfilepermissions Adjust file permissions for CLI and web server access* flow3:core:shell Run the interactive Shell
doctrine:validate Validate the class/table mappings doctrine:create Create the database schema doctrine:update Update the database schema doctrine:entitystatus Show the current status of entities and mappings doctrine:dql Run arbitrary DQL and display results doctrine:migrationstatus Show the current migration status doctrine:migrate Migrate the database schema doctrine:migrationexecute Execute a single migration doctrine:migrationversion Mark/unmark a migration as migrated doctrine:migrationgenerate Generate a new migration
help Display help for a command
package:create Create a new package package:delete Delete an existing package package:activate Activate an available package package:deactivate Deactivate a package package:list List available packages package:import Import a package from a remote location
routing:list List the known routes
security:importpublickey Import a public key security:importprivatekey Import a private key
PACKAGE "TYPO3.KICKSTART":------------------------------------------------------------------------------- kickstart:package Kickstart a new package kickstart:actioncontroller Kickstart a new action controller kickstart:commandcontroller Kickstart a new command controller kickstart:model Kickstart a new domain model kickstart:repository Kickstart a new domain repository
* = compile time command
See './flow3 help <commandidentifier>' for more information about a specific command.
Hanau, Germany
Command Line Use
$ ./flow3 help kickstart:package
Kickstart a new package
COMMAND: typo3.kickstart:kickstart:package
USAGE: ./flow3 kickstart:package <package key>
ARGUMENTS: --package-key The package key, for example "MyCompany.MyPackageName"
DESCRIPTION: Creates a new package and creates a standard Action Controller and a sample template for its Index Action. For creating a new package without sample code use the package:create command.
SEE ALSO: typo3.flow3:package:create (Create a new package)
Hanau, Germany
Hello World!
$ ./flow3 kickstart:package Acme.Demo
Robert LemkeD.P. Fluxtr
time();
5 1 11
Hello World!
Hanau, Germany
Hello World!
<?phpnamespace Acme\Demo\Controller;
use \TYPO3\FLOW3\MVC\Controller\ActionController;
class StandardController extends ActionController { /** * @param string $name * @return string */ public function indexAction($name) { return "Hello $name!"; }}
?>
StandardController.php
Hanau, Germany
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 \Acme\Conference\Domain\Model\SessionType * @validate NotEmpty */ protected $proposedSessionType;
/** * Constructs a new Paper * * @author Robert Lemke <[email protected]> */ public function __construct() { $this->materials = new \SplObjectStorage; }
/** * Sets the author of this paper * * @param \Acme\Conference\Domain\Model\Participant $author * @return void * @author Robert Lemke <[email protected]> */ public function setAuthor(\Acme\Conference\Domain\Model\Participant $author) { $this->author = $author; }
/** * Getter for the author of this paper * * @return \Acme\Conference\Domain\Model\Participant * @author Robert Lemke <[email protected]> */ public function getAuthor() { return $this->author; }
/** * Setter for title * * @param string $title The title of this paper * @return void * @author Robert Lemke <[email protected]> */ public function setTitle($title) { $this->title = $title; }
/** * Getter for title * * @return string The title of this paper * @author Robert Lemke <[email protected]> */ 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 <[email protected]> */ public function setShortAbstract($shortAbstract) { $this->shortAbstract = $shortAbstract; }
/** * Getter for the short abstract * * @return string The short abstract * @author Robert Lemke <[email protected]> */ public function getShortAbstract() { return $this->shortAbstract; }
/** * Setter for abstract * * @param string $abstract The abstract of this paper * @return void * @author Robert Lemke <[email protected]> */ public function setAbstract($abstract) { $this->abstract = $abstract; }
/** * Getter for abstract * * @return string The abstract * @author Robert Lemke <[email protected]> */ public function getAbstract() { return $this->abstract; }
/** * Returns the materials attached to this paper * * @return \SplObjectStorage The materials * @author Robert Lemke <[email protected]> */ public function getMaterials() { return $this->materials; }
/** * Setter for the proposed session type * * @param \Acme\Conference\Domain\Model\SessionType $proposedSessionType The proposed session type * @return void * @author Robert Lemke <[email protected]> */ public function setProposedSessionType(\Acme\Conference\Domain\Model\SessionType $proposedSessionType) { $this->proposedSessionType = $proposedSessionType; }
/** * Getter for the proposed session type * * @return \Acme\Conference\Domain\Model\SessionType The proposed session type * @author Robert Lemke <[email protected]> */ public function getProposedSessionType() { return $this->proposedSessionType; }}?>
Hanau, Germany
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
Hanau, Germany
Domain-Driven Design
Ubiquitous Language
• important prerequisite for successful collaboration
• use the same words for
• discussion
• modeling
• development
• documentation
Hanau, Germany
Domain-Driven Design
Hanau, Germany
Domain: Conference
Hanau, Germany
Domain: Conference
Hanau, Germany
Domain: Conference
Hanau, Germany
Domain: Conference
Robert LemkeD.P. Fluxtr
time();
5 1 11
Kickstarting "Conference"
Hanau, Germany
Domain: Conference
Hanau, Germany
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
Hanau, Germany
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)
Hanau, Germany
<?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 ' . $this->greeterService->greet($name), 200, array('Content-Type' => 'text/plain')); }}
Constructor Injection: Symfony 2Warning: might contain errors
(I'm no Symfony expert ...)
Hanau, Germany
<?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 ...)
Hanau, Germany
<?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
Hanau, Germany
Constructor Injection
Hanau, Germany
<?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
Hanau, Germany
<?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
Hanau, Germany
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
Hanau, Germany
class Customer {
/** * @inject * @var CustomerNumberGenerator */ protected $customerNumberGenerator;
...}
$customer = new Customer();$customer->getCustomerNumber();
Object Management
Hanau, Germany
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
Hanau, Germany
Your Own Commands
$ ./flow3 kickstart:commandcontroller Acme.Demo Test
Hanau, Germany
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
Hanau, Germany
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
Hanau, Germany
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)
Hanau, Germany
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;
Hanau, Germany
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;
Hanau, Germany
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
Hanau, Germany
Property Mapper
$articleArray = array( 'headline' => 'Hello World!', 'story' => 'Just a demo ...' );
$article = $mapper->convert($sourceArray, 'Acme.Demo\Domain\Model\Article');
Hanau, Germany
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:
Hanau, Germany
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
Hanau, Germany
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
Hanau, Germany
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);
Hanau, Germany
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;
...
}
Hanau, Germany
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.
Hanau, Germany
@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
Hanau, Germany
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(); }}
Hanau, Germany
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
Hanau, Germany
Schema Management
Running Migrations
• needed after installation or upgrade:
$ ./flow3 doctrine:migrate
Hanau, Germany
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
Hanau, Germany
Schema Management
$ ./flow3 doctrine:migrationgenerateGenerated 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
Robert LemkeD.P. Fluxtr
time();
5 1 18
Migrations
Hanau, Germany
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');
Hanau, Germany
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);
Hanau, Germany
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);
Hanau, Germany
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));
Hanau, Germany
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);
Hanau, Germany
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
Hanau, Germany
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
Hanau, Germany
Security Policy
K. Dambekalns & R. LemkeD.P. Fluxtr
time();
5 1 18
Users and Login
Hanau, Germany
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
Hanau, Germany
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
Hanau, Germany
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
Hanau, Germany
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) {}
Hanau, Germany
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:
Hanau, Germany
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('[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:
Hanau, Germany
Roadmap
http://forge.typo3.org/projects/flow3-distribution-base/roadmap
Hanau, Germany
Conference App
git://git.typo3.org/TYPO3v5/Distributions/Conference.git
Hanau, Germany
Blog App
git://git.typo3.org/FLOW3/Applications/Blog.git
Hanau, Germany
Thank You & Have Fun!
• FLOW3: http://flow3.typo3.org
• Blog: http://robertlemke.de/blog
• Twitter: @robertlemke / @mrbasti
• Feedback: [email protected] / [email protected]