March 3rd, 2011

Liveblogging Symfony Live Paris

Josh Holmes The Lost Art of Simplicity josh.holmes@microsoft.com joshholmes.com (This guy is taking a philosophical tack and is not here just to shill for Microsoft products, good talk so far.) An American living in Ireland. "Had to go to France for a drinkable cup of coffee." Twitter: your first thought was "I could have built that in a weekend," right? "Simplicity is the property, condition or quality of being simple or uncombined." "Absence of peretense, with a clarity of expression" How was dinner organized last night? Once it would have been a tremendously long email thread etc. Instead last night three guys asked a question on Twitter. "When someone asks you for a number, do you build a report writing system?" [A lot of hands go up] "The report writing system is a fantastic thing, when that is required. But what he really needed was a number. He didn't need to write 10,000 reports. So the first time he asks for a number, give him a number. When he asks for a different number, give him a number. On the third time, automate that request." [YES! This is the punkave philosophy on generalizing and automating stuff. Gather enough experience to be sure what is really desired] "Our terror is that our user will solve the problem themselves in Excel. Why is that terrifying to us?" "It takes a long time to make something complicated simple, but if you do, it will work without problems for a long time." - F. Andy Seidl, http://faseidl.com/ "Did we deliver this in time for his lunch?" "No. What is the average length of a project in our industry?" [Audience: one month longer!] "The average project fails. The average length of a project is 18 months, at which point they have decided if it is viable or not." "Read the Innovator's Dilemma" "Disruptive innovations are almost never the result off technological breakthroughs but are instead recombinations of existing and often inexpensive technology in forms the former market leaders don't pursue." - Clayton Christensen, author of "The Innovator's Dilemma" Now he's talking about cloud computing and standing up test servers in a hurry on a cloud platform rather than wasting time building out physical servers for projects that might not even be a good idea. Mentions Microsoft's Azure platform Making the case that cloud computing is a breakthrough combination of technologies, not a new technology. "Simplicity is not the goal. It is the by-product of a good idea and modest expectations." - Paul Rand "We need to be very careful about the lure of complexity. We should not fall into the trap of thinking that if it's hard to design, it must be good; that if it's using the latest technology, it must be good; that if all our friends think it's really cool, it must be good." - Gerry McGovern "If you have a business case to use jQuery, good for you. Everybody else is using it because the hipster douchebag at the Apple store told them to." Use what you need, not what is cool; use new technology because it will help you develop faster, more maintainably, more readably, etc. [This makes me think of our current situation with git at P'unk Avenue. Our svn workflow kicks ass, so why should we move? "Because everyone else uses git" is a terrible reason. "Because it is faster and better and makes it easier to collaborate with third parties" is a good reason, but we need to work out how to make it true in practice. We will move to github for Apostrophe 2, but there's not much value in migrating a Symfony 1.4 plugin to it for us] "What is the difference between a class and an object?" Don't hire a guy who can't answer that. The simple answer from the audience: "an object is an instance of a class." Nice, I was going to try to answer it by defining a class first, which is much more awkward "How many sort algorithms do you know? Which ones do you use in which situations?" "When a thought is too weak to be simply expressed, it's clear proof that it should be rejected." - Luc De Clapiers [Mmm... too weak to be expressed in an appropriate language maybe. There is a place for discrete cosine transforms in making pretty videos play] "When my wife asks how to do something in a particular application, and my answer is "blah blah blah blah blah blah blah" [many steps], I know it's [not a well-designed feature]

