Sanitize User Input for XSS in PHP

The best way to sanitize any input from your user is to use the HTML Purifier library. HTML Purifier will remove any XSS from your code, produce valid HTML, and generally make you sleep just a bit safer at night. It doesn’t completely sanitize user input, and you still need to be careful with it before using it anywhere (such as an SQL statement), but it will remove all XSS attacks against your website.

Here’s a simple example of how to use it:

$purifier = new HTMLPurifier();
$purifier->purify($user_string);

Yii Override Command Parameters

The Yii Framework is very flexible and has a variety of way you can configure it. Here I will show you how you can customize parameters on a Command task.

The default Yii Migration command asks the user for a confirmation before running if there are any tables that have been changed, this is quite a sensible default, but I don’t want to be asked if the command should be run after a deployment. Of course it should be.

To see what options can be configured, open the Migrations file

vendors/framework/cli/commands/MigrateCommand.php

Any of the public class variables can be configured in your config/console.php file. Using the commandMap parameter, you can configure values for Yii Commands. Then specify the migrate task, and then the config values you want to change. In this case, I want to change interactive to false, so it won’t ask for a confirmation.

Sample config/console.php:

return array(
 ...
  // database migration, don't ask for confirmation
  'commandMap'=>array(
    'migrate'=>array(
      'class'=>'system.cli.commands.MigrateCommand',
      'interactive'=>false,
    ),
  ),
);

How to add Local Config Variables to Yii

Often times you want to be able to specify configuration parameters or settings that only apply to a single environment. These local configuration don’t need to, and shouldn’t be entered into version control, and should over ride default values. I needed a solution for a project I was working on so I wrote one for Yii.

The main configuration file protected/config/main.php returns an array of parameters. Edit this file to merge 2 arrays, 1 from main.php, and another from local.php.

Edit main.php to look like this:

<?php

return CMap::mergeArray(
  array(
    'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
    'name'=>'Web app',

... other parameters ...

    'params'=>array(
      // this is used in contact page
      'adminEmail'=>'webmaster@example.com',
    ),
  ),
  local_config()
);

// return an array of custom local configuration settings
function local_config()
{
  if (file_exists(dirname(__FILE__).'/local.php'))
  {
    return require_once(dirname(__FILE__).'/local.php');
  }

  return array();
};

And then add any of your own configuration to local.php in the same config directory.

<?php
return array(
  'components'=>array(
    'db'=>array(
      'connectionString' => 'mysql:host=localhost;dbname=db_name',
      'username' => 'my_user',
      'password' => 'secret',
      'enableParamLogging'=>true,
    ),
    'log'=>array(
      'class'=>'CLogRouter',
      'routes'=>array(
        'file'=>array(
          'class'=>'CFileLogRoute',
          'levels'=>'trace, info, error, warning',
        ),
        'profile'=>array(
          'class'=>'CProfileLogRoute',
          'report'=>'summary',
        ),
      ),
    ),
  ),
  'params'=>array(
    // this is used in contact page
    'adminEmail'=>'yourself@example.com',
  ),
);

Here we’ve overridden the adminEmail parameter to yourself@example.com, we’ve added in custom database username and password, and we’ve enabled logging.

Feel free to use this as a straightforward way to add custom config values to your Yii project. Just remember to make local.php an ignored file in Git or Subversion.

Add an i18n Static Page to Symfony

My last post explained the basics on how to add static pages in symfony, this post expands on that and shows you how to do it for a multilingual site.

We split the template finding code out for code maintainability, and we enhance it on where to look for the file. First it tries to find the template in the language and country eg: en_CA, then it tries to find the template in the matching language, and if that is not found, it falls back to the default language.

/**
 * Load a static page.
 * @param sfRequest $request A request object
 */
public function executePage(sfWebRequest $request)
{
  $template = $this->findTemplate($request->getParameter('view'), $this->getUser()->getCulture());
  $this->forward404Unless($template);
  $this->setTemplate($template);
}

/**
 * Check if a template page exists for a given culture.
 * Be intelligent and check if language & country exist, try language, and then default to english.
 * @param string $name Template filename to check
 * @param string $culture Symfony culture string
 */
protected function findTemplate($name, $culture)
{
  // for safety, strip out all non-alphanumeric characters
  $name = preg_replace('/[^a-zA-Z0-9\s]/', '', $name);

  $directory = $this->getContext()->getModuleDirectory() . DIRECTORY_SEPARATOR ."templates";
  // try language and country: en_CA
  if (is_readable($directory . DIRECTORY_SEPARATOR . $culture. DIRECTORY_SEPARATOR . $name ."Success.php"))
  {
    return $culture. DIRECTORY_SEPARATOR . $name;
  }
  // try langage only: en
  elseif (is_readable($directory . DIRECTORY_SEPARATOR . substr($culture, 0, 2). DIRECTORY_SEPARATOR . $name ."Success.php"))
  {
    return substr($culture, 0, 2). DIRECTORY_SEPARATOR . $name;
  }
  // try default language
  elseif (is_readable($directory . DIRECTORY_SEPARATOR . $name ."Success.php"))
  {
    return $name;
  }
  return false;
}

The template directory should have the default language file as usual, eg: templates/helpSuccess.php, and then there should be folders for each language and possibly language & country with the same filename, but localized. eg: templates/fr/helpSuccess.php

Add a Static Page to Symfony

Static pages can be added to Symfony quite easily.

Edit your routing.yml file which is probably located at apps/frontend/config/routing.yml, and add the following routes to add an about, a privacy, and a terms and conditions page.

# static pages
about:
  url:   /about
  param: { module: home, action: page, view: about }
privacy:
  url:   /privacy
  param: { module: home, action: page, view: privacy }
terms:
  url:   /terms
  param: { module: home, action: page, view: terms }

If you are going to keep the generic rules, make sure you add these new rules before the default actions.

Clear your cache:
./symfony clear-cache

Then inside the module named home (create it if it doesn’t exist), add the following action:

  /** 
   * Load a static page.
   * @param sfRequest $request A request object
   */
  public function executePage(sfWebRequest $request)
  {
    $directory = $this->getContext()->getModuleDirectory().DIRECTORY_SEPARATOR."templates";
    $name = $request->getParameter('view');
    // for safety, strip out all non-alphanumeric characters
    $name = preg_replace('/[^a-zA-Z0-9\s]/', '', $name);
    if (is_readable($directory.DIRECTORY_SEPARATOR.$name."Success.php"))
    { 
      return $this->setTemplate($name);
    }
    else
    { 
      $this->forward404();
    }
  }

The template files will be on the home/templates directory, called aboutSuccess.php, privacySuccess.php, and termsSuccess.php

This action will check if the template file exists, and if so load the template, if not it will forward to the 404 not found page. Easy and safe static templates. Add more routes and the appropriate template file as required.