Friday, January 3, 2014

Rewrite a CSS stylesheet using PHP

I've been writing a user-generated content system in PHP where users can upload CSS (and HTML) that will be shown in a DIV.  Of course, I wanted to restrict their CSS to only be applied inside the DIV and not style the surrounding content (either accidentally or maliciously).

The first step was easy: create a class and assign it to the DIV.

<div class="user_content">

But how to restrict a CSS stylesheet with a bunch of random rules to that DIV?

Given something like this:

.block input, table td.money {
  ...
}

How do I turn it into this?

.user_content .block input, .user_content table td.money {
  ...
}

A long search finally ended when Stack Overflow revealed a way to insert my user_content class before every rule using a fancy regular expression ... but it broke down on comments with commas in them.  It was a long search because it was very difficult to figure out what search terms to use and a real bummer that the solution broke down on something as common as comments.

So, I developed this code to handle block out the commas in comments, apply the fancy regular expression from Stack Overflow and then put the commas back.

function rewriteCss($css, $cssClazz) {
  while (preg_match('/(\/\*.+?),(.+?\*\/)/', $css) === 1) {
    $contents = preg_replace(
      '/(\/\*.+?),(.+?\*\/)/', '$1:comma:$2', $css);
  }
  $css = preg_replace(
    '/([^\r\n,{}]+)(,\s*(?=[^}]*{)|\s*{)/',
    '.'.$cssClazz.' $1$2', $css);
  while (preg_match('/(\/\*.+?):comma:(.+?\*\/)/', $css) === 1) {
    $css = preg_replace(
      '/(\/\*.+?):comma:(.+?\*\/)/', '$1,$2', $css);
  }
  return $css;
}

That did the trick.  Each comma in a comment is blocked out by replacing it with :comma:.  Then, a complicated preg_replace() call is made with the fancy regular expression from Stack Overflow.  Finally, the :comma: in comments are restored to being literal commas.

Super.

No comments:

Post a Comment