"I apologize for the length of this letter, but I didn't have time to make it shorter." - Blaise Pascal Josh says he uses jQuery for most front end UIs, but sometimes straight JavaScript is simpler, and sometimes other frameworks do it better. Don't be stunned by the shiny "Learn more than one JavaScript framework because it will make you better at jQuery" "How many know more programming languages than just PHP?" [Almost all hands go up] He's listing the many languages he's used in production over the years, pretty long list "I study a lot of other languages, because it helps me understand the fundamentals of what I'm doing, makes me a better programmer." This morning Ryan was talking about how studying Symfony 2 has improved his Symfony 1.4 code. Talking about banking software. "The application we write today has to exist in 30 years and talk to other applications then." REST, service-driven design. "If you find yourself talking more than walking, shut up, cut the vision in half, and launch it. You can always fill int he gaps later." - Jason Fried Launch it. Launch it early. Get people using it. Trying it. How do you get the whale? One bite at a time. Get feedback. Build your systems in small, simple layers and the end result will be greater than the sum of the parts. "How many people are designers in the room?" [One hand] Aesthetics. "We can't just let [designers] have free reign because they will create something that is absolutely beautiful and unusable. [On the other hand?] we in our industry want everything 100% reusable. A 12-inch buck knife can open cans of soup and letters and other things, but is that what I need?" Something purpose-designed to open a can of soup, please. [But isn't this exactly what designers are good at? Maybe we mean different things when we say designer... at P'unk Avenue designers aim to make everything iPhone-perfect for its role, and developers try to nudge it back a little bit toward the reusable end so we can ship someday. It's a good balance (: ] Good talk.


Application Security, Dependency Injection, and AOP in Symfony2 Johannes Schmitt

Application security

Quick review of Symfony 2's separate firewall and authentication layers

Authentication process always involves the same steps regardless of the backend you authenticate against

Accessing a protected resource Application redirects to login form Entering your credentials Sending credentials to the server Validating credentials Retrying original request

There are different trust levels He is moving fast. He points out many types of authentication listeners that can be used to implement authentication in Symfony 2. Form login, HTTP basic/digest, X.509 certificate, etc. You can add your own (Shibboleth...)

AdvancedAccountInterface isEnabled() isAccountNonLocked() isAccountNonExpired() isCredentialsNonExpired()

If any of the above return false the user will not be allowed to log in. You can implement this interface yourself

Security component automatically takes care of hashing credentials before comparing them to password coming from database.

MessageDigestPasswordEncoder can use any algorithm supported by hash(), automatically encode with base64 instead of hex, etc.

Authentication trust levels

Anonymous Trust Level (you surfed in) Remember-Me Trust Level (no actual contact info or username, just a cookie) Full-Fledged Trust Level (all users who have submitted their password or equivalent to verify their identity)

