Archive for the 'framework' Category

It’s Oh So Quiet

It’s so quiet in this blog, because it is closed. Forever. No more post will ever be published, you can’t send any comment, and you can unsubscribe safely from its RSS feed. More than 8,000 unique visitors a month now have time to procrastinate elsewhere.

I have finally admitted that I have better things to do in my free time than contributing to the symfony project. So I can say goodbye to all the following, without regret:

  • Writing a book,
  • Publishing 54 blog posts here and a certain amount in the symfony project blog,
  • Reading 715 comments here and countless emails in the symfony mailing-lists,
  • Following the symfony timeline every day, and reviewing the code contributed to the framework core,
  • Developing, testing, and documenting more than 20 plugins,
  • Giving a few conferences and trainings,
  • And helping newcomers find their way in the symfony ecosystem via IRC, chat, and email.

You can’t imagine how much time all that takes. Well, that’s how much free time I get by leaving symfony completely.

Since summer 2005, my involvement in symfony has been more and more thorough, more and more visceral, and more and more painful. I did my best to push symfony in the direction that I considered to be the right one, but I failed. Symfony used to be simple, well documented, and powerful; today it’s just powerful. Long forgotten are the days where symfony’s motto was “Professional Tools for Lazy Folks”. I see no future in a project where release dates are never satisfied, where new features are released undocumented, where discussions either never start or die without a generally accepted decision, where the community is tolerated only for its praises, and most of all, where the average user is despised.

If you use any of the symfony plugins that I developed and maintained, and if you want to contribute back to their code, you should contact Kris Wallsmith, the new symfony community manager. He’s responsible for these plugins now, and will give developer access at his own discretion. As for me, I may use my commit access for modifications regarding the projects I work on, without further notice.

  • DbFinderPlugin
  • sfAssetsLibraryPlugin
  • sfControlPanelPlugin
  • sfFeed2Plugin
  • sfMediaLibraryPlugin
  • sfModerationPlugin
  • sfPagerNavigationPlugin
  • sfPropelActAsSortableBehaviorPlugin
  • sfPropelAlternativeSchemaPlugin
  • sfPropelSpamTagBehaviorPlugin
  • sfSimpleBlogPlugin
  • sfSimpleCMSPlugin
  • sfSimpleForumPlugin
  • sfSpyPlugin
  • sfStatsPlugin
  • sfUFOPlugin
  • sfUJSPlugin
  • sfWebBrowserPlugin

Redoing the web was an ambitious task. Who knows, I might still manage to do it in the future.

Chapter 10 - Forms

Dealing with the display of form inputs, the validation of a form submission, and all the particular cases of forms is one of the most complex tasks in web development. Luckily, symfony provides a simple interface to a very powerful form sub-framework, and helps you to design and handle forms of any level of complexity in just a few lines of code.

NOTICE: This document is the first draft of a methodology experiment explained earlier in this blog. It documents the sfForm framework found in symfony 1.1, but with some changes in the API and usage. As such, it describes a library that is not yet written (like that) and cannot be used to learn the usage of the current sfForm implementation. It is quite long, so you might prefer to download the Markdown version and read it offline. Being a first draft, this document is a call for comments, both about its structure and its content. And if you are interested in implementing the differences between what this document describes and what is currently implemented in the symfony framework, please contact me.

Read more »

Document-Driven Development in Practice: Rethinking sfForms

If you’ve watched or read my presentation on Documentation-Driven Development, you may wonder how to put that new methodology into action. A practical example is often better than a long explanation, so let’s see ho to apply it to the new Forms sub-framework introduced by symfony 1.1.

Not DDD

In order to use the new sfForm library, you must either read a book (not yet completely written) or dive into the source code and guess how to use it. To my mind, this is pretty much the contrary of what leads to a large adoption.

The Form framework was designed with power in mind, and reaches this goal very well: you can use it to create forms of any level of complexity, including forms embedding other forms, forms with a variable number of fields, forms split into several steps (”wizards”), etc. It is very much object oriented, so everything can be reused or overridden.

But unfortunately, in order to create a simple form, you need to learn a lot more and write a lot more code than what you used to do in symfony 1.1. The current Forms documentation describes the API and justifies its implementation. It goes very much into the details of each part of the sub-framework, and quite early in the learning process. The result - for me, at least - is that the reader feels overwhelmed by the huge amount of classes, features and options, and dismisses the whole sub-framework for being too complex.

“Let’s use that new Form stuff for complex forms and keep the current form helpers and YAML validation for everyday forms”, I hear. That’s a pity, because once you understand how the new Forms sub-framework works and accept its verbosity, there is no good reason to stick with the old system.

An Ideal sfForm Documentation

I think that a piece of documentation is missing. This piece is probably an introduction to the Form sub-framework.

In symfony 1.0, a single chapter of the book was enough to master forms for most use cases. Even if the new form sub-framework is more powerful than the 1.0 one, it should not be more complicated to learn and use in similar cases. So the sfForms introduction should be short, requiring at most one hour to read it.

After reading this documentation, an average developer should be able to use sfForms in 80% of the cases. That includes at least all the features described in the original Forms chapter of the symfony book:

  • Displaying a form
  • Available form helpers
  • Displaying a model-based form
  • Dealing with Foreign keys
  • Handling a form submission
  • Validating a form
  • Available validators
  • Repopulating a form
  • Complex use cases

