Transcript
Page 1: Advanced symfony Techniques

Advanced symfony TechniquesKris Wallsmith

Page 2: Advanced symfony Techniques

@kriswallsmith

• Release Manager for symfony 1.3 & 1.4

• On Symfony and Doctrine teams

• Senior Software Engineer at

• 10 years experience with PHP and web development

• Open source evangelist and international speaker

• Hopeless plugin developer…

Page 3: Advanced symfony Techniques

• DbFinderPlugin

• sfControlPanelPlugin

• sfDoctrineDynamicFormRelationsPlugin

• sfDoctrineMasterSlavePlugin

• sfFeed2Plugin

• sfFormYamlEnhancementsPlugin

• sfGoogleAnalyticsPlugin

• sfGoogleWebsiteOptimizerPlugin

• sfModerationPlugin

• sfPagerNavigationPlugin

• sfPropelActAsPolymorphicBehaviorPlugin

• sfSimpleBlogPlugin

• sfSimpleCMSPlugin

• sfSimpleForumPlugin

• sfSpyPlugin

• sfSslRequirementPlugin

• sfStatsPlugin

• sfTaskExtraPlugin

• sfWebBrowserPlugin

Page 4: Advanced symfony Techniques

Please see me if you want tohelp with or take overa plugin's maintenance.

Lots to choose from!

Page 5: Advanced symfony Techniques

#phpmatsuriOctober 2-3, 2010

Tokyo

Page 6: Advanced symfony Techniques

• Around 90 attendees

• CakePHP, Symfony, & Lithiumwere represented

• Most folks were CakePHP users

• CakePHP documentation wastranslated early, so…

• Please help translate Symfony2 & Doctrine2 documentation!

#phpmatsuri

Page 7: Advanced symfony Techniques
Page 8: Advanced symfony Techniques

PSEUDO CODE AHEAD

CAUTION

Page 9: Advanced symfony Techniques

Host Aware Routing

Page 10: Advanced symfony Techniques

domain.com

Page 11: Advanced symfony Techniques

foobar.domain.com

Page 12: Advanced symfony Techniques

barfoo.domain.com

Page 13: Advanced symfony Techniques

homepage: url: / param: { module: main, action: indexOrDash }

Page 14: Advanced symfony Techniques

homepage: url: / param: { module: main, action: indexOrDash }

if (preg_match('/.../', $r->getHost(), $m))

Page 15: Advanced symfony Techniques

• ->matchesUrl(...)Does the supplied URL match this route?

class sfRoute

GET / HTTP/1.0Host: foobar.domain.com

Page 16: Advanced symfony Techniques

• ->matchesUrl(...)Does the supplied URL match this route?

• ->matchesParameters(...)Do the supplied parameters match this route?

class sfRoute

url_for('main/dashboard?username=foobar')

Very slow

Page 17: Advanced symfony Techniques

• ->matchesUrl(...)Does the supplied URL match this route?

• ->matchesParameters(...)Do the supplied parameters match this route?

• ->generate(...)Generate a URL using this route and these parameters.

class sfRoute

url_for('@dashboard?username=foobar')

Page 18: Advanced symfony Techniques

->matchesUrl(...)

• $urlThe current URI

• $contextAn array of contextual information, including the current host

• Returns false or an array of parameters extracted from the URI

Page 19: Advanced symfony Techniques

->matchesParameters(...)

• $paramsAn associative array of parameter names and values

• $contextAn array of contextual information, including the current host

• Returns true or false

Page 20: Advanced symfony Techniques

->generate(...)

• $paramsAn associative array of parameter names and values

• $contextAn array of contextual information, including the current host

• $absoluteWhether to generate an absolute URL

• Returns the generated URL

Page 21: Advanced symfony Techniques

Process the host string with a second, internal route

Page 22: Advanced symfony Techniques

public function __construct(...){ list($host, $pattern) = explode('/', $pattern, 2);

$hostRoute = $this->createHostRoute($host, ...);

parent::__construct(...);}

Page 23: Advanced symfony Techniques