Trust level is used to implement multi-tier security. (In our experience it's important to distinguish "this person made an account for themselves" from "the organization actually knows who this user is." I suppose it depends on the site. Can we add more trust levels?)

Authorization

Attributes are primitive strings Attributes may be simple roles, or have more complex meaning The meaning ofa ttributes depends on the implementation of the AccessDecisionManager (which you can swap out = interfaces)

Authorization decisions are reached through a voting process. The access decision manager receives votes from the role voter (ROLE_), the authenticated voter (IS_AUTHENTICATED_FULLY, _REMEMBERED, and _ANONYMOUSLY) and the ACL voter (CREATE, VIEW, EDIT, etc.).

(The ACL implementation is interesting. It looks good, but does it scale if you need to pull information about many ACLs? They have used direct queries to implement it which is a good sign performancewise)

Custom voters allow you to add more meaning to existing attributes.
class SuperAdminVoter implements VoterInterface
public function vote(TokenInterface $token, $object, array $attributes)
{
  foreach ($token->getROles() as $role) {
    if "IS_SUPERADMIN" === $role->getRole() {
      return VoterInterface::ACCESS_GRANTED;
    }
  }
  return VoterInterface::ACCESS_ABSTAIN
}
Very elegant. Voters can abstain from judging where they don't have the knowledge to rule something in or out.

When using the affirmative strategy the access decision manager grants access as soon as there is one affirmative vote. If no one grants access, access is denied.

The second strategy is the unanimous strategy. Denies access as soon as one voter denies access. Thus you must have the role, the ACL and the authentication status.

The consensus strategy is based on majority rule. (When would this be a sound strategy? I don't have to use it, but it's odd)

Default is the affirmative strategy. (This is only OK if you never have roles stick around after logout... depends on how roles are stored)

Unanimous has best performance for saying "no"

The consensus strategy has equal performance for saying "yes" and "no" but I find it difficult to imagine when it would be secure

Web request authorization is used for very general checks... URL, controller, etc. Easy to set up, access roles inc onfig file. Does not allow fine-grained restrictions. Similar to security.yml in Symfony 1

The SecurityExtraBundle makes it easier to create proxy classes with the requested security checks built-in:

namespace SecurityProxy; class BlogController extends \BlogController

(This is the decorator pattern at work)

Annotations offer many benefits over plain PHP code authorization. Annotations avoid boilerplate and are less error-prone, withc lear separation of business functionality and security concerns (is this true when annotations are inline with code?). Easy unit testing too. Plain PHP code does have a more obvious control flow.

Domain access control allows you to make decisions not only on who is requesting access, but also on the object requested. (This is something we had to build ourselves in Apostrophe and we look forward to being able to leverage this)

Pros and cons presented. (Why? If you need object-specific access rules, you need them)

Fine-grained control, not coupled to the web request and thus affected by route changes and refactorings, etc.

The ACL system is decoupled from your domain objects. Domain object (blog post, comment, user, etc.) has exactly one "object identity," which has an access control list with many entries. These point to security identities based on roles and users

ACL system has custom attributes for access checks: VIEW, CREATE, EDIT, DELETE, UNDELETE and OWNER (allowed to do any of the above). (Not sure why OWNER isn't implemented via roles...) This is similar to the "userHasPrivilege($privilege)" method we implement on pretty much all models in our projects that require access controls on a per-object basis. He's using the same names etc.

if ($this->context->vote('EDIT', $post)) { throw new AccessDeniedException(); }

Or annotate:

@SecureParam(name="post", permissions="EDIT")

(Pet peeve: why uppercase? It's a pain)

Great stuff! Time to get ready for my own presentation (:


Next up: Apostrophe! That's me, so I can't liveblog it, but here are my slides:
View more presentations from tompunk.

I'm back! There were a few repeat presentations from San Francisco, coupled with a lack of functional power outlets or wifi and an overcrowded room for the second track of presentations I hadn't seen before. So I went back to the hotel and fixed an Apostrophe bug relating to internationalization, since that is a hot-button issue for the Paris attendees.

Then I almost fell asleep on the comfy hotel couch... jet lag is dangerous!

Now it's time for Bernhard to regale us on the topic of forms in Symfony 2. To be followed by Fabien's much-anticipated keynote speech. I'll be liveblogging both. One good way to stay awake (: I managed to get on the wifi and have the brightness allll the way down. We'll see if I make it through two hours.


Bernhard Schussek Leveraging Symfony2 Forms

Software architect in Vienna. Audience clapping for slick dude headshot slide.

"Student of software engineering."

Symfony since 2006.

Agenda: introductory example, the form config class, form processing, fields, useful features

Evolution of symfony 1 forms. 2.5 years of development.

Fixes most of its problems: embedded forms especially. Also made widgets more reusable (I thought that part worked okay, but embedded forms, yipes, broken yeah).

Referenced quote about simplicity and its long term payoff from earlier presentation today.

Service Oriented Architecture

Applications provide services Services are interchangeable Can be consumed by different actors: humans, machines (tests, APIs), etc.

Forms don't contain business logic. Not at all. Services do.

Forms are a means of communication between human and machine, period

Twitter is @webmozart

Example: online sausage shop

You: request /sausages/order. Server: here are some sausages...

Consumer might be a dog, or an intermediary shop reselling to dogs...
class Order
{
  function setName($name);
  function setAddress($address);
}
Flow of information

The dog know's he's Max. Somehow $order->setName('max') has to happen.

The order form facilitates this. A form renders fields (name, address, amount) and calls setters on some service or entity - some PHP object. It doesn't know what they do

In reverse: the form calls getters on the provided object.

The Form Config class

There is a config class for each form
class OrderFormConfig extends AbstractConfig
{
  public function configure(FieldInterface $form, array $options)
  {
    $form->add('text', 'name')->add('integer', 'amount');
  }
}
"Why not a simple OrderForm class like in Symfony 1?"

OrderForm cannot be put into the Dependency Injection Container, but OrderFormConfig can. (Why and why not?)
<service id="form.order" class="OrderFormConfig">
  <tag name="form.config" alias="form.order" />
  <argument type="service" id="form.factory" />
</service>
public function getIdentifier()
{
  return 'form.order';
}
(Yipes. Seems like a lot for the minimum requirements of a form.)

Workflow:
$factory = $this->get('form.factory');
$form = $factory->getInstance('form.order');
$form->setData($order); // Object with getters and setters
if (post) { $form->bindReQuest($request); } // calls setters
if ($form->isValid()) { $order->send(); redirect }
Rendering can be as simple as {{ form.widget }} in Twig, but you typically template it out more
{{ form.errors }}
{% for field in form.vars.fields %}
  {{ field.errors }}
  {{ field.label }}
  {field.widget }}
{% endfor %}
{{ form.rest }}
Etc.

(What is form.rest? Does that give us fields we didn't iterate over? Yes!)

So you might output six fields manually and then the rest. And it also outputs the hidden fields.

(What if you didn't want to render some fields? Well, probably you should have removed them from the form object entirely.)

What about validation?

Validation constraints

Note: annotations have to be in comments, I am typing them naked to save time.

This is the entity class, not the form class. (So how does validation find its way back to the form?)
class Order
{
  @check:NotNull
  @check:AssertType("string")
  @check:MaxLength(50, message="Long name, dude...")
  private $name;
}
Text input

$form->add('text', 'title'); $form->add('textarea', 'content'); $form->add('date', 'publishAt');

The date selector is nicely localized in Symfony 2. (And easier to override with a DHTML gadget, I gather.)

Country selector

$form->add('file', 'profilePicture');

Uploaded files are sticky on errors! (This is nice and replaces aValidatorFileUpload. How about preview for image uploads that are not in error although other fields are?)

$form->add('repeated', 'email');

Core fields

entity, birthday file, checkbox, hidden, choice, integer, collection, language, country, locale, date, money, datetime, number, password, percent, repeated, textarea, text, timezone, url

The url field is smarter and understands what to supply automatically.

Field architecture

Filters Value transformers

Filters modify a value, unidrectionally. Example: FixUrlProtocolFilter.

symfony-project.com -> http://symfony-project.com

(We do this with validators in Symfony 1, I showed an example earlier)

Value transformers

Bidrectional. Converts between two representations.

DateTimeToArrayTransformer

object(DateTime) <--> array('year' => 2011, 'month' => 5, 'day' => 1)

Example: entity field

Users sees checkboxes:

x Symfony * security * validator x container

Symfony sees:

Entity field with four Checkbox fields, with their checkbox values (0, 1, 2, 3)

The output then goes through ChoicesToArrayTransformer, mapping to a hash of checkbox ids pointing to boolean values, and then to ArrayToEntitiesTransformer, which creates an ArrayCollection (?) of just the items where the boolean is true.

CSRF protection

Protection built in, as in Symfony 1.

Field creation

Manual and automatic. Symfony2 can look at domainc lass metadata (entity class metadata?) to guess the field type and settings. For example Validator metadata and Doctrine2 metadata.

You could be verbose and write:

$form->add('entity', 'sausage', array('class' => 'Sausage');

But Doctrine already knows that sausage is a one-to-one relationship to the Sausage class in the entity class associated with the form.

$form->setDataClass('Order'); $form->add('sausage');

Done.

However we can override defaults of automatic field creation when adding the field:

$form->add('sausage', array('required' => false));

Symfony 2 forms and fields implement the same interface (FieldInterface). So a form is a field. You can nest them at will.

Embedding a form:

$form->add('form.sausage', 'sausage');

That identifier has to be associated with SausageFormConfig in your services.yml.

One-to-many:

$form->add('collection', 'sausages', array('identifier' => 'form.sausage'));

"It is possible to hook JavaScript in to add new rows and remove rows and the collection will notice that" !! Very nice

Not sure exactly how that works for adding a mix of existing related objects and new related objects.

Dynamic inheritance can be implemented by implementing getParent in your config class. Forms and fields can inherit each other. EntityChoice fields inherit from the normal Choice field even though they do not do so via PHP class inheritance. (Decorator pattern)

Form themes are normal Twig templates. Blocks for each field type.

{% block textarea__widget %} <textarea {{ block('attributes') }}> {{ value }} </textarea> {% endblock textarea__widget %}

There are two themes in the core, div_layout and table_layout. Configuration in the DI parameter form.theme.template. "More flexible configuration options coming soon." [I would like to see a theme that emphasizes CSS extensibility, with no naked bits of text that can't be targeted by a class. I lost this argument for Symfony 2 core but the nice thing about Symfony 2 is that I can easily do it via a bundle and offer that to others.]

Questions a little hard to hear. He's working on the javascript plugin.

"What about useField()?" "That was a hack. It doesn't exist in Symfony 2. If you add a new property to a class it doesn't show up in the form unless the form actually calls add()"

Q. "When do you expect to merge your experimental branch back to symfony master and update the documentation?" A. "As soon as possible. Within the next week."

Q. [My question] "Can the javascript support for adding and removing related items handle a mix of existing and new items?" A. No, it does not handle existing items because the AJAX is difficult etc. [Seems achievable to me, I guess it could be a bundle]

Q. about HTML 5 support. A. There is "some."

Q. What about the Intl extension requirement? A. Eric (?) is working on it. Stub implementation should solve dependency problem. (I am hoping to contribute to this. I actually wrote a first pass but need to understand better what the expectations are and how to integrate with testing)

WAYS TO HELP: he is "searching for someone to implement a multiple file upload field," and also templates in PHP rather than Twig.

(A good talk. I need to see some good examples of this FormConfig stuff being worth the trouble.)

Next up Fabien!


Fabien Potencier Keynote

@fabpot is getting underway

"How many think Symfony 2 is too complex?"

[No hands, crowd not quite tuned in yet]

"OK, I'm done then, sorry"

"The problem is that Symfony 2 is not stable yet, we don't have all the documentation we should, but we're working on it. We got a bunch of new documents last week. I want to talk about Symfony 2, why I won't release it today or tomorrow or next week, and why I think that's great news for the project. I also want to do some demonstration on steps that are not well-documented and things that are documented but you probably don't know how to do them"

"I'll demo why I think Twig is better than PHP, and why annotations are good"

"Let's start with some code"

Default directory structure: app, bin, src, vendor, web. "You can change it"

In Symfony 2 we still have the conventions we had in Symfony 1, but you can change them

(I notice the data folder is gone. Probably its equivalents have all been sucked into the relevant bundles)

Showing the app kernel. "Most of the time the defaults are good enough."

Showing the config files. xml, yml, php

There is also a parameters.ini with stuff you are really likely to change: database_driver, database_host, mailer_transport, mailer_user, locale, csrf_secret. Nice addition

We use namespaces everyone in Symfony 2, all code is namespaced and follows PSR0 convention, which means each namespace is a directory name, so \Acme\Blog\Author lives in src/Acme/Blog/Author.php

This is the sample he showed us in San Francisco but he is picking up where he left off somewhat, not writing the setters and getters live (:

"Business logic should not be tied to the framework you are using." In Symfony 2 all code is in bundles, the core framework is a bundle, Doctrine integration is a bundle. If I want to create a blog I create a blog bundle. Bundles are first class citizens, unlike Symfony 1 plugins. A bundle is "just a namespace, nothing more" (well, there is a bundle class too)

namespace Acme\BlogBundle; class AcmeBlogBundle extends Bundle; (I left out a lengthy import here)

Bundles can be stored anywhere, your registerBundles() method in AppKernel.php decides what bundles are actually active.

Actions still typically correspond to (Twig) templates.

In Symfony 1 you must first add a route in routing.yml, then write an action, then write a template.

In Symfony 2, you *can* write:
public function welcomeAction()
{
  return new Response('Hello Symfony2!');
}
Although typically you would use a template.

"Okay, so it doesn't work! But I can do a symfony cc..." rm -rf app/cache/*

[Crowd goes wild]

"We will have a clear cache task, but it will be different because the Symfony 1 cache takes a long time to clear and blocks many users." (Earlier it seemed there would never be a need to manually clear the cache in Symfony 2, but of course stuff always comes up...)

"The better way is to move the old one and let Symfony create a new one and then take your time removing the old one" (Like the "just change a symlink" deployment strategy)

[Something just isn't quite working.] "OK, I know what to do. You will trust me."

"Unable to find the controller for /create, check your route configuration"

"I made a mistake. I updated the Symfony code to the latest one, which was a really bad idea"

HA! DocumentRoot was pointing to some other project. An audience member nailed it. vi follies ensued

"Hello Symfony2!" is finally on the screen! Victory!

You can preface all routes for individual actions that were created with annotations like:

@extra:Route("/welcome")

By setting an annotation at the class level:

@extra:Route("/blog")

You can describe parameters in URLs with annotations:

@extra:Route("/post/{id}")
public function viewAction($id)
{
  $post = whatever...
}
But better:
public function viewAction(Post $post)
{
  return new Response('Post ' . $post->getTitle());
}
That works because there is an annotation listener (?) in Doctrine that spots Doctrine entities in the arguments of actions and translates recognized columns to objects of that class.

You can specify the use of templates in a non-verbose way:

@extra:Template()

You can specify a particular template:

@extra:Route('/create', name="create_post") @extra:Template("AcmeBlogBundle:Post:new.reg.twig")

More great annotations:

@extra:Route("/post/{id}", name="blog_post") @extra:Cache(maxage="600")

Q. Where is the responsibility to issue a 404 handled when these lookups fail? A. "I forget, but you can say $post = null so if it does not exist then your code is prepared to deal with null and we don't do it for you" (not clear if this works yet)

Mentions the option of compiling your routes to Apache rewrite rules since Apache is much faster

Some confusion about the meaning of the Route annotation and the fact that it doesn't function like a relative path, it's appending components.

The big news is that Symfony 2 will not be declared stable at the conference or this week, there is too much still going on.

All in all the functionality seems to work as advertised when you are not coding live in front of hundreds of people (:

"When will we release a release candidate? The goal was today or tomorrow or after the hacking day. We won't. First we are not ready yet. Why? A huge number of people are contributing now while a year ago it was just me. Fixing bugs, having discussions about the architecture of Symfony 2, how to do things the right way. Also because this is the foundation of the 2.x version so we will have the same architecture for five years perhaps. We still have a few things to discuss about, the form framework is one of them. Very important to make sure the architecture is the right one."

"The good news is that we have so many people involved that it would be a shame to discard their ideas."

"200 pull requests in just three weeks that I've actually merged into the code"

"We haven't broken backwards compatibility for perhaps two weeks"

Q. When do the annotations get compiled / do annotations slow down my code / does it matter if I use YML vs PHP / etc.?

A. It makes no difference. It all compiles to PHP (in app/cache)

"If you want to contribute we have documentation on how to contribute to the code on the website, if you don't really know git that well, we have an explanation of how to fork and branch and send pull requests. Crazy."

Questions about hiphop, Facebook's PHP-to-C++ compiler. "No, it is not PHP 5.3 compatible." "But will you make it compatible if they fix that?" "No. I don't care. It's very hard to use. It probably matters if you are Facebook. Is your site as big as Facebook? HTTP cache is a much bigger improvement. For the average user, 50% (CPU time) improvement is nothing. Configure Varnish properly." (And APC!)

Tomorrow he'll be illuminating us about the glories of HTTP cache, in particular the ability to cache small parts of a page in conjunction with intermediate proxy servers, including Varnish and a built-in fallback implementation.

Q. "One of the main goals was to reduce the magic. I was in the training yesterday. $this->get('orm.entity_manager') returns something that is not typesafe. I have no idea what it is. It feels like magic. Are there plans to have convenience functions so I have interface-safe methods?"

A. "It's not about magic really, it's about scope. Killing the magic means everything is explicit. We don't use __get, __set. None of that in Symfony 2 because it is a nightmare to debug. When you get the service there is no typing because you can't know what class it is (because the point is to offer multiple implementations)." The controllers use ->get, nothing else has to (?). (Some of this involves issues I haven't really made contact with yet)


That's it for today! Thanks for reading.
Check out another article
February 8th, 2011
Liveblogging Symfony Live
By
January 31st, 2011
U-Turns $1