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.
2. Robert Lemke
chief architect 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
8. At a Glance
FLOW3 is a web application platform
• 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
9. 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
12. 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.
Linux:
$ sudo usermod -a -G www-data robert
Mac OS X:
$ sudo dscl . -append /Groups/_www GroupMembership robert
13. 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'
14. 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>
24. Jus t C o de a Sh o p
5 2 1 1
Ro bert Lem ke
D.P. F l u x t r
time ();
25. Tackling the Heart of Software Development
/**
Domain-Driven Design * A Book
*
* @FLOW3Scope(“protot
ype”)
* @FLOW3Entity
A methodology which ... */
class Book {
• results in rich domain models /**
* @var string
*/
• provides a common language protected $title;
across the project team /**
* @var string
*/
• simplify the design of complex protected $isbn;
applications /**
* @var string
*/
protected $description
;
FLOW3 is the first PHP framework
/**
tailored to Domain-Driven Design * @var integer
*/
protected $price;
26. 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
27. Domain-Driven Design
Ubiquitous Language
• important prerequisite for successful
collaboration
• use the same words for
• discussion
• modeling
• development
• documentation
28. 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.
29. 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.
31. 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)
32. Constructor Injection
namespace AcmeDemoController;
use TYPO3FLOW3MvcControllerActionController;
use AcmeDemoServiceGreeterService;
class DemoController extends ActionController {
/**
* @var AcmeDemoServiceGreeterService
*/
protected $greeterService;
/**
* @param AcmeDemoServiceGreeterService
*/
public function __construct(GreeterService $greeterService) {
$this->greeterService = $greeterService;
}
/**
* @param string $name
*/
public function helloAction($name) {
return $this->greeterService->greet($name);
}
}
33. Setter Injection
namespace AcmeDemoController;
use TYPO3FLOW3MVCControllerActionController;
use AcmeDemoServiceGreeterService;
class DemoController extends ActionController {
/**
* @var AcmeDemoServiceGreeterService
*/
protected $greeterService;
/**
* @param AcmeDemoServiceGreeterService
*/
public function injectGreeterService(GreeterService $greeterService) {
$this->greeterService = $greeterService;
}
/**
* @param string $name
*/
public function helloAction($name) {
return $this->greeterService->greet($name);
}
}
34. Property Injection
namespace TYPO3DemoController;
use TYPO3FLOW3Annotations as FLOW3;
use TYPO3FLOW3MVCControllerActionController;
use AcmeDemoServiceGreeterService;
class DemoController extends ActionController {
/**
* @var TYPO3DemoServiceGreeterService
* @FLOW3Inject
*/
protected $greeterService;
/**
* @param string $name
*/
public function helloAction($name) {
return $this->greeterService->greet($name);
}
}
37. Object Management
<?php
declare(ENCODING = 'u
tf-8');
namespace TYPO3Conf
erenceDomainModel
use TYPO3FLOW3Anno Conference;
tations as FLOW3;
FLOW3 creates proxy classes /**
* Autogenerated Prox
y Class
for realizing DI and AOP magic * @FLOW3Scope(“prot
otype”)
* @FLOW3Entity
*/
• new operator is supported class Paper extends
TYPO3FLOW3Persist
Paper_Original implem
ents TYPO3FLOW3Obj
ect
enceAspectPersiste
nceMagicInterface {
/**
• proxy classes are created * @var string
on the fly
* @ORMId
* @ORMColumn(length
="40")
* introduced by TYPO
3FLOW3Persistence
*/ AspectPersistenceMa
• in production context all protected $FLOW3_Per
sistence_Identifier
= NULL;
code is static private $FLOW3_AOP_P
roxy_targetMethodsAn
dGroupedAdvices = ar
ra
private $FLOW3_AOP_P
roxy_groupedAdviceCh
ains = array();
private $FLOW3_AOP_P
roxy_methodIsInAdvic
eMode = array();
/**
* Autogenerated Prox
y Method
*/
public function __co
nstruct()
38. 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
39. 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);
40. Validation and Doctrine Annotations
namespace TYPO3BlogDomainModel;
/**
* A Blog object
*
* @Entity
*/
class Blog {
/**
* @var string
* @validate Text, StringLength(minimum = 1, maximum = 80)
* @Column(length="80")
*/
protected $title;
/**
* @var DoctrineCommonCollectionsCollection<TYPO3BlogDomainModelPost>
* @OneToMany(mappedBy="blog")
* @OrderBy({"date" = "DESC"})
*/
protected $posts;
...
}
41. 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 Defines relations to other entities. Unlike with
@OneToMany vanilla Doctrine targetEntity does not have to be
@ManyToMany given but will be reused from the @var
@OneToOne annotation.
cascade can be used to cascade operation to
related objects.
42. Persistence-related Annotations
@var Defines the type of a property, collections can be
typed using angle brackets:
DoctrineCommonCollectionsCollection<TYPO3ConferenceDomainModelComment>
@transient The property will be ignored, it will neither be
persisted nor reconstituted
@identity Marks the property as part of an objects identity
43. Custom Queries using the Query Object Model
/**
* A PostRepository
*/
class PostRepository extends TYPO3FLOW3PersistenceRepository {
/**
* Finds posts by the specified tag and blog
*
* @param TYPO3BlogDomainModelTag $tag
* @param TYPO3BlogDomainModelBlog $blog The blog the post must refer to
* @return TYPO3FLOW3PersistenceQueryResultInterface The posts
*/
public function findByTagAndBlog(TYPO3BlogDomainModelTag $tag,
TYPO3BlogDomainModelBlog $blog) {
$query = $this->createQuery();
return $query->matching(
$query->logicalAnd(
$query->equals('blog', $blog),
$query->contains('tags', $tag)
)
)
->setOrderings(array(
'date' => TYPO3FLOW3PersistenceQueryInterface::ORDER_DESCENDING)
)
->execute();
}
}
44. 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
46. Schema Management
Manual database updates
• for simple situations this can be good enough:
$ ./flow3 doctrine:create
$ ./flow3 doctrine:update
• useful when
• you are just starting a project and have never released
47. Schema Management
Generating migrations
$ ./flow3 doctrine:migrationgenerate
Generated new migration class to "…/Version20110608074324.php"
from schema differences.
$
• Generated migrations can contain errors and should be checked
and adjusted as needed
• Migrations need to be moved to their “owning” package manually
48. 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
49. 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
50. 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)
51. 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;
52. 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 DambekalnsStuffDomainModelStuff
* @validate DambekalnsStuffDomainValidatorStuffValidator
*/
protected $stuff;
53. 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
55. Resource Management
Image Upload
Resources are handled like other properties in a form:
<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>
56. Property Mapper
Allow nested object structures
For security reasons the creation of nested structure through the
property mapper is disabled by default
/**
* @return void
*/
public function initializeUpdateAction() {
$this->arguments['article']->getPropertyMappingConfiguration()
->allowCreationForSubProperty('picture');
$this->arguments['article']->getPropertyMappingConfiguration()
->allowModificationForSubProperty('picture');
}
57. 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)
58. Fluid
Example for assigning a string to a Fluid variable:
// in the action controller:
$this->view->assign('title', 'Welcome to Fluid');
<!-- in the Fluid template: -->
<head>
<title>{title}</title>
</head>
59. Fluid
Variables can also be objects:
// in the action controller:
$this->view->assign('conference', $conference);
<!-- in the Fluid template: -->
<div class="venue">
<p>Venue Street: {conference.venue.street}</p>
</div>
60. Fluid
if-then-else:
// in the action controller:
$this->view->assign('post', $blogPost);
<!-- 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>
61. Fluid
for-each:
// in the action controller:
$this->view->assign('ages', array("Karsten" => 34, "Robert" => 35));
<!-- in the Fluid template: -->
<ul>
<f:for each="{ages}" as="age" key="name">
<li>{name} is {age} year old.</li>
</f:for>
</ul>
62. Fluid
for-each:
// in the action controller:
$this->view->assign('post', $blogPost);
<!-- 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>
63. Fluid
View helpers – in this case the link.action view helper:
<!-- in the Fluid template: -->
{namespace f=TYPO3FluidViewHelpers}
<f:link.action action="delete" arguments="{post: post, really: 'yes'}">
Delete this post
</f:link.action>
64. 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
66. 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...
67. 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
68. 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
69. 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
70. AOP
/**
* @aspect
FLOW3 uses AOP for ... * @introduce
*/
TYPO3FLOW3P
ersistenceAs
pectPersiste
class Persist nceMagicInter
enceMagicAspe face, TYP
ct {
• persistence magic
/**
* @pointcut c
lassTaggedWit
*/ h(entity) ||
classTaggedWi
th(valueobjec
• logging public functi
on isEntityOr
ValueObject()
{}
t)
/**
* @var string
• debugging
* @Id
* @Column(len
gth="40")
* @introduce
TYPO3FLOW3P
*/ ersistenceAs
pectPersiste
• security protected $FL
OW3_Persisten
ce_Identifier
;
nceMagicAspec
t->isE
/**
* After retur
ning advice,
* making sure w
e have an UUI
* @param TYP D for each an
O3FLOW3AOP d every
* @return voi JoinPointInte
d rface $joinPo
* @before cla int The curre
ssTaggedWith( nt join
*/ entity) && me
thod(.*->__co
public functi nstruct())
on generateUU
$proxy = $joi ID(TYPO3FLO
nPoint->getPr W3AOPJoinPo
oxy(); intInterface
TYPO3FLOW3 $joinPoin
ReflectionOb
} jectAccess::s
etProperty($p
r o x y , 'FLOW3_
Persi
71. 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
73. Signal-Slot Event Handling
Signals are wired to Slots in a package’s bootstrap:
/**
* Invokes custom PHP code directly after the package manager has been
* initialized.
*
* @param TYPO3FLOW3CoreBootstrap $bootstrap The current bootstrap
* @return void
*/
public function boot(TYPO3FLOW3CoreBootstrap $bootstrap) {
$dispatcher = $bootstrap->getSignalSlotDispatcher();
$dispatcher->connect(
'TYPO3BlogControllerCommentController', 'commentCreated',
'TYPO3BlogServiceNotification', 'sendNewCommentNotification'
);
}
74. Signal-Slot Event Handling
Any method can be a slot:
/**
* @param TYPO3BlogDomainModelComment $comment
* @param TYPO3BlogDomainModelPost $post
* @return void
*/
public function sendNewCommentNotification(TYPO3BlogDomainModelComment
$comment,
TYPO3BlogDomainModelPost $post) {
$mail = new TYPO3SwiftMailerMessage();
$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();
}
81. Social Media Suite
• central hub for social media activities
for potentially thousands of travel
agencies
• advanced form engine
• various detail improvements by AKOM360, Munich
• uses an early version of
TYPO3 Phoenix
82. “Our senior developers are
extremely happy with FLOW3 –
it is definitely the most
capable PHP framework we Fabian Pfütze
Project Lead
have come across
so far.”