The target audience would be people knowing some concepts about symfony, but not yet everything. In fact, they should know what the Chapters 1 to 9 of the symfony guide cover, not more. So some advanced concepts should probably be skipped, or explained only after the fundamental usage is clear.

This introduction should not require additional lookup in the Forms book. That means that it should be self-sufficient. It probably also means not including the justifications of the Forms implementation that you can find in the current Forms book. The reasons why the API was designed the way it is should become obvious at the end of the introduction. Expert customization and rare use cases should probably also be left aside.

The symfony 1.0 documentation introduces concepts and features in a certain order, with a precise purpose: not loading too much information into the reader’s mind at a time. In a similar fashion, the forms introduction should be a linear piece of documentation, not a set of articles that you can read in any order with hyperlinks everywhere to break the reading flow.

The forms framework is powerful, but the current form book somehow translates that into length, and verbosity. On the contrary, I think the reader should feel exalted: the documentation should put him in a rush to start using the new forms. So the forms introduction should “tell a story”, and gently lead the reader to a point where he feels he can grab the steering wheel and drive the car by himself.

API enhancements

The problem is that explaining the current API takes much longer than a single piece of documentation. That’s because of the many options available, because of the many objects to learn, and because even the simplest things (like a list of form controls) look complicated (sfWidgetFormSchema).

There is not much choice to overcome this problem. In order to write a short and readable guide to the forms sub-framework, its API must be adapted. That’s right, the API must be changed so that the documentation can be made shorter, and more usable. This is one of the principles of the Documentation-Driven Development methodology.

These API enhancements should be completely backward compatible, so that any existing application using the current sfForms implementation can continue to work seamlessly with the modified implementation. In a way, that qualifies the API enhancements as a simplicity layer on top if the existing code. As a side note, the current Forms book still remains indispensable for advanced usage.

Note that the API enhancements don’t need to be implemented before the new documentation is published. The implementation comes second, after the documentation. That’s another of the DDD principles: explain first, make it work afterwards. After all, project managers write requirements for web applications before they exist, all the time.

Do As I Do, Not As I Say

Some people are getting sick of reading me criticizing parts of the symfony framework. Well, I’m not criticizing: I’m actively improving.

Rethinking sfForms is a good example for a Documentation-Driven Development. To illustrate this methodology, I’m going to rewrite the Chapter 10 of the symfony book for symfony 1.1. That’s right, the current Chapter 10, which describes the “old way” of doing forms, can be rewritten in a similar fashion and serve for symfony 1.1.

But since the current API requires too much explanation to be used, I’m going to introduce the necessary API changes to the sfForms library. I’ll create and manage forms in a way slightly different from what the current API allows, to make it simpler to use - and to explain.

When the new Chapter 10 is published here in this very blog, this piece of documentation will be of no use since the features it describes won’t be implemented yet. But I know that writing documentation is not enough to convince people (yet), so I will Implement the API changes as a second step to the exercise. As I’m not a very good developer, any help will be welcome during that phase (contact me If you want to give me a hand after the documentation is published).

If everything goes well, the implementation of the API changes will be be released as a symfony plugin - maybe called sfSimpleForms. I hope it can lead more developers to adopt the greatest open-source Forms framework around.

Designing a CMS Architecture

When faced with the alternative between an off-the-shelf CMS or a custom development, many companies pick solutions like ezPublish or Drupal. In addition to being free, these CMS seem to fulfill all possible requirements. But while choosing an open-source solution is a great idea, going for a full-featured CMS may prove more expensive than designing and developing your own Custom Management System.

Hidden Costs

What does it cost to integrate and deploy a website based on an open-source CMS? At first sight, not much. As for every CMS, you have to design your own templates and fill your website with initial data. But there are additional costs that pop up as soon as you need a little more than just plain content management.

Think about adding a blog or a forum to a website managed by a CMS. There are modules or plugins for that, but they never provide the same flexibility as plain blogging engines such as Wordpress, or plain forum engines like phpBB. So even if the basic requirement is fulfilled by a module, you will always need - always - to adapt its code.

And this is where it gets ugly. The code base of open source CMS engines and their plugin is nowhere as good as what you can see in RAD frameworks these days. Most of them are based on a very old architecture (PHP4, no object orientation, no proper error handling, direct access to the database, etc.). That means that changing something will be very painful, and very expensive. You will encounter numerous bugs, change the blogging plugin three times because neither of the ones you tested are capable of doing what you need, you will upgrade your CMS to the latest version to benefit from this single bug fix that should save your life but then you need to change all your existing configuration…

This is as bad as it sounds. Start changing one single line of code in an application build on top of Drupal or ezPublish, to name only the two major ones, and you are in trouble. The moment you need something that is not natively supported, you enter the Dark Zone of CMS hell. You are going to spend a lot of money on development. You will never see the end of the tunnel. That is, until someone says, a few years from now, “Do we need all that crap? Let’s build something that fits our needs and that actually works”.

Making Your Own CMS

Given number of available open-source CMS solutions, building one on your own sounds like a stupid idea. But if your website is 50% content management and 50% something else, you probably need to start with a web application framework like symfony or Django, rather than a CMS. These frameworks provide plugins that do part of the Content Management job already, so creating a CMS today is like assembling Lego bricks to build something that exactly fits your needs.

