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