Search code examples
phpcsshtml-emailstyling

Add element to every css rule in extracted style sheet (in string)


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!


Solution

  • 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;
    
                            }