Take symfony, for instance. It provides native support, or support through plugins, for:

Symfony doesn’t yet provide an Access Control List or a Workflow plugin, but you can already put all of the above together and have a pretty powerful CMS engine.

A tailor-made CMS will always have less code and show better performance than any of the existing full-featured solutions. Also, you will be able to tweak it completely, since all the components are decoupled, and built with extensibility in mind.

Your custom CMS will cost you more during the first year, but if you expect your website(s) to live longer than that, then the benefit will become obvious after a year and a half. Plugging the CMS features into other parts of the website, adding features unrelated to content management, scaling to a larger audience, replacing the database engine or the caching backend, all that will be painless.

That is, if you design your custom CMS carefully, and with the future in mind.

Environments

When you add features to an application, you need a testing environment - a place where you can check that the additions work and don’t kill the rest of the application. That means that developers have a version of the website on their desktop computer, where they change stuff. Then, they upload the application to a test server, check that everything is OK, and only then can they deploy the application to the production server. This is a very common practice, often backed up by source version control and continuous integration tools.

But what happens when a new feature is not made of code, but of data? In ezPublish, for instance, in order to define a new type of content (they call it a “Class”), you have to use the backend web interface and fill in a few forms. The properties of the new type of content are stored in the database. In order to deploy this new type of content from the testing environment to the production environment, the developers need to transfer data from one database to another - without wiping off unrelated information on the production database, such as user comments, statistics, etc.

Deploying new features in this context means executing some SQL code on each server. This is much more dangerous than just pushing a new version of the codebase, especially when the data model is made of many tables glued together in complex joins. That’s why, in many websites based on ezPublish, developers add features directly on the production environment, or repeat the configuration using the backend interface on every environment. This is either a high risk or a large waste of time.

Data, or Code?

This environment drawback tends to be a major influence over the choice of features a CMS should provide. For almost every CMS feature, you should wonder: Can the user do that through the backend interface, or do we need a programmer to add a new element? In other terms, is the feature made of data, or code?

Off-the-shelf CMS engines will almost always answer ‘Data’. My personal opinion is that it is wrong in many cases. Content types are just one example, but think about workflows or page layouts for instance. They define a complex logic that always translates to code, and giving the user the ability to change them via a backend interface means storing code in the database and evaluating it at runtime. Then you can’t use op-code cache engines like APC incriease your website performance. And deploying that to production is a nightmare.

Some companies think that most of the CMS features should be accessible via a backend interface in order to be able to enhance the application without additional developments. But this is an illusion. For one, the configuration of content classes in ezPublish is so complex that it does indeed require a PHP developer, and an expensive one, since experience with ezPublish is one of the most demanded skills in the IT market (at least in France). More features mean more development, and there is no CMS out there that replaces the power of a programming language with a web interface.

So that leads to one good rule of thumb: Design your features so that they can be made of code rather than data. That applies to elements that can be modified by a graphical user interface, or programatically:

  • Content classes
  • “Widgets” or “Components” for pages
  • Page layouts or “templates”
  • Content validation workflow
  • Tasks

Fundamental questions

The complexity of a CMS engine depends greatly on the answer you give to a few fundamental questions:

  • Can contents exist independently of a page?
  • Can contents exist at more than one place in the website?
  • Are there several views for a single piece of content?
  • Can contents have different versions simultaneously?
  • Can contents be modified in the backend and keep unchanged in the frontend?
  • Can users compose a page with “widgets” or “components” in a WYSIWYG interface?
  • Can predefined zones in a template contain more than one “widget” or “component”?
  • Can section pages have different templates?
  • Can section pages have different versions simultaneously?
  • Can users program the publishing of a section page, or of contents, in advance?
  • Can the CMS remember previous URLs for a content that changed title?

If the answer to the first question is no, then the concept of “page” and “content” coincide. You probably don’t need to develop anything, since your CMS will be quite simple.

If you answer yes to all these questions, then the CMS might take three times longer to develop than what it would be otherwise.

That’s why the idea of a tailor-made CMS is not that stupid. No existing CMS will be able to answer these questions in every possible way. But designing your own relational schema based on the answer to these questions makes sense, economically speaking. Don’t make it complex if you don’t need do, or, to put it otherwise, Keep It Simple, Stupid.

Bootstrapping the reflection

Now that you’re trying to imagine what you actually need for your own CMS, here is a glimpse of the kind of technical challenge you will face all the time.

The question turns around the concept of content types. In a CMS, you mostly deal with “articles”. This type of content has a title, an author, a summary, a body, and a few other attributes. But you probably also need to deal with some other content types, like movies, slide shows, quiz games, polls, or recipes. These content types are defined by properties distinct from that of an article. Some of them can fit in a single structure, others require several structures related to each other. For instance, quiz games require a structure for the quiz itself, one for the questions, one for the answers to each question, and one for the quiz results.

The question is: Do you store the data for all these content types in a single table, or do you create a table for each content type? The most “normalized” choice is probably to create one data structure for each. You could have an “article” table, a “recipe” table, and even a “quiz” table with foreign keys to a “quiz_question” and a “quiz_result” table. That would allow you to make queries on some specific attributes of a specific content type. You could build a custom search engine for your recipes and look for ingredients, foreign cuisine and preparation time.