public function matchesUrl($url, $c){ // check parent::matchesUrl() first

$hp = $hostRoute->matchesUrl('/'.$c['host'], $c);

// include host parameters in return}

Page 24: Advanced symfony Techniques

public function matchesParameters($p, $c){ $hp = $this->extractHostParams($p);

return parent::matchesParameters($p, $c) && $hostRoute->matchesParameters($hp, $c);}

Page 25: Advanced symfony Techniques

public function generate($p, $c, $abs){ $hp = $this->extractHostParams($p);

// protocol, prefix...

$host = $hostRoute->generate($hp, $c, false); $uri = parent::generate($p, $c, false);

return $protocol.':/'.$host.$prefix.$uri;}

Page 26: Advanced symfony Techniques

homepage: url: / param: { module: main, action: indexOrDash }

Page 27: Advanced symfony Techniques

Hardcoded FTL :(homepage: url: domain.com/ class: sfHostAwareRoute param: { module: main, action: index }

dashboard: url: :username.domain.com/ class: sfHostAwareRoute param: { module: main, action: dashboard }

Page 28: Advanced symfony Techniques

homepage: url: %APP_HOST%/ class: sfHostAwareRoute param: { module: main, action: index }

dashboard: url: :username.%APP_HOST%/ class: sfHostAwareRoute param: { module: main, action: dashboard }

Page 29: Advanced symfony Techniques

Custom Config Handler

Page 30: Advanced symfony Techniques

class sfHostAwareRoutingConfigHandler extends sfRoutingConfigHandler{ protected function parse($configFiles) { return array_map( array($this, 'filterRoute'), parent::parse($configFiles) ); }

// ...}

Page 31: Advanced symfony Techniques

protected function filterRoute($route){ list($class, $args) = $route;

$args[0] = $this->replaceConstants($args[0]);

return array($class, $args);}

Free FTW!

Page 32: Advanced symfony Techniques

# config_handlers.ymlconfig/routing.yml: class: sfHostAwareRoutingConfigHandler file: %SF_LIB_DIR%/sfHostAwareRout...

Page 33: Advanced symfony Techniques

sfHostAwareRoutingPluginAdd subdomains to your routing rules.

Page 34: Advanced symfony Techniques

Graceful POST Authentication

Page 35: Advanced symfony Techniques

An example…

Page 36: Advanced symfony Techniques
Page 37: Advanced symfony Techniques
Page 38: Advanced symfony Techniques

CENSORED

CENSORED

Page 39: Advanced symfony Techniques
Page 40: Advanced symfony Techniques

Where's my blog post!?!

#FAIL

Page 41: Advanced symfony Techniques

Extend the security filter

Page 42: Advanced symfony Techniques

class GracefulSecurityFilter extends sfBasicSecurityFilter{ protected function forwardToLoginAction() { // stash the interrupted request $attr->add(array( 'module' => $context->getActionName(), 'action' => $context->getModuleName(), 'method' => $request->getMethod(), 'params' => $requestParams->getAll(), ), 'stash');

parent::forwardToLoginAction(); }}

Page 43: Advanced symfony Techniques

# filters.ymlsecurity: class: GracefulSecurityFilter

Page 44: Advanced symfony Techniques

Replay the stashed requestafter login

Page 45: Advanced symfony Techniques

// called after authenticationprotected function replayStashedRequest(){ if ($s = $attr->removeNamespace('stash')) { $request->setMethod($s['method']);

$params->clear(); $params->add($s['params']);

$this->forward($s['module'], $s['action']); }}

Page 46: Advanced symfony Techniques

Extra Security

Page 47: Advanced symfony Techniques

An example…

Page 48: Advanced symfony Techniques
Page 49: Advanced symfony Techniques
Page 50: Advanced symfony Techniques

# security.ymlacceptInvitation: is_secure: true extra_credentials: account: { lifetime: 300 }

Page 51: Advanced symfony Techniques

Events to the rescue!

Page 52: Advanced symfony Techniques

controller.change_action

Page 53: Advanced symfony Techniques

// connect to the event$ed->connect('controller.change_action', $cb)

Page 54: Advanced symfony Techniques

// check security.yml$action->getSecurityValue('extra_credentials')

Page 55: Advanced symfony Techniques

// check current user$u->getAttribute('extra_credentials', array())

Page 56: Advanced symfony Techniques

// remove any expired credentials$now = time();foreach ($creds as $name => $attr){ if ($now > $attr['expires_at']) { unset($creds[$name]); }}

Page 57: Advanced symfony Techniques

// stash credentials and referer$u->setAttribute('challenge_credentials', ...)$u->setAttribute('challenge_referer', ...)

Page 58: Advanced symfony Techniques

// forward to challenge form$controller->forward('security', 'challenge')throw new sfStopException();

Page 59: Advanced symfony Techniques

// add the granted credentials$now = time();foreach ($new as $name => $attr){ $creds[$name] = array( 'expires_at' => $now + $attr['lifetime'], );}$u->setAttribute('extra_credentials', $creds);

Page 60: Advanced symfony Techniques

// send them on their way$this->redirect($referer);

Page 61: Advanced symfony Techniques

sfExtraSecurityPluginRe-prompt your users for authentication.

Page 62: Advanced symfony Techniques

Javascript Compression

Page 63: Advanced symfony Techniques

<script src="http://domain.com/widget.js"></script>

Page 64: Advanced symfony Techniques

class jsActions extends sfActions{ public function executeWidget(sfWebRequest $req) { $this->lightbox = $req->hasParameter('lb'); $this->debug = $req->hasParameter('debug'); }}

Page 65: Advanced symfony Techniques

<?php if ($debug): ?>console.log("embedding mootools");<?php endif; ?>

var e = document.createElement("script");e.src = "<?php echo public_path('js/moo.js', true) ?>";e.async = true;document.body.appendChild(e);

// etc...

Page 66: Advanced symfony Techniques

Custom View Class

Page 67: Advanced symfony Techniques

# module.ymlall: view_class: Javascript

JavascriptView

Page 68: Advanced symfony Techniques

class JavascriptView extends sfPHPView{ public function render() { return $this->compress(parent::render()); }

protected function compress() { // ... }}

Page 69: Advanced symfony Techniques

$i = tempnam(sys_get_temp_dir(), __CLASS__);$o = tempnam(sys_get_temp_dir(), __CLASS__);

file_put_contents($i, $content);

shell_exec(vsprintf( 'java -jar %s --type js -o %s %s', array_map('escapeshellarg', array($yui, $o, $i))));

return file_get_contents($o);

Page 70: Advanced symfony Techniques

Standard Caching

Page 71: Advanced symfony Techniques

# cache.ymlwidget: enabled: true with_layout: true

Page 72: Advanced symfony Techniques

developer.yahoo.com/yui/compressor/

Page 73: Advanced symfony Techniques

A Few Apache Tricks

Page 74: Advanced symfony Techniques

rm web/.htaccess

Page 75: Advanced symfony Techniques

AllowOverride None

Page 76: Advanced symfony Techniques

<Directory /path/to/web> Include /path/to/.htaccess</Directory>

Page 77: Advanced symfony Techniques

Core Assets

Page 78: Advanced symfony Techniques

Missing Assets #FAIL :(

Page 79: Advanced symfony Techniques

AliasMatch /sf/(.*) /path/to/symfony/data/web/sf/$1

AliasMatch /sfDoctrinePlugin/(.*) /path/to/sfDoctrinePlugin/web/$1

NameVirtualHost *:80<VirtualHost _default_:80> # ...

Page 80: Advanced symfony Techniques

Assets Found FTW!

Page 81: Advanced symfony Techniques

The Dreaded Trailing Slash…

Page 82: Advanced symfony Techniques
Page 83: Advanced symfony Techniques

#FAIL

Page 84: Advanced symfony Techniques

RewriteEngine OnRewriteRule ^(.*)/$ /$1 [R=301,L]

Page 85: Advanced symfony Techniques

GET /about/ HTTP/1.1Host: domain.com

HTTP/1.1 301 Moved PermanentlyLocation: http://domain.com/about

Page 86: Advanced symfony Techniques

Embedded Forms

Page 87: Advanced symfony Techniques

Person

One book has many authors,one author has many books.

Book

Authors

Page 88: Advanced symfony Techniques

Book: columns: title: string(255) relations: authors: { class: Person, refClass: BookAuthor }BookAuthor: columns: book_id: integer author_id: integer relations: book: { local: book_id } author: { class: Person, local: author_id }Person: columns: name: string(255)

Page 89: Advanced symfony Techniques
Page 90: Advanced symfony Techniques

// embed related forms$this->embedRelation('authors');unset($this['authors_list']);

Page 91: Advanced symfony Techniques
Page 92: Advanced symfony Techniques
Page 93: Advanced symfony Techniques
Page 94: Advanced symfony Techniques
Page 95: Advanced symfony Techniques
Page 96: Advanced symfony Techniques
Page 97: Advanced symfony Techniques

// embed related forms dynamically!$this->embedDynamicRelation('authors');

Page 98: Advanced symfony Techniques

form.method_not_found

form.filter_values

Page 99: Advanced symfony Techniques

// called when a form is configuredpublic function embedDynamicRelation($name){ $rel = $table->getRelation($name); $this->rels[] = $rel;

$this->doEmbed($name, $obj->get($rel->getAlias()));}

Page 100: Advanced symfony Techniques

// called when a form is boundpublic function filterValues(sfEvent $event, $values){ foreach ($this->rels as $rel) { $name = $rel->getName(); $this->doEmbed($name, $values[$name]); }

$obj->addListener(new DeleteListener($form));}

Page 101: Advanced symfony Techniques

$parent = new BaseForm();foreach ($values as $i => $value) { if (is_object($value)) { // create form with object } elseif ($value['id']) { // find previously embedded form } else { // create a new form }

$parent->embedForm($i, $child);}

$form->embedForm($rel->getName(), $parent);

Page 102: Advanced symfony Techniques

// extract existing objects from embedded forms// and compare to the current object collectionpublic function preSave(Doctrine_Event $event){ foreach ($coll as $i => $obj) { $pos = array_search($obj, $existing, true); if (false === $pos) $coll->remove($i);

if ($column['notnull']) $obj->delete(); }}

Page 103: Advanced symfony Techniques

sfDoctrineDynamicFormRelationsPluginCommon sense embedded forms.

Page 104: Advanced symfony Techniques

Questions?

• Host aware routing

• Graceful POST authentication

• Extra security

• Javascript compression

• Apache tricks

• Embedded forms

Page 105: Advanced symfony Techniques

OpenSky is Hiring!http://engineering.shopopensky.com

Please contact me if you're interested.

Page 106: Advanced symfony Techniques

Thank you!


Top Related