Skip to content
Mar 3 11

Symfony Live 2011: Day 1

by Jean-Marie de Boer

We just arrived back at the apartment after the first day of sflive2011. ‘Moe maar voldaan’, as the Dutch say.

Driebit backend developers

“My personal highlights: Josh Holmes’ talk on simplicity and Tom Boutell’s presentation on Apostrophe Now. Not so great: The git presentation, too much information crammed into the available time. Funniest moment: Well I will leave that one to one of my coworkers to write about…”
- JM

“I always had my doubts about checking “credentials” for accessing a certain object from your model layer in the action layer. These doubts were confirmed by the architecture of the new Symfony2 Security component, which makes it possible to check permissions in a very early stage.
The new Forms component looks also very promising. It is also a great example of “delegation” and “loose coupling”. For example the value filters and value transformers know nothing about their surrounding, and do exactly and only what they are meant for. In every part of Symfony2, everything seems to be really delegated to the right part of the software, which is on itself fully interchangeable by your own implementation.”
- Matthias

Interesting...

“I was impressed by the second talk of the day by Johannes Schmitt. One of the few intelligible non-native English speakers. The talk had a good pace, covered an interesting topic and was at a pleasantly in-depth level. Another positive lecture, in hindsight, was the one by Pablo Díez. At first I got distracted by his soft Spanish-accented voice, but eventually I was able to get the hang of it – still needed his slides though – and actually enjoyed his presentation on Doctrator. Hasta la vista Pablo!”
- Sander

Mar 2 11

Symfony Live 2011: Nous sommes arrivés

by Sander Coolen

Train platform at Paris NordJust disembarked at Paris Nord. More updates soon…

Feb 15 11

Subdomain dat app

by Sander Coolen

On a recent project we had multiple front-ends on the same back-end. Not a hipster mobile version, but a “For Teachers” section on a website for schoolchildren. We could have chosen to do this as separate modules, but being relatively new to Symfony I was interested in going for the “another application” approach. Or maybe that’s just my mind justifying the actions of my one year younger Symfony noobish self.

Anyway, what I wanted to achieve was to have a nice subdomain for that additional app. Beforehand I was guessing a bit of .htaccess hocus pocus would do the trick. A colleague, however, suggested to try and solve it inside index.php, by instantiating a different ProjectConfiguration based on the HTTP_HOST. Great idea!

{ pseudo }
if (subdomain in HTTP_HOST) then
  getApplicationConfiguration('myapp', 'prod')
else
  getApplicationConfiguration('frontend', 'prod');

Although this worked like a charm, there was one problem with the URLs. All applications, besides frontend, have a route prefix that defaults to the application name. For example: backend_dev.php for your backend application in its development environment. The URLs for my teachers app now looked something like:

http://subdomain.driebit.com/subdomain.php/module/action

Must … get … rid … of …

After a quick search (“Google called… they’re running out of search results!”) I ended up in the cached settings file: config_settings.yml
The frontend one differed from the other apps in one particular, intriguing looking, setting:

'sf_no_script_name' => false

And lo and behold, changing it to true removed the prefix from the route! For the actual project I used the initialize method in the ProjectConfiguration to explicitly set it to true:

if ('prod' === $this->getEnvironment())
{
  sfConfig::set('sf_no_script_name', true);
}

That’s gold, Jerry! Gold!

Feb 13 11

Reusing generated routes (thou shalt)

by Sander Coolen

Suppose you have a large set of items of which you want to print a URL. For example a list of records in the back-end or a paginator with a large number of links. In your view template you would code something like:

<?php foreach ($items as $item):?>
<?php   echo link_to($item->getTitle(),
                '@some_route?id=' . $item->getId());?>
<?php endforeach;?>

This will work as expected, but for very long lists you will pay a small performance penalty. Of course you could introduce one of the caching mechanisms Symfony provides, but there is fast and then there is … sfDrum::roll() … sprintf! S-hide-your-kids-hide-your-wife-print-motherf*ckin’-snakes-on-a-plane-f! Watch me as I unfold this grandeur of a tweak:

<?php $link = urldecode(url_for('@some_route?id=%u'));
<?php foreach ($items as $item):?>
<?php   echo link_to($item->getTitle(),
                sprintf($link, $item->getId()));?>
<?php endforeach;?>

Art thou not impresseth?! Perfometh thine Websitus Internetus not betters?

More on Routing, caching and performace: Lazy Routing deserialization

Dec 22 10

Using frontend environment in a backend application and vice versa

by Hidde Braun

