You are here

public function Emogrifier::emogrify in HTML Mail 5

Same name and namespace in other branches
  1. 6 emogrifier/emogrifier.php \Emogrifier::emogrify()

File

emogrifier/emogrifier.php, line 69
CSS to Inline Converter Class

Class

Emogrifier

Code

public function emogrify() {
  $body = $this->html;

  // process the CSS here, turning the CSS style blocks into inline css
  if (count($this->unprocessableHTMLTags)) {
    $unprocessableHTMLTags = implode('|', $this->unprocessableHTMLTags);
    $body = preg_replace("/<({$unprocessableHTMLTags})[^>]*>/i", '', $body);
  }
  $xmldoc = new DOMDocument();
  $xmldoc->strictErrorChecking = false;
  $xmldoc->formatOutput = true;
  $xmldoc
    ->loadHTML($body);
  $xmldoc
    ->normalizeDocument();
  $xpath = new DOMXPath($xmldoc);

  // get rid of css comment code
  $re_commentCSS = '/\\/\\*.*\\*\\//sU';
  $css = preg_replace($re_commentCSS, '', $this->css);

  // process the CSS file for selectors and definitions
  $re_CSS = '/^\\s*([^{]+){([^}]+)}/mis';
  preg_match_all($re_CSS, $css, $matches);
  foreach ($matches[1] as $key => $selectorString) {

    // if there is a blank definition, skip
    if (!strlen(trim($matches[2][$key]))) {
      continue;
    }

    // split up the selector
    $selectors = explode(',', $selectorString);
    foreach ($selectors as $selector) {

      // don't process pseudo-classes
      if (strpos($selector, ':') !== false) {
        continue;
      }

      // query the body for the xpath selector
      $nodes = $xpath
        ->query($this
        ->translateCSStoXpath(trim($selector)));
      foreach ($nodes as $node) {

        // if it has a style attribute, get it, process it, and append (overwrite) new stuff
        if ($node
          ->hasAttribute('style')) {
          $style = $node
            ->getAttribute('style');

          // break it up into an associative array
          $oldStyleArr = $this
            ->cssStyleDefinitionToArray($node
            ->getAttribute('style'));
          $newStyleArr = $this
            ->cssStyleDefinitionToArray($matches[2][$key]);

          // new styles overwrite the old styles (not technically accurate, but close enough)
          $combinedArr = array_merge($oldStyleArr, $newStyleArr);
          $style = '';
          foreach ($combinedArr as $k => $v) {
            $style .= $k . ':' . $v . ';';
          }
        }
        else {

          // otherwise create a new style
          $style = trim($matches[2][$key]);
        }
        $node
          ->setAttribute('style', $style);
      }
    }
  }

  // This removes styles from your email that contain display:none;. You could comment these out if you want.
  $nodes = $xpath
    ->query('//*[contains(translate(@style," ",""),"display:none;")]');
  foreach ($nodes as $node) {
    $node->parentNode
      ->removeChild($node);
  }
  return $xmldoc
    ->saveHTML();
}