You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In order for core to grow, funded contribution is necessary.
Reluctance to give funding since results won't be out for 2-3 years.
Burnout.
2-3 year release cycle:
Pros:
Provides long-term stability that clients want
Some initiatives will take a long time (WSCCI)
Cons:
Difficult for developers as they'll have to live with their choices for 2-3 years.
@heyrocker's ideas:
Drupal 9 - 3.5 years
Layering release cycles on top of that.
We shouldn't be afraid to make improvements in minor release cycles
If we provide backwards compatibility, we could break APIs mid-release.
Backwards compatibility has a performance cost.
Decoupled-ness provides better flexibility, should be easier.
Post-presentation Conversation
Gabor: Last week, people expected Google to release a new Android OS, but instead they released lots of new features to the core programs that improved stuff
Smallcore?
Market fragmentation - phone providers customize their distribution of Android so much that new features take a long time to get to everyone's hands.
Larry Garfield:
Good architecture lets you have nice code without breaking APIs.
The trend we're on must continue.
Being able to predict how much time we have to throw at problems is the thing that's missing when we're working on Core. - Views initiative did this right.
Hard to figure out what to do when we do have time to work on Core
XJM (Jess): Maybe we need to maintain two branches - one branch for stable public API, new APIs on a different branch.
Would love employer to pay full-, half-time to work on Drupal, how do I convince them to do that?
It's super-important for people in the Drupal community to work on Core.
We want to write code that is ignorant - the less it knows, the more reusable it is.
Forms of DI:
constructor injection:
Accept interfaces instead of classes in constructors
Setter injection:
Setter method accepts interface, changes reference in object
Interface injection:
For every setter, there's an interface for each dependency's setter method
Very verbose
Not common
Inversion of control == Dependency Injection
Traditionally, your code calls things in a library.
In DI/IoC, the framework calls your code with it's dependencies.
"Don't call us, we'll call you."
Where does injection happen?
Manual injection (example code)
Use a factory
Use a container / injector (used in frameworks)
DI from a framework perspective
Dependency injector = Dependency injection container = IoC Container = Service container
Responsible for constructing object graphs based on configuration
Objects as services
An object that provides global functionality
Cache backend
Logger
Mailer
URL generator
NOT services:
product
blog post
e-mail message
DI containers need to be aware of:
Classes
Service definitions
Control flow / application flow
Some frameworks use XML
How does it work?
Service keys map to service definitions
Code asks for service
Symfony-style DI components
Stand-alone component
Service keys are strings
Service definitions specify which class to instantiate, what constructor arguments, allows additional methods to call after instantiation
Scope = container
Can be configured in PHP, XML, YAML
Can be "compiled" to plain PHP
Too expensive to parse config each request.
Parse once and put the result into a PHP class that hardcodes a method for each service.
"Synthetic services"
Container doesn't know how to instantiate, but the container can be told about it so it can treat it as a service when someone wants one
"Compiler classes"
Classes that process the container to manipulate existing service definitions
Use to:
Process tagged services
"Tagged services"
Way to flag services
"Bundles"
Symfony's answer to plugins or modules
Each bundle includes a class implementing the BundleInterface which allows it to interact with the container
Mention this because we kind-of use it in core, so modules can interact with container
Small tangent: Symfony's event dispatcher
Olays an important role in the app flow
Can be used to dispatch any kind of event
Alternative to hook system
Listeners are notified when event fires
Subscribers are classes that provide event listeners for different events.
Compiler pass is used to register all subscribers to the dispatcher using service IDs / tags
Holds reference to service container
Can therefore instantiate subscriber services with dependencies
DI in Drupal 8
Default services:
Default DB connection ("database")
Module handler ("module_handler")
HTTP request object ("request")
Two ways to use core's services:
Procedural code: use helper: Drupal::service("some_service")
Write OO code and get wired into the container
Implement EventSubscriberInterface
Write a service definition and add "event_subscriber" tag
How do I get my controller wired in?
Hotly debated in Symfony community
Controllers have dependencies on services
Recommended way in D8 is not to make controllers services themselves, but to implement a special interface that has a factory method which accepts the container
Not always full ports of the corresponding modules.
Moved to core:
Entity Reference, because entities are everywhere.
Date and time
Link
Missing: internal URLs
Missing: attributes
E-mail and telephone
e-mail validation
no anti-spam report
CMI
field_config, field_config_instance gone away , you can deploy through CMI
Field and instance definitions in YAML files
Human-readable
No more serialized storage
Can be deployed between environments
Deleted fields and instances are kept "in state" until purged
Don't hack your active field config
Hacking your active views config is usually fine, because it's all run-time stuff
But in the case of the Fields API, there's data as well as run-time stuff
API impact: CRUD
D7 field_create_field(), field_update_field(), etc are replaced with regular entity API
EntityDisplay
In D7, you had to deal with big arrays of display settings that were scattered all around. In D8, there's a ConfigEntity called EntityDisplay that manages the display, and settings are stored in CMI YAML files.
EntityFormDisplay
Similar to displaying Entities, there's a EntityFormDisplay to let you configure the form
Opens the door to "Form modes", similar to "Display modes"
Still in progress — if it doesn't make it into core, it'll be in DS
Plugins
Pluggable, custom Widgets, Formatters and Field types.
Everything that was a property is now a field!
Every entity property on an entity is now a "field"
In D7, base fields ($node->nid, $node->title, $node->uuid) were not configurable; now they are, called "extensible entity types".
Haiku
No content without entity
A property on config
Fields are everywhere
Outlook
Make formatters and widgets work on all fields
configure the formatter of the node title
Re-use existing widgets / formatters with base fields
Necessary for in-place editing
WIP
Entity field API
Field values are objects
Field ítem clases === Field type plugins
Computed fields
Computed fields are evaluated on the fly.
Improved DX
$node->body[LANGUAGE_NONE][0]['value'] is now $node->body->value or $node->body[0]->value
There are dedicated helper objects for dealing with translated values.
Know your fields
There are ways to find metadata about:
Fields
Field items
Field item values
The metadata contains:
Label
Description
Data type
etc
TypedData
Foundation of the new Entity API
A way to express metadata in Drupal
Plugins include:
primitives (stirng, integer, etc.)
field types
entity types
Validation
Decouples entity validation from form validation
Builds upon Symfony Validator
Validation constraints are classes…
Symfony Validator already provides lots of useful constraints
Discovery by the D8 plugin system
Built into the Typed Data API
To be leveraged by the Entity API
EntityFormController will map violations to form errors.
Hidden widget
There is now a "hidden" widget in core, applicable to any field type
Decoupling the code (attacking the spaghetti problem)
Create the dream API for each component, and slowly refactor around it (use the APIs rather than the components themselves) — called the Facade pattern
With Symfony2, the new PAI of the Facade pattern is a service
Create a bundle for every module you identified
Create a service for every bundle you now have
Migrating model and data
A typical MySQL database is highly coupled
Classical solution is syncing two versions of the data
Can be done with ETL tools like Kettle or custom code
To avoid headaches, only write to one DB! (ideally the new)
You cannot avoid scripting this!
Even on non-progressive rewrites, it would be suicide to not regularly test data migration
You need to script the migrations from the old model to the new and use them every day on your new developments!
Good rewrite from scratch should look like a progressive rewrite.
Sometimes non-relational databases make good new databases.
Progressive rewrite relies on decoupling, just like scaling. Non relational DBs make partial evolutions easier in the long term
In a document DB like Mongo you can have the same collection data which coexist in different versions
To migrate them just in time, use /** @MongoDB\PreLoad */ from the Doctrine ORM.
The future
Theodo Evolution - R&D project started in 2013
Aims to solve all these issue to make app rewriting agile again
Theodo is getting ready to open-source a ZF1->SF2 demo
Want to eventually create a SF1->SF2 demo
Work in progress: real-time sync from PHP+MySQL to Symfony2+MongoDB
2013-05-23 09:45 — 10 Lessons from a traveling Symfony2 Circus
@weaverryan
github.com/weaverryan
Intro — Reasons to be happy as a Symfony developer
PHP: We, the unexpected generation of quality.
* Interfaces
* DI
2. Cooperation:
* Symfony omponents
* AWS SDK
* Drupal
3. Interoperability
4. PSR
* Instead of using Symfony's logger interface, use Psr\Log\LoggerInterface
Symfony is moving really quickly
Most active project on Github
Symfony - Why we haven't all retired to a beach yet
1: Symfony framework is too hard
How we see symfony:
Front controller
Container built
Kernel handles request, fires events
Routing returns attributes
Resolver finds the controller, gets its args
Controller is executed, returns a response
?
How a newcomer sees it:
app_dev.php (wtf is this?)
…
Profit
Symfony framework is not hard!
Symfony frameworkisms
Namespaces
Configuration
Directory structure
Shortcuts
The level of code shown for advertising should be short, the level of code shown for teaching should be easy-to-understand
Hardest part:
Directory structure
Frameworkisms
First
4 hours
What is Symfony?
A Routing-controller request-response framework for making web pages
A bunch of optional objects to help you build that page
A DI container with optional pre-built services for your convenience
Optional shortcuts and other Symfony-isms that speed up development.
Teaching:
Avoid "Frameworkisms"
Do things the long way, then opt into shortcuts
Start small (Silex!)
Remind people that routing+controller is 50% of
2: Symfony framework is too fat
The smaller we make Symfony, the faster a user will understand it
Only really need FrameWorkBundle, TwigBundle, DoctrineBundle and the custom code.
Opt into your level of Shortcuts / Smyfonyisms
3: There be WTF on your timeline
Initialization time - building and booting the container
boot bundles, ~1-5ms, affects prod
Rebuild container, ~1-300ms, does not affect prod.
OK, build container, ~500ms-seconds, does not affect prod
Instantiate container, <1ms, affects prod
In other words, if you see your initialization time increasing, it's probably just the dev environment.
kernel.request.loading
Amount of time it takes to instantiate your listeners.
How lightweight are your event listeners to construct?
Never do work in your listener constructor!
Solutions:
Inject the container, so instantiating your object is very simple
Lazy services (Symfony 2.3), so you get "Frankenstein" objects until you need to work with it.
If a service is heavy to instantiate, there's no good way to see that yet
Heavy instantiation is hidden in event loading statements, the firewall and the controller
Having 1000 services is okay, but it's not okay to load all of them for every request (because of dependencies)
4: ACLs
How do I enforce that a comment is only editable by it's owner and the super-admin?
ACLs create new generic table that stores relationships between objects, users, and what a user can do with the object
Written to be highly efficient
Don't think you should actually use it.
The logic for users editing a comment is very simple; ACLs are overkill
Use voters instead
Voters can deny, grant or abstain from voting.
You can pass an arbitrary object to all the voters
Therefore, write your own voter!
Invent your own attribute to act on
Decide which objects will vote
Unit test your voters!
Write simple business logic in a voter
Use existing relationships to enforce authorization
Enforce in your controller at a really high level
Performance implications
When the firewall runs, all the authentication listeners and voters get instantiated
Make sure they are very lightweight!
5: Get wild and crazy and simple with form variables
Every form field has variables which give you everything you need to render the form
Most form_* vars take an array of variables as arguments
You can just dump the form variables in your twig!
There's a dump function in twig to help you
6: Get to know HttpKernel
At the heart of Drupal
Read Fabien's "Create your own framework" series!
7: Play telephone with ESI
Use ESI
Hard to learn because you can't see what's happening
Use AppCache to debug and develop and the X-Symfony-Cache header - it'll tell you what's going on.
You can copy those _fragment URLs into your browser and see the individual fragments!
8: SOA and call it a day
9: Moar decoupling
10: ???
Homework
Remove layers, get down to basics
Emphasize Service Oriented Architecture over Symfony
Challenge Symfony, share your solutions.
Sit with a Drupal developer at lunch today (and tell them about composer).
2013-05-23 10:45 — Functional testing with Symfony 2
@qafoo
Functional testing is just one part of testing
Unit tests (fast)
Integration / Functional tests
Acceptance tests
System (end-to-end) tests (small)
Unit testing
Unit testing is testing a very small component of the application, not looking at anything else.
Should be small and fast to run
Can be hard to teach
Sometimes it helps to start writing functional tests - then you don't have to learn how to separate dependencies.
Use multiple feedback cycles…
Developer makes unit tests pass and refactor.
Project owner facilitates acceptance tests and developer makes them pass.
It's important to find a good mix between slow functional tests and fast unit tests.
Symfony functional tests
Looks at application as an HTTP black box.
Use a browser (Client) for interaction.
Assertions on HTTP responses
Integrates into PHPUnit with WebTestCase
There is a Symfony2 extension for Behat
Implementation
Class with test(s)
phpunit.xml file to specify which tests to run.
Symfony2\Component\BrowserKit\Client object
Only really used for testing
Simulates browser sessions, cookies, and history
Implemented by Symfony HttpKernel
Wraps around your application kernel
Simulates HttpKernelInterface::handle
See also @igorw's session "The HttpKernelInterface is a lie"
Goutte…
Testing "over the wire" using Guzzle HTTP client
Time-travel back to @mtdowling's session yesterday
Client#request
You can manipulate the:
HTTP method
relative URI and query string
POST parameters
Upload files
Headers and environment variables
POST body string
DomCrawler
Simple API for HTML/XML traversal
Similar to jQuery API :)
Two traversal languages:
XPath
CSS Selector
You can also traverse with a PHP API.
Client::request returns a Crawler instance
Click links and submit forms
The Client Request and DomCrawler allow you to click links and submit forms
Lets you use the Client like a real browser
Crawler and Client interaction
Benefits:
Tests in terms of user-interaction.
No URL hardcoding in the tests.
Test setup configuration
How do you deal with the stateful-ness of your application?
Shared fixture versus Isolated fixture
Shared fixture:
All tests read/write on the same data
No isolation of tests
Requires many datasets / rows
Order of tests is important!
Setup / teardown only happens once.
Faster
Isolated fixture:
Every test uses it's own data
Isolation of tests
Slow test-setup
Micromanagement of fixtures
Database setup for tests
When do I create this?
CI requires automation of schema creation
Creating a database is very expensive
Do we really need to re-create the entire schema for each test?
Decouple the database schema from the tests themselves… tests should assume the database is set up correctly.
Use SQLite or a "real" database?
SQLite can run in memory! (no I/O overhead)
But is SQLite compatible with your application?
Share the database connection between every test.
How do I work with security so I can test?
Options:
Use real authentication — in every test, visit the login page, enter the credentials and log in.
Disable authentication — works if your application doesn't depend on the user object or use the isGranted() method. But, it changes the environment too much; and you can't test security things.
Change authentication strategy — using HTTP Basic Authentication. Favoured.
How do I load fixtures?
Different solutions:
SQL dumps
Easy to maintain
Simple
PHPUnit DBUnit XMLs
Easy to use
May be difficult to set up with Symofny
Doctrine / Propel fixtures
Makes sense if you're using them for something else (initial installation)
Keep it simple
Golden rule for Database Testing
Don't use tearDown() to reset (database) fixtures!
It's helpful to look at the current state of the database when something goes wrong!
Behat and BDD
Gherkin language describes acceptance criteria
Mink is a gherkin language for browser interaction
Symfony2 Extension using BrowserKit
Very useful!
Integration tests with container
Using thrid-party code requires integration tests
Don't trust their API! :)
Caution when mocks simulate wrong behaviour.
Examples
Code relies on INSERT +ORDER BY of database
WebService breaks format or has unreliable uptime
Symfony Container is case-insensitive on service ids.