TYPO3 Flow 2.0 Workshop T3BOARD13

Post on 19-May-2015

12.218 views 3 download

Tags:

description

Slides of the TYPO3 Flow 2.0 fundamentals workshop which took place at Schmittenhöhe, Austria during T3BOARD13

Transcript of TYPO3 Flow 2.0 Workshop T3BOARD13

TYPO3 Flow 2.0

Robert Lemke_

TEXT HERE

project founder of TYPO3 Flow and TYPO3 Neos

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

TYPO3 Flow Website and Download

Installation via Composer

$ curl -s https://getcomposer.org/installer | php$ sudo mv composer.phar /usr/local/bin/composer

$ composer create-project --stability="beta" --dev typo3/flow-base-distribution MyProject

Set File Permissions

$ sudo ./flow core:setfilepermissions robert _www _wwwTYPO3 Flow 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:

Set Up Database Connection

Configuration/Settings.yaml

TYPO3: Flow: persistence: backendOptions: host: '127.0.0.1' # adjust to your database host dbname: 'training' # adjust to your database name user: 'root' # adjust to your database user password: 'password' # adjust to your database password

# if you want to log executed SQL queries, enable the next 2 lines # doctrine: # sqlLogger: 'TYPO3\Flow\Persistence\Doctrine\Logging\SqlLogger'

# You might need to uncomment the following lines and specify # the location of the PHP binary manually. # core: # phpBinaryPathAndFilename: 'C:/path/to/php.exe'

Set Up Virtual Host

Apache Virtual Host

<VirtualHost *:80> DocumentRoot ~/Sites/Flow/Web/ ServerName flow.dev SetEnv FLOW_CONTEXT Development</VirtualHost>

<VirtualHost *:80> DocumentRoot ~/Sites/Flow/Web/ ServerName flow.prod SetEnv FLOW_CONTEXT Production</VirtualHost>

Final Check

Command Line Use

$ ./flow help kickstart:package

Kickstart a new package

COMMAND: typo3.kickstart:kickstart:package

USAGE: ./flow 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.flow:package:create (Create a new package)

Command Line Use

$ ./flow help kickstart:actioncontroller

Kickstart a new action controller

COMMAND: typo3.kickstart:kickstart:actioncontroller

USAGE: ./flow kickstart:actioncontroller [<options>] <package key> <controller name>

ARGUMENTS: --package-key The package key of the package for the new controller with an optional subpackage, (e.g. "MyCompany.MyPackage/Admin"). --controller-name The name for the new controller. This may also be a comma separated list of controller names.

OPTIONS: --generate-actions Also generate index, new, create, edit, update and delete actions. --generate-templates Also generate the templates for each action. --generate-related Also create the mentioned package, related model and repository if neccessary. --force Overwrite any existing controller or template code. Regardless of this flag, the package, model and repository will never be overwritten.

DESCRIPTION: Generates an Action Controller with the given name in the specified package. In its default mode it will create just the controller containing a sample indexAction. By specifying the --generate-actions flag, this command will also create a set of actions. If no model or repository exists which matches the controller name (for example "CoffeeRepository" for "CoffeeController"), an error will be shown. Likewise the command exits with an error if the specified package does not exist. By using the --generate-related flag, a missing package, model or repository can be created alongside, avoiding such an error. By specifying the --generate-templates flag, this command will also create matching Fluid templates for the actions created. This option can only be used in combination with --generate-actions. The default behavior is to not overwrite any existing code. This can be overridden by specifying the --force flag.

SEE ALSO: typo3.kickstart:kickstart:commandcontroller (Kickstart a new command controller)

Biggest Book Store: Amazon

Biggest River: Amazon River

© Google

Smallest River: Roe River

© Google

Smallest River: Roe River

© Google

Smallest River: Roe River

© Google

Smallest River: Roe River

Smallest Book Store: Roebooks

Sketchy Model

Robert LemkeD.P. Fluxtr

time();

5 1 12

Hello World …

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

<?php

class SomeService { protected static $instance; public function getInstance() { if (self::$instance === NULL) { self::$instance = new self; } return self::$instance; }}

class SomeOtherController { public function action() { $service = SomeService::getInstance(); … } }

?>

class ServiceLocator { protected static $services = array(); public function getInstance($name) { return self::$service[$name]; }

}

class SomeOtherController { public function action() { $service = ServiceLocator::getInstance("SomeService"); … } }