But then, if each content type has its own table(s), what do you do when you have to list all the contents of a section, or worse (that happens in the backend) all the contents of the website? Does that mean that, in order to display a list of contents, you must query several tables and aggregate the results together? This solution simply doesn’t scale, and a CMS built like that will become slower and slower as you add new content types.

So that probably means that you should store a reference to each content in a separate table, with a copy of the data that is generic to all content types (like title, publication date, section, etc.). Pages displaying a list of contents would use this aggregate table, while pages displaying content details would use the specific tables.

And that means that you must find a way to synchronize the specific tables and the generic tables whenever data changes in content. That’s not a big deal, but it gives you an idea of the kind of complexity you will encounter in a large scale CMS.

A Challenging Exercise

Designing a CMS is difficult and fun, and you’ll probably do it more than once. Every CMS is different, because every content management need is different, and mostly because every customer wants more than just plain content management.

If you are a developer, whenever you meet a client that asks you for a Drupal integration, try to sell your knowledge of CMS architectures rather than a few hours of developer time. Raise the important questions, talk about the possible problems of using off-the-shelf solutions. If you ever used one of those before, you will have plenty of issues to talk about. Then, try to convince your customer to trust you into a custom development. Make it small at the beginning, so that the customer can start using it right away and refine its requirements incrementally.

This will be a very satisfying experience, and the client will thank you later for leading him on the right path. And this will give you a lot to talk about for the next CMS you build…

Validating a YAML file against a schema in PHP

As of today, there is no simple way to validate the syntax of a YAML file in PHP. But with two simple tricks, it takes only a few dozens of lines of code to build a robust validator capable of checking the syntax of any YAML file against a given schema.

The problem

YAML is much easier to write and read than XML, but YAML has no schema validation capabilities. With DTD and XSD, you can check that an XML file is correctly formatted before actually using it, and it helps debugging a great lot. Modern web application frameworks like symfony encourage the use of YAML for configuration files, but the lack of validation tool sometimes make YAML a poor choice in a professional environment.

Such a validation tool exists in Ruby, it's called kwalify. But unless you want to spend a huge amount of time translating the 6,000+ lines of code of the library from Ruby into PHP, or to run Ruby code inside your PHP application, you're basically stuck.

First Idea

Did you just read that XML allows validation by way of XSD? Well, why not use this mechanism to validate a YAML file? After all, PHP has a great XML manipulation library, installed by default, and capable of validating any XML file against a DTD or an XSD. Actually, this mechanism is already in use in symfony, since the Propel schema.yml is transformed into an XML counterpart that has an XSD.

It is trivial to transform a YAML file into a PHP associative array. Symfony 1.1 provides a class that does exactly that, and it's called sfYaml. With a little bit of recursion and a few lines of PHP code, it is also quite easy to transform an associative array into a simple XML file.

Let's use the view.yml configuration file in symfony for example. In a typical module, it looks like the following:

# view.yml
default:
  http_metas:
    content-type:  text/html

  metas:
    title:         My symfony project
    robots:        index, follow
    description:   This is my first symfony project
    keywords:      symfony
    language:      en

  stylesheets:     [main.css, top.css]

  javascripts:     [jquery-1.2.6.js, main.js]

  has_layout:      on
  layout:          layout

indexSuccess:
  metas:
    title:        Welcome to my site

Now what does it take to transform this YAML into a simple XML equivalent? Not much. A bit of googling shows that someone already worked on transforming an associative array into XML, and as it is not a good idea to reinvent the wheel, let's reuse this work.

// Transform YAML into XML
include 'sfYaml.class.php';
$yamlString = file_get_contents('view.yml');
$yamlArray =  sfYaml::load($yamlString);
$xmlString = ArrayToXml($yamlArray);

function ArrayToXml($data, $rootNodeName = 'root', $xml = null)
{
  if ($xml == null)
  {
    $xml = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$rootNodeName />");
  }

  // loop through the data passed in.
  foreach($data as $key => $value)
  {
    // no numeric keys in our xml please!
    if (is_numeric($key))
    {
      // make string key...
      $key = "unknownNode_". (string) $key;
    }

    // replace anything not alpha numeric
    $key = preg_replace('/[^a-z]/i', '', $key);

    // if there is another array found recrusively call this function
    if (is_array($value))
    {
      $node = $xml->addChild($key);
      // recrusive call.
      ArrayToXml($value, $rootNodeName, $node);
    }
    else
    {
      // add single node.
      $xml->addChild($key, $value);
    }
  }

  // pass back as string. or simple xml object if you want!
  return $xml->asXML();
}


Second idea

The result of the simple YAML to XML transformation looks like this:

<?xml version="1.0" encoding="utf-8"?>
<!-- view.yml.xml -->
<root>
  <default>
    <httpmetas>
      <contenttype>text/html</contenttype>
    </httpmetas>
    <metas>
      <title>My symfony project</title>
      <robots>index, follow</robots>
      <description>This is my first symfony project</description>
      <keywords>symfony</keywords>
      <language>en</language>
    </metas>
    <stylesheets>
      <unknownNode>main.css</unknownNode>
      <unknownNode>top.css</unknownNode>
    </stylesheets>
    <javascripts>
      <unknownNode>jquery-1.2.6.js</unknownNode>
      <unknownNode>main.js</unknownNode>
    </javascripts>
    <haslayout>1</haslayout>
    <layout>layout</layout>
  </default>
  <indexSuccess>
    <metas>
      <title>Welcome to my site</title>
    </metas>
  </indexSuccess>
