No Piece of Cake

Welcome to all of you on my blog! Whether you are a PHP developer, a symfony power user or just getting started in object-oriented web application development – this blog is for you.

With my first post, I want to talk about symfony and where this blog is heading. And I don't mean "symphony" as in music – no! Scrap that "ph". It's "symfony", one of the greatest web frameworks that has been developed in PHP5 so far. (If you ever dare to write symfony with "ph", tiny kittens will suffer!)

Symfony is by a large part a product of Fabien Potencier'sg ingenuity and dedication. I want to deeply express my respect for his work at this point. The framework is a very well-thought piece of work and can immensely boost your productivity.

The amount of features and the complexity come with a drawback though: It takes a lot of time to learn how to develop efficiently with symfony. Many PHP developers are overwhelmed by the object-oriented concepts applied in the framework. Many call for a simplified version and I must agree that symfony suffers some usability problems lately. But why is that and how can we improve the current situation?

The Average Developer

Let's face it. The average PHP developer knows nothing about "Design Patterns", "Unit Tests" or "Refactoring". In the course of my job I had to interview a lot of potential employees. Not only once I faced total ignorance when I talked about these or similar topics. Even basic understanding of PHP could not be taken for granted! What struck me most was that many of these people programmed PHP applications for a living, some of them for more than a decade.

What happens when you confront these developers with symfony? It's like when my grandma got a computer. It took so much time to teach her using the mouse that telling her about how to write a letter would just have made no sense.

In my opinion, every PHP developer who is familiar enough with the language itself should be able to learn symfony. If we want to raise the general quality and standards of PHP applications, we must reach these developers. Lowering the burdens for them to learn the framework is crucial for its future community.

Back To Basics

The excellent documentation of symfony obviously is a great help. I remember when I read the symfony documentation for the first time. Its great writing, the presented simplicity of powerful features really struck me. Every couple of pages I was astonished by the fact that programming in PHP could be so easy! I was torn apart, because I had to read on while I just felt the urgent desire to start programming. Anything.

Unfortunately, this feeling of simplicity quickly fades away when you get started. Questions over questions arise. Where do I put that code? What method again should I use? Is it better to write it this or that way?

Now this is the case for every major programming library. You can't expect to read a little of the documentation and be an expert (in this case it probably wouldn't even be worth to use the library). What you can do is look at the code of others, extract the common ideas and find out for yourself how to best organize your code.

What you are searching for is generally referred to as "Best Practices". The Jobeet tutorial did a great job at presenting such best practices. But further than just being documented, the described concepts must be simple enough for you to remember them next time you need them. And this, partly, is symfony's weak point.

Forms By Example

To demonstrate my argument, let's look at how to create a basic profile form in symfony. This form should allow to edit a user's name, email address and password. The name should only be editable by administrators, while the password should be entered twice to make sure it was entered correctly. The maximum length of the name is 15 characters and the password should not be the same as the name.

Let's start with the constructor. We need to alter the form depending on the authentication of the current user session, thus we pass the user object to the form.

// lib/form/doctrine/ProfileForm.class.php
class ProfileForm extends sfFormDoctrine
  protected $user = null;

  public function __construct(sfUser $user)
    $this->user = $user;

  // ...

So far so good. Now let's configure the form's appearance and validation logic.

public function configure()
    'email'          => new sfWidgetFormInput(),
    'password'       => new sfWidgetFormInputPassword(),
    'password_again' => new sfWidgetFormInputPassword(),
  $this->widgetSchema->setLabel('password_again', 'Password (again)');

    'email'          => new sfValidatorEmail(array(), array(
      'required' => 'Please enter your email address',
    'password'       => new sfValidatorString(array(), array(
      'required' => 'Please enter a password',
    'password_again' => new sfValidatorPass(),

  // implement custom validation logic
  $this->validatorSchema->setPostValidator(new sfValidatorAnd(array(
    new sfValidatorSchemaCompare('password_again', '==', 'password', array(
      'invalid' => 'The passwords must be equal'
    new sfValidatorCallback(array(
      'callback' => array($this, 'comparePasswordAndName')

  // administrators may enter the user name
  if ($this->user->hasCredential('admin'))
    $this->setWidget('name', new sfWidgetFormInput(array(), array(
      'max_length' => 15
    $this->setValidator('name', new sfValidatorString(array(
      'max_length' => 15
    ), array(
      'max_length' => 'The name must be 15 characters or longer'

Phew, there's already a lot going on. We need to know about the widget schema and the validator schema. These are tricky, because for example setWidgets() is called on the form directly, while setLabel() is called on the widget schema. Don't get that wrong.

Then you need to pass the error messages that are displayed when the fields are not filled out, because the default "Required." is not really usable for a customer.

The post validator is especially tricky. Once you understood the concept, you need to remember to set it on the validator schema, use sfValidatorAnd with a couple of braces thrown in and mix it with sfValidatorSchemaCompare and sfValidatorCallback. Not easy.

The last thing is to add the "name" field and validator only when the user is administrator. This especially causes complications, because then the field "name" is not always present in the form values, which requires extra checks and template customizations from you. We need to configure the "max_length" constraint twice, once on the input field and once on the validator. Again we override the error message, because the default is not usable.

What is missing now is the method that checks that the password and name differ:

public function comparePasswordAndName(sfValidator $validator, array $values)
  // if the field "name" is not editable, it does not exist in $values
  if (array_key_exists('name', $values) && $values['name'] == $values['password'])
    $error = new sfValidatorError($validator,
        'The password must not be the same as the name');

    // throw an error schema so the error appears at the field "password"
    throw new sfValidatorErrorSchema($validator, array('password', $error));

If you want to set an error for the form, you basically need to throw an sfValidatorError, which behaves like an exception. Because the error should be displayed at the password field, we need to wrap it with an sfValidatorErrorSchema instance and associate it with the field name "password".

This form is not yet complete. The logic to save the profile is still missing, but the above implementation pretty well demonstrates what I want to show.

Tying It All Together

If you go back and re-read the form requirements, you will see that these requirements are not uncommon even for very basic websites. Still the implementation requires you to know many different concepts. Altering the form based on the authentication status, modifying field labels, limiting field lengths, implementing custom validation logic, all of this is so common that it should be far easier. The default error messages should only need to be overridden in edge cases. Don't expect that the average developers that I spoke about will get that right.

In my opinion, symfony must strive for absolute simplicity. The easier it is to explain its use, the quicker new developers will grasp the real power that lies under the hood of this framework. Together with a well-defined set of recommendations and best practices we will be able to teach new developers and expand the community. And we will be able to help those "average PHP developers" to step into the world of powerful object-oriented concepts.

This is the goal of this blog. We want to analyze our work and present best practices to improve your development. We want to speak about symfony's strengths and learn about symfony's flaws to point out possible solutions. We want to teach object oriented concepts in PHP5. We want to investigate what the community needs and try to find answers. Of course, we also want to present our own work. And last but not least, we do want to hear your feedback.

In the coming weeks I will think about possible solutions to the above problems with the Forms framework and post them on this blog. I would also appreciate to hear your ideas.

What is your experience with this new component? Is symfony simple enough for you?