* @author Roman Ožana * @author Sander Kruger * @author Zoli Szabó */ class CssInliner extends AbstractHtmlProcessor { /** * @var int */ private const CACHE_KEY_CSS = 0; /** * @var int */ private const CACHE_KEY_SELECTOR = 1; /** * @var int */ private const CACHE_KEY_CSS_DECLARATIONS_BLOCK = 2; /** * @var int */ private const CACHE_KEY_COMBINED_STYLES = 3; /** * This regular expression pattern will match any uninlinable at-rule with nested statements, along with any * whitespace immediately following. Currently, any at-rule apart from `@media` is considered uninlinable. The * first capturing group matches the at sign and identifier (e.g. `@font-face`). The second capturing group matches * the nested statements along with their enclosing curly brackets (i.e. `{...}`), and via `(?2)` will match deeper * nested blocks recursively. * * @var string */ private const UNINLINABLE_AT_RULE_MATCHER = '/(@(?!media\\b)[\\w\\-]++)[^\\{]*+(\\{[^\\{\\}]*+(?:(?2)[^\\{\\}]*+)*+\\})\\s*+/i'; /** * Regular expression component matching a static pseudo class in a selector, without the preceding ":", * for which the applicable elements can be determined (by converting the selector to an XPath expression). * (Contains alternation without a group and is intended to be placed within a capturing, non-capturing or lookahead * group, as appropriate for the usage context.) * * @var string */ private const PSEUDO_CLASS_MATCHER = 'empty|(?:first|last|nth(?:-last)?+|only)-(?:child|of-type)|not\\([[:ascii:]]*\\)'; /** * This regular expression componenet matches an `...of-type` pseudo class name, without the preceding ":". These * pseudo-classes can currently online be inlined if they have an associated type in the selector expression. * * @var string */ private const OF_TYPE_PSEUDO_CLASS_MATCHER = '(?:first|last|nth(?:-last)?+|only)-of-type'; /** * regular expression component to match a selector combinator * * @var string */ private const COMBINATOR_MATCHER = '(?:\\s++|\\s*+[>+~]\\s*+)(?=[[:alpha:]_\\-.#*:\\[])'; /** * @var bool[] */ private $excludedSelectors = []; /** * @var bool[] */ private $allowedMediaTypes = ['all' => true, 'screen' => true, 'print' => true]; /** * @var mixed[] */ private $caches = [ self::CACHE_KEY_CSS => [], self::CACHE_KEY_SELECTOR => [], self::CACHE_KEY_CSS_DECLARATIONS_BLOCK => [], self::CACHE_KEY_COMBINED_STYLES => [], ]; /** * @var CssSelectorConverter */ private $cssSelectorConverter = null; /** * the visited nodes with the XPath paths as array keys * * @var \DOMElement[] */ private $visitedNodes = []; /** * the styles to apply to the nodes with the XPath paths as array keys for the outer array * and the attribute names/values as key/value pairs for the inner array * * @var string[][] */ private $styleAttributesForNodes = []; /** * Determines whether the "style" attributes of tags in the the HTML passed to this class should be preserved. * If set to false, the value of the style attributes will be discarded. * * @var bool */ private $isInlineStyleAttributesParsingEnabled = true; /** * Determines whether the