</root>


The trouble here is that the <default> and <indexSuccess> tags are not real tags. That means, they do not define a class of content but a value. Same for the <unknownNode> nodes. To make sense, a real equivalent to the view.yml in XML should look like this:

<?xml version="1.0"?>
<!-- view.yml.xml, semantically correct -->
<templates>
  <template name="default">
    <httpmetas>
      <contenttype>text/html</contenttype>
    </httpmetas>
    <metas>
      <title>My symfony project</title>
      <robots>index, follow</robots>
      <description>This is my first symfony project</description>
      <keywords>symfony</keywords>
      <language>en</language>
    </metas>
    <stylesheets>
      <stylesheet>main.css</stylesheet>
      <stylesheet>top.css</stylesheet>
    </stylesheets>
    <javascripts>
      <javascript>jquery-1.2.6.js</javascript>
      <javascript>main.js</javascript>
    </javascripts>
    <haslayout>1</haslayout>
    <layout>layout</layout>
  </template>
  <template name="indexSuccess">
    <metas>
      <title>Welcome to my site</title>
    </metas>
  </template>
</templates>


The difference is that main entries are <template> tags with a name attribute, and that children of the <javascripts> element are simple <javascript> elements. This second XML file is semantically correct, because it follows a simple grammar - and can be validated.

But how can you turn the first XML file into the second? The right tool for this job is called XSLT, or Extensible Stylesheet Language Transformations. An XSLT file is a set of transformation rules described in XML. Applying these rules on an XML files transforms it into another XML file. That's exactly what you need here.

The XSLT file to turn the first view.yml.xml into the second one is quite simple:

<?xml version='1.0'?>
<!-- view.yml.xsl -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/root">
    <templates>
      <xsl:for-each select="child::*">
        <template>
          <xsl:attribute name="name">
            <xsl:value-of select="local-name()" />
          </xsl:attribute>
          <xsl:apply-templates select="child::*"/>
        </template>
      </xsl:for-each>
    </templates>
  </xsl:template>
  <xsl:template match="//stylesheets/unknownNode">
    <stylesheet>
      <xsl:value-of select="text()" />
    </stylesheet>
  </xsl:template>
  <xsl:template match="//javascripts/unknownNode">
    <javascript>
      <xsl:value-of select="text()" />
    </javascript>
  </xsl:template>
  <xsl:template match="*">
    <xsl:copy>
       <xsl:apply-templates/>
     </xsl:copy>
  </xsl:template>
</xsl:stylesheet>


Basically, this XSL stylesheet copies most of the original tags (<xsl:copy>), but does special operations for elements that should be attributes (like <default>), or that should be renamed. This stylesheet defines a "semantical correction" for the automatically created XML translation of the YAML file, and is the first step of the validation. Of course, you need to define one XSLT file for each type of YAML file you want to validate.

How to apply this XSLT to the XML version of the YAML file in PHP? Using the powerful capabilities of PHP in XML, it is extremely simple:

// Transform the XML using XSLT
// Load the simple XML transformation into a DOMDocument object
$xmlDoc = new DomDocument;
$xmlDoc->loadXML($xmlString);
// Load the XSD stylesheet into another DOMDocument object
$xslDoc = new DomDocument;
$xslDoc->load('view.yml.xsd');
// Proceed with transformation using an XsltProcessor object
$xsltp = new XsltProcessor();
$xsltp->importStylesheet($xslDoc);
if (!$xmlTransformed = $xsltp->transformToDoc($xmlDoc))
{
  throw new Exception('XSL transformation failed.');
}


Validating

Validating the semantically correct XML file is quite basic: write an XML Schema, or XSD, describing the syntax expected in a view.yml.xml. You could do it with a DTD instead of and XSD, but XSD is more powerful. Here is a simple schema defining a grammar to validate view.yml.xml files:

<?xml version="1.0"?>
<!-- view.yml.xsd -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="templates">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="template" maxOccurs="unbounded">
          <xs:complexType mixed="true">
            <xs:all>
              <xs:element name="httpmetas" minOccurs="0">
                <xs:complexType>
                  <xs:all>
                    <xs:element name="contenttype" type="xs:string"/>
                  </xs:all>
                </xs:complexType>
              </xs:element>
              <xs:element name="metas" minOccurs="0">
                <xs:complexType>
                  <xs:all>
                    <xs:element name="title" type="xs:string" minOccurs="0"/>
                    <xs:element name="robots" type="xs:string" minOccurs="0"/>
                    <xs:element name="description" type="xs:string" minOccurs="0"/>
                    <xs:element name="keywords" type="xs:string" minOccurs="0"/>
                    <xs:element name="language" type="xs:string" minOccurs="0"/>
                  </xs:all>
                </xs:complexType>
              </xs:element>
              <xs:element name="stylesheets" minOccurs="0">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="stylesheet" type="xs:string" maxOccurs="unbounded"/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name="javascripts" minOccurs="0">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="javascript" type="xs:string" maxOccurs="unbounded"/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name="haslayout" type="xs:integer" minOccurs="0"/>
              <xs:element name="layout" type="xs:string" minOccurs="0"/>
            </xs:all>
            <xs:attribute name="name" type="xs:string" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>


