Issue
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;
}
Answered By - Bas Verhagen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.