-1

I want to change every css rule in a string whitch I extracted from a HTML email body. The string contains css inside a element whitch influence my website styling.

I want to add an element (div class) to every css rule inside that string. Is this possible with php?

Example:

$string = '<style type="text/css">body { blah blah } .div1 { blah blah } .div2 { blah blah }</style> Blah blah blah body text blah blah';

$extractcss = strip_tags($string , '<style>');

I want to add .mydiv to every css rule to get this:

$extractcss = '.mydiv body { blah blah } .mydiv .div1 { blah blah } .mydiv .div2 { blah blah }';

With the new string I want to influence the styling of the email body so that it no longer has any effect on my website styling.

Thanks in advance!

2
  • You could search for "PHP CSS Parser", there is sure to be a library out there that can accomplish this. Note that .mydiv body will not work. Commented Nov 18, 2023 at 12:09
  • Thansk for your comment. I didn't know how to search for an answere. Thanks to you I know I had to look for a PHP CSS Parse. Commented Nov 18, 2023 at 13:48

2 Answers 2

2

With te help of this topic I have the solution for my problem.

With the following function I parse the CSS from a string. After that I make a new css string with a div class in front of every css rule.

function parse_css($cssstring){
                            preg_match_all( '/(?ims)([a-z0-9\s\.\:#_\-@,]+)\{([^\}]*)\}/', $cssstring, $arr);
                            $result = array();
                            foreach ($arr[0] as $i => $x){
                                $selector = trim($arr[1][$i]);
                                $rules = explode(';', trim($arr[2][$i]));
                                $rules_arr = array();
                                foreach ($rules as $strRule){
                                    if (!empty($strRule)){
                                        $rule = explode(":", $strRule);
                                        $rules_arr[trim($rule[0])] = trim($rule[1]);
                                    }
                                }
                                
                                $selectors = explode(',', trim($selector));
                                foreach ($selectors as $strSel){
                                    $result[$strSel] = $rules_arr;
                                }
                            }
                            return $result;
                        }

// Extract and change css style
                        if ( str_contains($message, '<style') ) { 
                        
                            $extractcss = strip_tags($message, '<style>');
                            $parsecss = parse_css($extractcss);
                            $newcssstring = '<style type="text/css">';
                        
                            foreach ( $parsecss as $key => $value ) {
                                $newcssstring .= '.message-body '. $key .' { ';
                                    
                                    foreach ( $value as $k => $v ) {
                                        $newcssstring .= $k .': '. $v .'; ';
                                    }

                                $newcssstring .= ' } ';
                            }
                            $newcssstring .= '</style>';

                            $message = preg_replace("#([<]style)(.*)([<]/style[>])#s", "<!-- style extracted -->", $message);

                            $message= $newcssstring . $message;

                        }

EDIT

I searched for another css parse function. This one parses also media query's. I also changed this function so that selectors with , in it will be seperated.

function parse_css($css) {
    
    $cleanCss = [];

    // Remove css comments
    $clean1 = explode('/*', $css);
    foreach($clean1 as $clean2) {
        $clean3 = explode('*/', $clean2);
        $cleanCss[] = $clean3[count($clean3) -1];
    }
    $css = implode('', $cleanCss);

    // Make array of all css selectors
    $temp = explode('}', $css);
    $params = [];
    $type = 'all';
    $nextBracketIsNotMediaEnd = false;

    // Loop through all css selectors
    foreach( $temp as $tem2 ) {

        // Make array of all css rules
        $data = explode('{', $tem2);

        // 1 result in array probably end of media query
        if (count($data) == 1) {
            if ($nextBracketIsNotMediaEnd) {
                $nextBracketIsNotMediaEnd = false;
            } else {
                $type = 'all';
                continue;
            }
        }

        // 3 results in array probably begin media query with first rule
        if (count($data) == 3) {
            $typeTemp = trim($data[0]);
            if ( substr( $typeTemp, 0, 6 ) === "@media" ) {
                $type = $typeTemp;
                array_shift($data); // Delete media from array
            } else {
                $data[1] = $data[0].$data[1];
                $nextBracketIsNotMediaEnd = true;
            }
        }

        // 2 results in array probably one css rule
        if (count($data) == 2) {
            $rows = explode(';',$data[1]);
            
            $tempData = [];

            foreach($rows as $row) {
                $paramsinline = explode(':', $row);
                if (empty($paramsinline[0]) || empty($paramsinline[1])){
                    continue;
                }
                $tempData[trim($paramsinline[0])] = trim($paramsinline[1]);
            }

            $selector = trim($data[0]);

            // Make value for selector as $value when it's not excist
            if (!empty($tempData)) {
                if (empty($params[$type][$selector])) {
                    $value = $tempData;
                } else {
                    $value = array_merge($params[$type][$selector], $tempData);
                }
            } else {
                $value = '';
            }

            if ( $value != '' ) {

                // Split css selector with , in multiple selectors with same css rules
                if ( strpos($selector, ',') ) {
                    $selectors = explode(',', $selector);
                    foreach ($selectors as $newselector){
                        $params[$type][$newselector] = $value;
                    }
                } else {
                    $params[$type][$selector] = $value;
                }

            }
            
        }
    }
    return $params;
}

The code that put the class message-body in front of every selector is changed to:

$message = 'message (email) body'; 
   
if ( str_contains($message, 'style') ) { 
                            
                            // Strip all tags except for <style> tags and change the <style> tags to STARTSTYLE and ENDSTYLE
                            $extractcss = strip_tags($message, '<style>');
                            $extractcss = str_replace('<style type="text/css">', 'STARTSTYLE', $extractcss);
                            $extractcss = str_replace('</style>', 'ENDSTYLE', $extractcss); 

                            // Get every css between STARTSTYLE and ENDSTYLE
                            preg_match_all( '/STARTSTYLE(.*?)ENDSTYLE/s', $extractcss, $arr);

                            $newcssstring = '';

                            // Loop through all css between every <style> tag and put .message-body in fornt of every css selector
                            foreach ( $arr[1] as $css ) {

                                $parsecss = parse_css($css);

                                //echo '<pre>'. print_r($parsecss, true) .'</pre>';

                                $newcssstring .= '<style type="text/css">';
                            
                                foreach ( $parsecss as $parsedelement => $valuee ) {

                                    $mediaquery = false;
                                    
                                    if ( strpos($parsedelement, 'media') ) {
                                        $newcssstring .= $parsedelement .' { ';
                                        $mediaquery = true;
                                    }
                                
                                foreach ( $valuee as $element => $value ) {
                                    
                                    $newcssstring .= '.message-body '. $element .' { ';
                                
                                    foreach ( $value as $k => $v ) {
                                    $newcssstring .= $k .': '. $v .'; ';
                                    }
                                
                                    $newcssstring .= ' } ';
                                
                                }
                                
                                if ( $mediaquery == true ) {
                                        $newcssstring .= ' } ';
                                    }
                                
                                }

                                $newcssstring .= '</style>';

                            }

                            // Delete old css
                            $message = preg_replace("/<style\\b[^>]*>(.*?)<\\/style>/s", "<!-- style extracted -->", $message);

                            // Make new body
                            $message = $newcssstring . $message;

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

1 Comment

Great job working it out
-1

The code have some error, anyway the simpliest way is to use the str_replace() php function

$newstring=str_replace("{",".mydiv {",$string);

1 Comment

That would end up with classes like body .mydiv { blah blah } .div1 .mydiv { blah blah }, not body { blah blah } .mydiv .div1 { blah blah }

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.