This talk represents the combined experience from several web development teams who have been using Symfony2 since months already to create high profile production applications. The aim is to give the audience real world advice on how to best leverage Symfony2, the current rough spots and how to work around them. Aside from covering how to implement functionality in Symfony2, this talk will also cover topics such as how to best integrate 3rd party bundles and where to find them as well as how to deploy the code and integrate into the entire server setup.
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Symfony2 - from the trenches
1. Symfony2 - From the Trenches
by Lukas Kahwe Smith,
Kris Wallsmith,
Thibault Duplessis,
Jeremy Mikola,
Jordi Boggiano,
Jonathan H. Wage,
Bulat Shakirzyanov
3. Getting setup
PHP 5.3+ with ext/intl (compat lib is in the works)
Read check.php for details (dev/prod php.ini's from Liip)
Using OSX?
php53-intl from liangzhenjing or build-entropy-php from chregu
Blog post on installing PHP 5.3 with intl from Justin Hileman
Initial setup
symfony-sandbox
symfony-bootstrap
Symfony2Project
Read the Coding Style Guide (Code Sniffer Rules)
Managing external dependencies
Submodule: not everything is in git (svn, mercurial, etc.)
Vendor install/update scripts: risk of getting out of sync
MR (not cross platform)
4. Code Flow
1. Frontend Controller (web/app[_dev].php)
Loads autoloader
Creates/boots kernel
Creates request (from globals) and passes to kernel
2. Kernel
Loads app config (app/config/config_[prod|dev|test])
Resolves URL path to a controller (go to 3)
Outputs response returned by the controller
3. Controller
Loads model and view
Potentially creates a sub-request (go to 2)
Creates response and returns it
7. <?php
class cachedDevDebugProjectContainer extends Container
{
/**
* Gets the 'sitemap' service.
*
* @return BundleAvalancheSitemapBundleSitemap
*/
protected function getSitemapService()
{
return $this->services['sitemap'] = new BundleAvalancheSitemapBundleSitemap();
}
/**
* Gets the default parameters.
*
* @return array An array of the default parameters
*/
protected function getDefaultParameters()
{
return array(
'sitemap.class' => 'BundleAvalancheSitemapBundleSitemap'
);
}
}
Service Definitions Are Dumped to Raw PHP
8. Service Container (aka DIC)
Benefits:
No performance loss
Lazy instantiation
Readable service configurations
Gotchas:
Can become hard to work with if the DI extension tries to do
too much
Be aware of circular dependencies
Might lead to code that cannot be used outside of DIC
9. <?php
class SomeClass
{
private $container;
public function __construct(ContainerInterface $container) <service id="some_service" class="SomeClass">
{ <argument type="service" id="service_container" />
$this->container = $container; </service>
} <!-- or -->
// or <service id="some_service" class="SomeClass">
public function setContainer(ContainerInterface $container) <call method="setContainer">
{ <argument type="service" id="service_container" />
$this->container = $container; </call>
} </service>
public function getDocumentManager()
{
return $this->container->get('document_manager');
}
}
Container Injection
10. <?php
class SomeClass
{
private $documentManager;
public function __construct(DocumentManager $documentManager)
{
$this->documentManager = $documentManager;
}
public function getDocumentManager()
{
return $this->documentManager;
}
}
<service id="some_service" class="SomeClass">
<argument type="service" id="document_manager" />
</service>
Constructor Injection
11. <?php
class SomeClass
{
private $documentManager;
public function setDocumentManager(DocumentManager $documentManager)
{
$this->documentManager = $documentManager;
}
public function getDocumentManager()
{
return $this->documentManager;
}
}
<service id="some_service" class="SomeClass">
<call method="setDocumentManager">
<argument type="service" id="document_manager" />
</call>
</service>
Setter Injection
12. <?php
interface SomeInterface
{
function setDocumentManager(DocumentManager $documentManager);
}
class SomeClass implements SomeInterface
{
private $documentManager;
public function setDocumentManager(DocumentManager $documentManager)
{
$this->documentManager = $documentManager;
}
public function getDocumentManager()
{
return $this->documentManager;
}
}
<interface id="some_service" class="SomeInterface">
<call method="setDocumentManager">
<argument type="service" id="document_manager" />
</call>
</interface>
<service id="some_service" class="SomeClass" />
Interface Injection
13. Configuration Choices
Symfony supports XML, YAML and PHP for configuration
YAML and PHP uses underscore to separate words
XML uses dashes to separate words
XML attributes usually map to array values in
YAML/PHP
YAML merge key syntax to reuse pieces within a file
XSD-aware editors provide auto-completion/validation
XML is recommended for Bundle/DI configuration
YAML is recommended for application configuration
Bundle extensions can optionally utilize the Config
component to normalize/parse configurations in any format
See FrameworkExtension, SecurityExtension, TwigExtension
14. Controller Choices
Defining Controllers as services is optional
Non-service controllers must use container injection
Create a Bundle extension to load Bundle services
It's recommended to not extend from the base Controller
The base controller is mainly a tool for beginners
It provides convenience methods that invoke services,
such as generateUrl(), redirect(), render()
15. Application Choices
Security system makes it possible to have just one
application for both frontend and admin backend
Location of AppKernel is totally flexible, just update the
frontend controllers accordingly
Large projects should use multiple applications
Better separation when multiple teams work
Facilitate step-by-step updating and refactoring
For example: main, mobile, API, admin
16. Doctrine Examples
Retrieve references to entity/document without DB queries
Using raw SQL queries with Doctrine2 ORM
Simple search engine with Doctrine MongoDB ODM
17. Retrieving References w/o DB Queries
$tags = array('baseball', 'basketball');
foreach ($tags as $tag) {
$product->addTag($em->getReference('Tag', $tag));
}
18. Raw SQL Queries
$rsm = new ResultSetMapping;
$rsm->addEntityResult('User', 'u');
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$query = $this->_em->createNativeQuery('SELECT id, name
FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
http://www.doctrine-project.org/docs/orm/2.0/en/reference/native-sql.html
24. Doctrine in the Real World
Go see Jon Wage's talk at later today!
25. Database Migrations
Deploy DB schema changes before the code
Prevent DB schema BC breaks
Use DoctrineMigrationBundle
app/console doctrine:migrations:diff
app/console doctrine:migrations:migrate
Do not use entities in migration scripts ever!
Write fixtures as migrations or make the fixtures able to
update existing data gracefully
app/console doctrine:data:load --fixtures=app/fixtures
27. Caching with Edge Side Includes
Symfony2 provides support for Edge Side Includes (ESI)
Proxy assembles page from snippets of HTML
Snippets can have different cache rules
Develop without ESI, test with Symfony2 internal ESI
proxy, deploy using ultra-fast Varnish Proxy
Break up page into different controller actions based on
cache invalidation rules
Do not worry about overhead from multiple render calls
Never mix content that has different cache timeouts
Consider caching user specific content in the client
Varnish Reverse Proxy
Super fast, PHP cannot match its performance
Cache full pages for anonymous users
Not just for HTML, also useful for JSON/XML API's
30. Performance Tips
Dump routes to Apache rewrite rules
app/console router:dump-apache
Write custom cache warmers
Do not explicitly inject optional services to controllers
If your controller receives many services, which are
optional or unused by some actions, that's probably a
hint that you should break it up into multiple controllers
Do minimal work in the controller, let templates pull
additional data as needed
Use a bytecode cache with MapFileClassLoader
31. Testing
Symfony2 rocks for unit and functional testing
Dependency Injection, core classes have interfaces (easy mocking)
No base classes, no static dependencies, no ActiveRecord
Client fakes "real" requests for functional testing (BrowserKit
component)
Functional Testing
Pros: tests configuration, tests API not implementation
Unit Testing
Pros: pinpoints issues, very directed testing
http://www.slideshare.net/avalanche123/clean-code-5609451
Recommendation:
Functional testing is recommended for controller actions
Symfony2 provides WebTestCase and BrowserKit
Unit testing for complex algorithms, third party API's too hard
to mock
Use LiipFunctionalTesting to load fixtures, validate HTML5
32. Deployment
Debian style (aka Liip Debian Packager)
Write a manifest in YAML
Build Debian packages with MAKE
Install with apt-get install
Server specific settings are asked during install, change later with
dpkg-reconfigure
Maintain a global overview of all application dependencies in case of
(security) updates
Watch Lukas' unconference talk later today!
Fabric (used at OpenSky)
Repository managed with git-flow
Clone tagged release branch, init submodules
Fix permissions (e.g. cache, log), delete dev/test controllers
Replace password/API-key placeholders with prod values in config
files
Upload in parallel to production nodes, swap "current" symlink
33. Third Party Bundles
@weaverryan
Ryan Weaver
Here's a new year's resolution: to *always*
work on an existing Symfony2 bundle and
never recreate my own. #focus #teamwork
27 Dec http://twitter.com/weaverryan/status/19565706752299009
34. Third Party Bundles
Many vendors have already published bundles:
FriendsOfSymfony (http://github.com/friendsofsymfony)
UserBundle (forked from knplabs' DoctrineUserBundle)
FacebookBundle (forked from kriswallsmith)
Liip (http://github.com/liip)
FunctionalTestBundle
ViewBundle
OpenSky (http://github.com/opensky)
LdapBundle
Sonata (http://github.com/sonata-project)
AdminBundle
Additionally, a couple sites currently index community bundles:
http://symfony2bundles.org/
http://symfohub.com/
35. Third Party Bundles
Bundles should follow the best practices
No version-tagging or official package manager (yet)
Use bundles by adding git submodules to your project
Maintain your own fork and "own" what you use
Not all bundles are equally maintained
Symfony2 API changes => broken bundles
If you track symfony/symfony, learn to migrate bundles
Avoid rewriting a bundle's services/parameters directly
The bundle's DI extension should allow for such
configuration; if not, submit a pull request
If absolutely necessary, a CompilerPass is cleaner
36. Contributing to Third Party Bundles
Similar to Symfony2's own patch guidlines
Fork and add remote repository
Merge regularly to keep up-to-date
Avoid committing directly to your master
Merges from upstream should be fast-forwards
Once upstream changes are stable, bump your
project's submodule pointer
37. Contributing to Third Party Bundles
Create branches for patches and new features
Can't wait to use this in your project? Temporarily
change your project submodule to point to your
branch until your pull request is accepted.
Help ensure that your pull request merges cleanly
Create feature branch based on upstream's master
Rebase or merge upstream's master when finished
38. Contributing to Third Party Bundles
Was your pull request accepted? Congratulations!
Don't merge your feature branch into master!
Doing so would cause your master to divert
Merge upstream's master into your master
Delete your feature branch
Update your project's submodule to point to master
39. Resources
If you want to jump in and contribute:
http://docs.symfony-reloaded.org/master/contributing/community/other.
html
If you are still fuzzy on Dependency Injection:
http://fabien.potencier.org/article/11/what-is-dependency-injection
If you keep up with Symfony2 repository on github:
http://docs.symfony-reloaded.org/master/
43. Dependency Injection
All objects are instantiated in one of two ways:
Using the "new" operator
Using an object factory
All objects get collaborators in one of two ways
Passed to object constructor
Set using a setter