7

I'm writing a quick preg_replace to strip comments from CSS. CSS comments usually have this syntax:

/* Development Classes*/
/* Un-comment me for easy testing
  (will make it simpler to see errors) */

So I'm trying to kill everything between /* and */, like so:

$pattern = "#/\*[^(\*/)]*\*/#";
$replace = "";
$v = preg_replace($pattern, $replace, $v);

No dice! It seems to be choking on the forward slashes, because I can get it to remove the text of comments if I take the /s out of the pattern. I tried some simpler patterns to see if I could just lose the slashes, but they return the original string unchanged:

$pattern = "#/#";
$pattern = "/\//";

Any ideas on why I can't seem to match those slashes? Thanks!

3
  • 1
    I won't supply it as an answer since it's not really applicable directly to the question, but there are some good tools out there that remove/minify CSS content already: minifycss.com Just for those who might be on here trying to reinvent the wheel :) Commented Oct 17, 2009 at 15:29
  • You should better use a parser. Otherwise you will remove something like content: "/* … */". Commented Oct 17, 2009 at 15:33
  • AvatarKara - I'm actually using this script: code.google.com/p/cssmin - but it wasn't removing comments for some reason. I assumed I would have to write that bit in myself, but since other scripts AND answers aren't working, it seems like something stranger is going on....This is for use in an EE plugin, so I wonder if the string being passed in or the environment have some quirks I'm not aware of. Thanks for the link! Commented Oct 17, 2009 at 18:16

5 Answers 5

12

Here's a solution:

$regex = array(
"`^([\t\s]+)`ism"=>'',
"`^\/\*(.+?)\*\/`ism"=>"",
"`([\n\A;]+)\/\*(.+?)\*\/`ism"=>"$1",
"`([\n\A;\s]+)//(.+?)[\n\r]`ism"=>"$1\n",
"`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism"=>"\n"
);
$buffer = preg_replace(array_keys($regex),$regex,$buffer);

Taken from the Script/Stylesheet Pre-Processor in Samstyle PHP Framework

See: http://code.google.com/p/samstyle-php-framework/source/browse/trunk/sp.php

csstest.php:

<?php

$buffer = file_get_contents('test.css');

$regex = array(
"`^([\t\s]+)`ism"=>'',
"`^\/\*(.+?)\*\/`ism"=>"",
"`([\n\A;]+)\/\*(.+?)\*\/`ism"=>"$1",
"`([\n\A;\s]+)//(.+?)[\n\r]`ism"=>"$1\n",
"`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism"=>"\n"
);
$buffer = preg_replace(array_keys($regex),$regex,$buffer);
echo $buffer;

?>

test.css:

/* testing to remove this */
.test{}

Output of csstest.php:

.test{}
Sign up to request clarification or add additional context in comments.

5 Comments

That took out some spaces and linebreaks, but seemed to leave all comments intact. It definitely looks like the third line SHOULD be removing comments, though. Baffling.
hi there, it works fine for me - totally. comments were all removed.
Interesting! Thanks for the updated test. I'm running this inside an ExpressionEngine plugin, so I wonder if some environment difference is causing my issues.
Lines 3 and 4 of this REGEX now throw an error in PHP 7.3 Warning: preg_replace(): Compilation failed: escape sequence is invalid in character class offset 4 - question here: stackoverflow.com/questions/57829977/… code was working great... for years... lol
This sample fails with Warning: preg_replace(): Compilation failed: escape sequence is invalid in character class at offset 4 in ....the preg_replace line. See onlinephp.io/c/799cd
7

I don't believe you can use grouping within a negated character class like you have there. What you're going to want to use is called Assertions, of which there are two types. "look-ahead" and "look-behind".

The pattern you're looking for in English is basically, "forward slash, literal wildcard, anything that isn't followed by a forward slash or anything other than a literal wildcard that is followed by a forward slash or a forward slash that isn't preceded by a literal wildcard zero or more times, literal wild card, forward slash"

<?php

$str = '/* one */ onemore
/*
* a
* b
**/
stuff // single line
/**/';

preg_match_all('#/\*(?:.(?!/)|[^\*](?=/)|(?<!\*)/)*\*/#s', $str, $matches);
print_r($matches);

?>

1 Comment

Thanks for the in-depth explanation. I can't get this to work in the file I'm using it in, but it works perfectly when isolated, so I suspect I have more problems to figure out...
5

I had the same issue. To solve it, I first simplified the code by replacing "/ASTERIX" and "ASTERIX/" with different identifiers and then used those as the start and end markers.

$code = str_replace("/*","_COMSTART",$code);
$code = str_replace("*/","COMEND_",$code);
$code = preg_replace("/_COMSTART.*?COMEND_/s","",$code);

The /s flag tells the search to go onto new lines

3 Comments

Excellent and stable solution!
Clever. It works very well. I removed all my "//" comments, replaced them with /* ... */ comments and used your code. Well done, less "//" means less problems too.
Glad it was of some use!
2

There's a number of suggestions out there, but this one seems to work for me:

$v=preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $v); 

so

"/* abc */.test { color:white; } /* XYZ */.test2 { padding:1px; /* DEF */} /* QWERTY */"

gives

.test { color:white; } .test2 { padding:1px; } 

see https://onlinephp.io/c/2ae1c for working test

Comments

1

Just for fun(and small project of course) I made a non-regexp version of a such code (I hope it's faster):

function removeCommentFromCss( $textContent )
{
    $clearText = "";
    $charsInCss = strlen( $textContent );
    $searchForStart = true;
    for( $index = 0; $index < $charsInCss; $index++ )
    {
        if ( $searchForStart )
        {
            if ( $textContent[ $index ] == "/" && (( $index + 1 ) < $charsInCss ) && $textContent[ $index + 1 ] == "*" )
            {
                $searchForStart = false;
                continue;
            }
            else
            {
                $clearText .= $textContent[ $index ];
            }
        }
        else
        {
            if ( $textContent[ $index ] == "*" && (( $index + 1 ) < $charsInCss ) && $textContent[ $index + 1 ] == "/" )
            {
                $searchForStart = true;
                $index++;
                continue;
            }
        }
    }
    return $clearText;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.