Just a quick post to share a function i use in a few symfony 1.2 projects to switch quickly to/from another application configuration.
I use this mainly to send emails from the backend application that use frontend routing and frontend app.yml variables

Here it is:

abstract class Util {
public static function switchToApplication($options = array())
    {

      $currentOptions = array(
        'app'            => sfConfig::get('sf_app'),
        'env'            => sfConfig::get('sf_environment'),
        'debug'          => sfConfig::get('sf_debug'),
        'no_script_name' => sfConfig::get('sf_no_script_name'),
      );

      $options = array_merge($currentOptions, $options);

      $configuration = ProjectConfiguration::getApplicationConfiguration($options['app'], $options['env'], $options['debug']);
      sfContext::createInstance($configuration);
      sfConfig::set('script_name', $options['no_script_name']);
      return $currentOptions;
    }
}

To use it, you can simply do this a backend application module action:

public function executeSendReminderEmail(){

$frontendConfig = array(
'app'            => 'frontend',
'env'            => 'prod',
'debug'          => true,
'no_script_name' => true
);

//The function returns the current config so you can switch back easily when you're done
$previousConfig = Util::switchToApplication($frontendConfig);

//Fetch the data you want
$user = UserPeer::retrieveByPk(1);

//This function willl use the frontend routing, app.yml etc !!
EmailManager::sendRegistrationConfirmation($user);

//Now switch back to the backend config
Util::switchToApplication($previousConfig);

}

This saved me several times when i wanted some frontend stuff in my backend app!

Nov 15 10

Create good looking documentation for PHP applications

by Matthias Noback

If you want to make the documentation for your PHP software good looking (i.e. make it look like the online Symfony documentation) and save it to a PDF file, do the following:

  1. Save the CSS code below to a file on your computer, name it (for example) symfony-markdown.css.
    #content1 {
      font-size: 13px;
      margin-right: 0; }
    
    .documentation pre {
      font-size: 100%; }
    
    #legacy_bar, #content1 > form, #content1 > hr, #content1 > h1, #content1 > .markdown_help, #topbar, #bar, #filters, #footer, .sensiolabs {
      display: none; }
    
  2. Write your documentation in the Markdown format.
  3. While you are writing, preview your documentation using the Markdown dingus, Symfony style.
  4. When you are done, create a preview of your documentation for the last time, using the above-mentioned “dingus”.
  5. Add a custom stylesheet to the page (for example, if you are using Firefox and have installed the Web Developer add-on, by clicking on CSS -> Add User Style Sheet…), select the file you created at the first step. The page should now be “clean”, no header, no footer, no sidebar, etc.
  6. Check the page settings in your browser and make sure that no headers and footers will be printed.
  7. Print the page to a PDF printer (for example Bullzip PDF printer)

A good looking PDF document is the result:

http://www.symfony-project.org/plugins/markdown_dingus
Nov 15 10

Filtering your task list in Eclipse

by Jean-Marie de Boer

Granted, it’s not about Symfony per sé, but still somewhat related.

As you know, the Eclipse IDE has a ‘task list’. It is sensitive to comments like @todo, TODO and FIXME etcetera and shows them in a pane. But in the default view there are so many it’s easy to lose track of your todo’s and since the list is so long, you are not triggered to clear it completely.

Unfiltered task list

So I write my TODO’s with my initials in parenthesis:

// TODO (JM): finish this code

Then I create a filter in the task list.

  • From the task list’s View menu, choose ‘Configure contents’.
  • Create a new configuration
  • Give it a handy name
  • In ‘Description’, select ‘contains (JM)’ or whatever your text is.

Task list filter

From now on, I can select 'Show' from the task list's View menu, and choose my filter; I will only see my own todo's.

Filtered task list

Nov 11 10

Schema changes and updating your database

by Hidde Braun

Sometimes you need to add to- or change your database schema and face the tedious error prone task of updating a live or staging server with the changes. If all you did was add tables, add fields or change a model relation or column configuration, you can use the method below to safely backup your datarebuild your model, forms, filters and database, and then restore the backup again. Things should work just fine after that  (note the “should” in that sentence…)

First we make a backup of the existing data in the database:

mysqldump -u<dbuser>-p
--no-create-info
--complete-insert
<dbname> > dump.sql

Note: change the<dbuser> and <dbname> with your database username and database name. You will be prompted for the password.

This command will dump your database to a file called dump.sql.  Since we only want the data and not the create statements to build all tables. (this is what symfony will do for us with the build –all command below) we add the option –no-create-info.

We want complete inserts, so the data AND the corresponding column names, so MySQLwill know which value to insert where. If you don’t add the –complete-insert option, and you added a column to your table,  MySQL will insert the right value in the wrong column,since it will simply use the order of the columns you had before you made the change, and you will get the wrong offset when re-inserting your data.