Note: I know this doesn't cover all cases; it is mostly a proof of concept.

Now, you need to check the XML file against that schema. Once again, the powerful XML manipulation library of PHP makes it a piece of cake:

// validate the new XML against and XSD
// $xmlTransformed is the semantically correct XML translation of the YAML file defined earlier
if($xmlTransformed->schemaValidate('view.yml.xsd'))
{
  return true;
}
else
{
  // display errors
}


Dealing with libxml errors

By default, DOMDocument::schemaValidate() will only return true if the XML file is valid, and false otherwise. But a good validation utility needs to be more verbose than that, and display errors where a files doesn't validate. In order to do that, you need to manually fetch the libxml errors when the validation fails, as explained in the PHP Manual.

libxml_use_internal_errors(true);
// validate the new XML against and XSD
// $xmlTransformed is the semantically correct XML translation of the YAML file defined earlier
if($xmlTransformed->schemaValidate('view.yml.xsd'))
{
  return true;
}
else
{
  // display errors
  $errors = libxml_get_errors();
  $message = "n";
  foreach ($errors as $error)
  {
    $message .= trim($error->message) . ' (';
    switch ($error->level)
    {
      case LIBXML_ERR_WARNING:
        $return .= "Warning $error->code";
        break;
      case LIBXML_ERR_ERROR:
        $return .= "Error $error->code";
        break;
      case LIBXML_ERR_FATAL:
        $return .= "Fatal Error $error->code";
        break;
    }
    if ($error->file)
    {
      $message .= " in $error->file";
    }
    $message .= " on line $error->line)n";
  }
  libxml_clear_errors();

  throw new Exception($message);
}


That's all. Now all it takes to validate any view.yml file are the XSLT and the XSD grammars. If a view.yml ever contains an incorrect setting, say:

default:
  foo: bar

Then an exception will be raised with a meaningful error message:

Element 'foo': This element is not expected. (Error 1871 on line 2)

Wrapping it up

The idea can be easily transposed to any YAML file. A YAML validator should:

  1. Turn a YAML file into a PHP associative array using sfYaml
  2. Turn this array into an XML structure, in a brute and blind way
  3. Turn the XML structure into a second XML structure using a set of XSLT rules to make the structure semantically correct
  4. Validate the second XML structure using an XML Schema
  5. If errors appear, return them wrapped up in an exception

To validate, say the generator.yml in symfony, all it takes is a generator.yml.xsl and a generator.yml.xsd to define the expected grammar in this file.

Ironic, isn't it?

You could say that the idea behind YAML is to avoid writing XML files. So using XML, XSD and XSLT in order to validate a YAML file may look a bit counter-intuitive, if not ironic.

But when you put it all together, the code necessary to validate any YAML file (not including, or course, the XSLT and XSD grammars, which depend on the file you validate) take only a few dozen lines. Besides, PHP is very good at handling XML, so it's better to use it for its strong points, instead of trying to mimic another language an end up writing thousands of lines of code. Actually, the 'K.I.S.S.' principle that encourages the use of YAML for configuration files should also apply here: XML manipulation is the simplest way to validate a YAML file, so it's the right tool for the job.

Last but not least, revolutions sometimes look backwards - think about the Renaissance. So using XML to validate YAML is probably not as dumb as it sounds.

The full YAML validator code is attached below, together with the example YAML file for your testing pleasure. Once again, I'm not a developer, so the code is just there to prove that the idea works. It could probably be much improved.

Source code + example YAML file and validator schemas

Including the YAML validation system in a web application framework that uses YAML is a must. Validation should only be done in development environment, of course, and only when the YAML files change. Symfony uses a configuration cache system with a set of configuration handlers that would make validation very easy and efficient. Let alone other frameworks in PHP, or in other languages, who could also take advantage of a similar approach.

Oh, and there is one more thing: The semantically correct XML file and its XSD syntax define a perfect XML equivalent to YAML files in symfony. If you want to use XML instead of YAML, and write your own configuration handlers, you should probably follow this kind of syntax.

Everybody Goes to Symfony Camp

And that includes me. I will be giving a presentation there, in two weeks from now, called "Developing for Developers - Usability Applied to Programming", and illustrated by my recent work on DbFinderPlugin.

If you'd like to meet the best PHP5 developers in the world, or me, you should definitely go to the Symfony Camp on September 12th and 13th. The conference is in The Netherlands, not far from Amsterdam - that means not far from anybody in Europe. I heard there are some tickets left for the conference, but it won't last long. The price is not free, but with the great people talking there, the tasty barbecue, the unique atmosphere and a huge lawn to put your tent in, it's a bargain. Besides, they may fill the swimming pool this year.

We'll have plenty of time to speak about symfony, plugins, documentation, the future and everything else. It's a unique opportunity to meet in person all those who lead the symfony community. Also, there are one or two seats left for the training session, so if you want to become operational in symfony quickly, dive in.