class BookController extends ActionController {

/** * @var BookRepository */ protected $bookRepository; /** * @param BookRepository $bookRepository */ public function __construct(BookRepository $bookRepository) { $this->bookRepository = $bookRepository; } }

class BookController extends ActionController {

/** * @var BookRepository */ protected $bookRepository; /** * @param BookRepository $bookRepository */ public function injectBookRepository(BookRepository $bookRepository) { $this->bookRepository = $bookRepository; } }

class BookController extends ActionController {

/** * @Flow\Inject * @var BookRepository */ protected $bookRepository;

}

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

Object Management

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

class Customer {

/** * @Flow\Inject * @var \Acme\CustomerNumberGenerator */ protected $customerNumberGenerator; ...}

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

Object Management

<?phpdeclare(ENCODING = 'utf-8');namespace TYPO3\Conference\Domain\Model\Conference;use TYPO3\Flow\Annotations as Flow;/** * Autogenerated Proxy Class * @Flow\Scope(“prototype”) * @Flow\Entity */class Paper extends Paper_Original implements \TYPO3\Flow\Object\Proxy\ProxyInterface, \TYPO3\Flow\Persistence\Aspect\PersistenceMagicInterface { /** * @var string * @ORM\Id * @ORM\Column(length="40") * introduced by TYPO3\Flow\Persistence\Aspect\PersistenceMagicAspect */ protected $Flow_Persistence_Identifier = NULL; private $Flow_AOP_Proxy_targetMethodsAndGroupedAdvices = array(); private $Flow_AOP_Proxy_groupedAdviceChains = array(); private $Flow_AOP_Proxy_methodIsInAdviceMode = array();

/** * Autogenerated Proxy Method */ public function __construct() { $this->Flow_AOP_Proxy_buildMethodsAndAdvicesArray(); if (isset($this->Flow_AOP_Proxy_methodIsInAdviceMode['__construct'])) { parent::__construct();

} else {

Flow 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

Object Scope

_ prototype: multiple instances for one request

_ singleton: one unique instance for one request

_ session: one unique instance for one session

_ default scope: prototype.

/** * @Flow\Scope("prototype") */class BookController extends ActionController {

Lifecycle Methods

/** * Called after the object has been constructed and all * dependencies have been injected * * @param integer $initializationCause * @return void */ public function initializeObject($initializationCause) { switch ($initializationCause) { case ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED : … case ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED : … } }

Lifecycle Methods

/** * Called shortly before the framework shuts down */ public function shutdownObject() { }

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

AOP

FLOW3 uses AOP for ...

_ persistence magic

_ logging

_ debugging

_ security

/** * @Aspect * @Introduce TYPO3\Flow\Persistence\Aspect\PersistenceMagicInterface, TYPO3\Flow\Persistence

*/class PersistenceMagicAspect { /** * @Pointcut classTaggedWith(entity) || classTaggedWith(valueobject) */ public function isEntityOrValueObject() {} /** * After returning advice, making sure we have an UUID for each and every entity.

* * @param \TYPO3\Flow\AOP\JoinPointInterface $joinPoint The current join point

* @return void * @Before classTaggedWith(entity) && method(.*->__construct()) */ public function generateUUID(JoinPointInterface $joinPoint) { $proxy = $joinPoint->getProxy(); ObjectAccess::setProperty($proxy, 'Flow_Persistence_Identifier', … }

Aspect

_ part of the application where cross-cutting concerns are implemented

_ in Flow aspects are classes annotated with@Flow\Aspect

Join Point

A single point in the call graph

_ method execution

_ exception

Join Point

A single point in the call graph

_ method execution

_ exception

Represents an event, not a location

Pointcut

A set of join points where advices could be executed

_ can be composed

_ can be named

Advice

Action to take at a join points defined by the point cut

Kinds of Advice

Advice types supported by Flow:

@Flow\Before@Flow\AfterReturning@Flow\AfterThrowing@Flow\After@Flow\Around

Pointcut Designators

method(Acme\Demo\MyClass->myMethod())class(Acme\Demo\MyClass)within(Acme\Demo\MyInterface)classAnnotatedWith(someTag)methodAnnotatedWith(anotherTag)setting(Acme.Demo.SomeSetting = "yeah, do it")filter(Acme\Demo\MyCustomFilterImplementation)

evaluate(coffe.kind = "Arabica")

namespace TYPO3\Flow\Session\Aspect;use TYPO3\Flow\Annotations as Flow;

/** * An aspect which centralizes the logging of important session actions. * * @Flow\Aspect * @Flow\Scope("singleton") */class LoggingAspect {

/** * @var \TYPO3\Flow\Log\SystemLoggerInterface * @Flow\Inject */ protected $systemLogger;

/** * Logs calls of start() * * @Flow\After("within(TYPO3\Flow\Session\SessionInterface) && method(.*->start())") * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current joinpoint */ public function logStart(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) { $session = $joinPoint->getProxy(); if ($session->isStarted()) { $this->systemLogger->log(sprintf('Started session with id %s', $session->getId()), LOG_DEBUG);

} }

Persistence

Object Persistence in the Flow

_ based on Doctrine 2

_ seamless integration into Flow

_ 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

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

Annotations

In order to use less code, the following examples assume that annotations have been imported directly:

use TYPO3\Flow\Annotations\Entity;

/** * @Entity */class Foo {}

Validation and Doctrine Annotations

/** * @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;

...

}

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.

@var Defines the type of a property, collections can be typed using angle brackets

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

Custom Queries using theQuery Object Model

class PostRepository extends 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\Flow\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\Flow\Persistence\QueryInterface::ORDER_DESCENDING) ) ->execute(); }}

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 Flow

Schema Management

Executing migration scripts

Needed after installation or upgrade:

$ ./flow doctrine:migrate

Schema Management

Manual database updates

Ad-hoc table and column creation, while you’re developing:

$ ./flow doctrine:create

$ ./flow doctrine:update

Schema Management

Generating migration scripts

Creates a basis for a migration script which sometimes needs to be adjusted but in any case needs to be checked:

$ ./flow doctrine:migrationgenerate

Security

_ centrally managed (through AOP)

_ as secure as possible by default

_ modeled after TYPO3 CMS and Spring Security

_ authentication, authorization, validation, filtering ...

_ can intercept arbitrary method calls

_ transparently filters content through query-rewriting

_ extensible for new authentication or authorization mechanisms

Accounts, Users, Authentication

Flow distinguishes between accounts and persons:

_ account: \TYPO3\Flow\Security\Account

_ person: \TYPO3\Party\Domain\Model\Person

A person (or machine) can have any number of accounts.

Creating Accounts

_ always use the AccountFactory

_ create a party (eg. a Person) separately

_ assign the account to the party

_ add account and party to their respective repositories

$account = $this->accountFactory->createAccountWithPassword( $accountIdentifier, $password, array($role));

$this->accountRepository->add($account);

$person = new Person();$person->addAccount($account);

$name = new PersonName('', 'Robert', '', 'Lemke');$person->setName($name);

$this->partyRepository->add($person);

Authentication Configuration

_ Authentication Provider is responsible for authentication in a specific "area"

_ Entry Point kicks in if a restricted resource is accessed and no account is authenticated yet

TYPO3: Flow: security: authentication: providers: DefaultProvider: provider: 'PersistedUsernamePasswordProvider' entryPoint: 'WebRedirect' entryPointOptions: routeValues: '@package': 'RobertLemke.Example.Bookshop' '@controller': 'Login' '@action': 'login' '@format': 'html'

Security Policy (policy.yaml)

_ resources defines what can potentially be protected

_ roles defines who can potentially be granted or denied access

_ aclsdefines who may or may not access which resource

resources: methods: BookManagementMethods: 'method(.*Controller->(new|edit|create|delete|update)Action())' BookManagementDelete: 'method(.*BookController->deleteAction())'

roles: Administrator: []

acls: methods: Administrator: BookManagementMethods: GRANT

Login / Logout

_ simply extend AbstractAuthenticationController

_ create a Fluid template with a login form

/** * @Flow\Scope("singleton") */class LoginController extends AbstractAuthenticationController {

/** * @param \TYPO3\Flow\Mvc\ActionRequest $originalRequest The request that * @return string */ protected function onAuthenticationSuccess(ActionRequest $originalRequest = NULL) { $this->redirect('index', 'Book'); }

/** * @return void */ public function logoutAction() { parent::logoutAction(); $this->redirect('index', 'Book'); }}

<f:base/><f:flashMessages /><f:form action="authenticate"> <f:form.textfield name="__authentication[TYPO3][Flow][Security][Authentication][Token][UsernamePassword][username]" /> <f:form.password name="__authentication[TYPO3][Flow][Security][Authentication][Token][UsernamePassword][password]" /> <f:form.submit value="login" /></f:form>

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

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

Security

CSRF Protection in Flow

_ you must not forget to add that token to any link

_ Flow 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

Robert Lemke_robertlemke.com@robertlemke