./symfony doctrine:build --all --no-confirmation

This command will rebuild all your model, form and filter classes, build the SQL statements for eveything and rebuild you database. After running this command you will have a clean empty database that corresponds to your schema.yml file

mysql -u<dbuser> -p  <dbname> < dump.sql

If however, you deleted tables and/or columns in your update, you have to first:

  • Backup all your data (better safe than sorry…)
  • Delete the tables and columns you don’t need anymore from your database
  • And THEN use the method above

This ensures that if you will restore the backup it will find all the tables and columns that are still in your schema and database.

Nov 11 10

Enable logging in the production environment

by Jean-Marie de Boer

1. In apps/frontend/config/factories.yml, change

prod:
  logger:
    class:   sfNoLogger
    param:
      level:   err
      loggers: ~

to

prod:
  logger:
    class: sfAggregateLogger
    param:
      level: debug

2. In apps/frontend/config/settings.yml, change

prod:
  .settings:
    no_script_name:         on
    logging_enabled:        off
    error_reporting:        <?php echo (E_ALL | E_STRICT)."\n" ?>

to

prod:
  .settings:
    no_script_name:         on
    logging_enabled:        on
    error_reporting:        <?php echo (E_ALL | E_STRICT)."\n" ?>

3. In index.php, change

$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);

to

$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', true);

4. symfony cc

Oct 30 10

Stop MIMEing already!

by Sander Coolen

When handling file upload fields in forms or manipulating media files you probably want some kind of file type detection.

This is called mime type guessing and there are a couple of ways to do this, all of which are used in Symfony (for example in sfValidatorFile and sfImageTransformPlugin):

Match the file extension against a known list of extensions
This is the lamest and most unreliable of the three. It’s like pulling over a drunken driver and make him recite the alphabet backwards to see if he’s too intoxicated; it works in some cases, but it’s just more reliable to analyze their breath or blood: “Look! It’s a Word document dangling down the Electronic Highway. Let’s pull him over and make him show his extension, if you know what I mean.”

Grab the magic bytes from the file header and compare them with known signatures
This is the approach for the more sensible developer. Most binary files start with a unique sequence of bytes, which can be used to identify the file format. For example: PNGs start with 89 50 4E 47 0D 0A 1A 0A (50 4E 47 = “PNG”).

Use *nix magic database
On Unix platforms there is a file command for detecting file formats. It uses the foregoing method and matches the bytes against a database of magic numbers. This database is located in a file called magic. A term reminiscence of a bygone era in which we developers were still whizzkids and Symfony 2 one of Beethoven’s early works.

There are a two PHP modules that utilize the magic file to determine content types:

  1. PECL Fileinfo
  2. PEAR MIME_Type

A third method in PHP is the deprecated mime_content_type() function.

The aforementioned sfImageTransformPlugin also has a fallback to getimagesize; mime type detection from PHP’s GD library that only supports a limited set of image types.
The type of detection in this image transform plugin is configurable:

sfImageTransformPlugin:
  default_adapter: GD
  mime_type:
    auto_detect: true
    library: gd_mime_type # gd_mime_type (GD), Fileinfo (PECL), MIME_Type (PEAR)

Make sure you have the necessary libraries installed when chosing for Fileinfo or MIME_Type!

When using Fileinfo (finfo) make sure PHP can find the magic database. On our servers it is located in /usr/share/file/magic, but the default for finfo_open is /usr/share/misc/magic. Use one of the following methods to define the path to your magic file:

  1. Pass is as the second parameter to finfo_open
    NB: Symfony’s design of sfValidatorFile makes it impossible to do this. They forgot to make it accessible through the validator options, I guess. There is however the option to add custom mime type guessing callbacks.
    The sfImage class of sfImageTransformPlugin has the same limitation. Workaround this by:

    • Overwriting sfImage or sfValidatorFile in your app lib (and modifying the necessary code), so your file takes precedence over the original
    • Subclassing sfImage or sfValidatorFile
  2. Set the MAGIC environment variable:
    • Via SetEnv in .htaccess or your webserver configuration
    • Via PHP’s putevn() function
  3. Create a symlink or duplicate the magic database to the default location

Note that your *nix distribution might not have the most recent version of file (the current version is 5.04), which could lead to unexpected results.

Finally there are some issues with finfo_open. Check http://php.net/manual/en/function.finfo-open.php for more information.

I hope this sheds some light on mime type detection with PHP, but it is certainly not the complete story, so feel free to share your wisdom in the comments.