One last world for those who expect drama: There Won't Be Blood.

Propel 1.3 is out

The news has just hit the Propel home page: Propel long-awaited 1.3 version was released yesterday.

Not only is Propel 1.3 a lot faster than the previous version (thanks to PDO), it also fixes a handful of problems many of us had with Propel 1.2, and adds a few features as well. Here is a quick overview of interesting changes for symfony users:

  • Table and column identifiers are now quoted with ticks in generated SQL code
  • Ability to define your own method names for foreign key getters and setters (refPhpName)
  • Using clearSelectColumns() and addAsColumn() works
  • Fully functional doCount(), handling limit/offset, group by columns, etc.
  • Ability to define the primary key of a new object being inserted
  • Subsequent calls to retrieveByPK() or doSelect*() with the same parameters return the same object instances (a.k.a. "Object Instance Pooling")
  • Hangling of Master-Slave connections in a replicated environment
  • One-to-one relationships
  • Native nested sets implementation (no need for sfPropelActAsNestedSetBehaviorPlugin anymore)
  • doSelectJoinXXX() methods now default to a left join instead of an inner join
  • Ability to self-reference several times AND hydrate related objects

You can check the list of the 242 tickets fixed for this release in the Propel Trac.

It seems that Propel 1.3 will be the default Propel version for symfony 1.2 - Dustin just made the change this morning.

This is great news indeed, and it means that future applications built with symfony and Propel will be even faster and easier to build than the ones we currently know!

Eating My Own Dog Food

I spent the last three hours porting my sfSimpleBlog plugin to sfPropelFinder. While it was the occasion to polish the sfPropelFinder API and fix a bug, it was also a great pleasure to replace Propel Peer/Criteria code with finder one.

The blog plugin is up and running, the code is now much cleaner, and as a bonus, the query count has been reduced. If you want to test it, checkout the latest trunk version of the plugin (the plugin release system of the symfony project website doesn't seem to appreciate my PEAR package).

I find sfPropelFinder code to be naturally flowing. It cuts model classes sizes by 50%, it is much more readable, and makes a few custom model methods useless.

See for yourself. Here is the old PluginsfSimpleBlogPostPeer class, holding the methods required to retrieve blog posts, in the previous version:

class PluginsfSimpleBlogPostPeer extends BasesfSimpleBlogPostPeer
{
  public static function getRecentPager($max, $page)
  {
    $pager = new sfPropelPager('sfSimpleBlogPost', $max);
    $c = new Criteria();
    $c->add(self::IS_PUBLISHED, true);
    $c->addDescendingOrderByColumn(self::CREATED_AT);
    $pager->setCriteria($c);
    $pager->setPage($page);
    $pager->setPeerMethod('doSelectJoinAll');
    $pager->init();

    return $pager;
  }

  public static function getRecent($max = 10)
  {
    $c = new Criteria();
    $c->add(self::IS_PUBLISHED, true);
    $c->addDescendingOrderByColumn(self::CREATED_AT);
    $c->setLimit($max);

    return self::doSelectJoinAll($c);
  }

  public static function getTaggedPager($tag, $max, $page)
  {
    $pager = new sfPropelPager('sfSimpleBlogPost', $max);
    $c = new Criteria();
    $c->addJoin(sfSimpleBlogTagPeer::SF_BLOG_POST_ID, self::ID);
    $c->add(sfSimpleBlogTagPeer::TAG, $tag);
    $c->add(self::IS_PUBLISHED, true);
    $c->addDescendingOrderByColumn(self::CREATED_AT);
    $pager->setCriteria($c);
    $pager->setPage($page);
    $pager->setPeerMethod('doSelectJoinAll');
    $pager->init();

    return $pager;
  }

  public static function getTagged($tag, $max)
  {
    $c = new Criteria();
    $c->addJoin(sfSimpleBlogTagPeer::SF_BLOG_POST_ID, self::ID);
    $c->add(sfSimpleBlogTagPeer::TAG, $tag);
    $c->add(self::IS_PUBLISHED, true);
    $c->addDescendingOrderByColumn(self::CREATED_AT);
    $c->setLimit($max);

    return sfSimpleBlogPostPeer::doSelectJoinAll($c);
  }

  public static function retrieveByStrippedTitleAndDate($text, $date, $con = null)
  {
    if ($con === null)
    {
      $con = Propel::getConnection(self::DATABASE_NAME);
    }

    $criteria = new Criteria(sfSimpleBlogPostPeer::DATABASE_NAME);
    $criteria->add(sfSimpleBlogPostPeer::STRIPPED_TITLE, $text);
    if (sfConfig::get('app_sfSimpleBlog_use_date_in_url', false))
    {
      $criteria->add(sfSimpleBlogPostPeer::PUBLISHED_AT, $date);
    }

    $v = sfSimpleBlogPostPeer::doSelect($criteria, $con);

    return !empty($v)> 0 ? $v[0] : null;
  }
}


And here is the revised version. It is no longer a Peer class, but a Finder class extending DbFinder:

class PluginsfSimpleBlogPostFinder extends Dbfinder
{
  protected $class = 'sfSimpleBlogPost';

  public function recent()
  {
    return $this->
      with(sfConfig::get('app_sfSimpleBlog_user_class', 'sfGuardUser'))->
      where('IsPublished', true)->
      orderBy('CreatedAt', 'desc');
  }

  public function tagged($tag)
  {
    return $this->
      join('sfSimpleBlogTag')->
      where('sfSimpleBlogTag.Tag', $tag);
  }

  public function withNbComments()
  {
    return $this->
      leftJoin('sfSimpleBlogComment c')->
      withColumn('COUNT(sf_blog_comment.id)', 'NbComments')->
      where('c.IsModerated', false)->
      groupBy('c.SfBlogPostId');
  }

  public function findByStrippedTitleAndDate($text, $date)
  {
    $this->where('StrippedTitle', $text);
    if (sfConfig::get('app_sfSimpleBlog_use_date_in_url', false))
    {
      $this->where('PublishedAt', $date);
    }

    return $this->findOne();
  }
}


For those who followed my previous posts, you probably understand that the only thing that prevents sfSimpleBlog from working with Doctrine is the advance in the implementation of sfDoctrineFinder class. Once that is finished - and I'm progressing quite fast - sfSimpleBlogPlugin will be the first true ORM agnostic plugin.

Add request method requirement to routing in symfony 1.1

Doing rails-like RESTful resources is not natively possible with symfony, but with a bit of tweaking you can get closer to it.

The problem

Thanks to routing requirements, you can restrict the cases where a route matches a URL.

show_article:
  url: /article/:id
  params: { module: article, action: show }
  requirements: { id: \d+ }

What I sometimes miss, is the ability to add a HTTP request method requirement, i.e. making a route match only if the request is GET, or POST, or DELETE. This is really useful when designing RESTful resources, where a single external URL can match more than one resource, depending on the request method:

show_article:
  url: /article/:id
  params: { module: article, action: show }
  requirements: { id: \d+, sf_method: get }

update_article:
  url: /article/:id
  params: { module: article, action: update }
  requirements: { id: \d+, sf_method: post }

delete_article:
  url: /article/:id
  params: { module: article, action: delete }
  requirements: { id: \d+, sf_method: delete }

The solution

In order to allow symfony to do that, you need to modify two symfony classes: sfWebRequest and sfPatternRouting. As both of these classes are controlled by a factory in symfony 1.1, I recommend creating two custom classes, one for the request and one for the routing. Download these two classes here, and place them under the application lib/ folder. Now all you need is to edit your factories.yml to use these classes instead of the default ones:

all:
  request:
    class: myWebRequest
  routing:
    class: myPatternRouting

Clear the cache, and now you can add method requirements to your routing rules, as explained above.

In order to create a link to a route with a requirement other than sf_method: get, you must pass the sf_method parameter to url_for() explicitly (don't worry, it will not end up in the external URL):

echo url_for('article/show?id=1') => /article/1
echo url_for('article/update?id=1') => fails
echo url_for('article/update?id=1&sf_method=post') => /article/1
echo url_for('article/delete?id=1') => fails
echo url_for('article/delete?id=1&sf_method=post') => /article/1


What's next

Note that this is just a proof-of-concept, and to be perfectly RESTful, link_to() should generate a form doing a POST request when sf_method=post is passed. Also, the routing configuration handler could be modified to transform a single RESTful rule:

article:
  restful:
    base_url: /articles
    module:   article
    identifier: id

Into a list of normal rules:

# display a list of articles
list_article:
  url: /articles
  params: { module: article, action: index }
  requirements: { sf_method: get }

# display a single article
show_article:
  url: /articles/:id
  params: { module: article, action: show }
  requirements: { id: \d+, sf_method: get }

# display an empty form for a new article
new_article:
  url: /articles/new
  params: { module: article, action: new }
  requirements: { sf_method: get }

# handle the submission of a new article form
create_article:
  url: /articles/new
  params: { module: article, action: create }
  requirements: { sf_method: post }

# display a form to edit an existing article
edit_article:
  url: /articles/:id/edit
  params: { module: article, action: edit }
  requirements: { id: \d+, sf_method: get }

update_article:
  url: /articles/:id/edit
  params: { module: article, action: update }
  requirements: { id: \d+, sf_method: post }

delete_article:
  url: /articles/:id
  params: { module: article, action: delete }
  requirements: { id: \d+, sf_method: delete }

I leave that to your sagacity.

Admin Generator compatible with Propel and Doctrine

Just a quick note to mention a recent addition I made to the sfPropelFinderPlugin. It now features an admin generator theme, identical in functionality to the Propel and Doctrine admin generators, except... It uses DbFinder queries instead of Criteria or Doctrine_Query calls. See more in the Generator README file.

This has two implications:

  • Modules based on this generator are easier to customize, especially if you need to override methods of the action class. Instead of dealing with complicated Criterion conditions, you manipulate finder objects, with all the ease of use it implies.
  • Modules based on this generator are ORM agnostic, meaning they work both with Propel and Doctrine (actually, this is not entirely true, since sfDoctrineFinder doesn't implement all the features required by the DbFinder generator yet... but it will soon be true).

It makes the writing of ORM-agnostic plugins possible, especially for plugins like sfSimpleCMSPlugin or sfSimpleBlogPlugin who feature backend modules generated by symfony.

That's decided, the next version of the plugins I maintain will use DbFinder!

Next Page »