public function testGetTagAttr() { $fesc = fesc($_ = "<&\"'>"); $tag = "\n <a data-value=1 fesc='{$fesc}' href='/about/' class=\"class\" target=_blank>About</a>\n "; $attr = getTagAttr($tag); $attrHref = getTagAttr($tag, 'href'); $attrNone = getTagAttr($tag, 'none'); $attrFesc = getTagAttr($tag, 'fesc'); $this->assertEquals($attr['href'], "/about/"); $this->assertEquals($attr['class'], "class"); $this->assertEquals($attr['target'], "_blank"); $this->assertEquals($attr['data-value'], "1"); $this->assertTrue($attrHref ? true : false); $this->assertFalse($attrNone ? true : false); $this->assertEquals($attrFesc, $_); }
function xpath($string, $query = '/*') { if (is_string($string)) { // SOME FIXES TO BE COMPATIBLE WITH XML $tags = 'area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr'; $string = preg_replace_callback("~<({$tags})\\b[^>]*>~", function ($m) { $last = mb_substr($m[0], -2); if ($last != "/>") { return rtrim(mb_substr($m[0], 0, -1)) . ' />'; } return $m[0]; }, $string); $string = preg_replace_callback('~<html\\b[^>]*>~', function ($m) { $attrs = getTagAttr($m[0]); if (!isset($attrs['xmlns'])) { return $m[0]; } unset($attrs['xmlns']); $attrs = prepareAttr($attrs); $attrs = $attrs ? " {$attrs}" : ''; return "<html{$attrs}>"; }, $string); } if (func_num_args() > 2) { $callback = func_get_arg(2); } $query = trim($query); if (!$query) { return array(); } $predefined = array('remove' => function ($tag) { $tag->parentNode->removeChild($tag); }, 'unwrap' => function ($tag) { if ($tag->hasChildNodes()) { $collector = array(); foreach ($tag->childNodes as $child) { $collector[] = $child; } for ($i = 0; $i < count($collector); $i++) { $tag->parentNode->insertBefore($collector[$i], $tag); } } $tag->parentNode->removeChild($tag); }); $query = preg_replace('~class\\((?P<class>.*?)\\)~i', 'contains(concat(" ",normalize-space(@class)," ")," $1 ")', $query); $query = preg_replace('~lower-case\\((?P<lower>.*?)\\)~i', 'translate($1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")', $query); if ($string instanceof DOMNode) { if ($string->nodeType === XML_TEXT_NODE) { if (func_num_args() === 1) { return $string->nodeValue; } elseif (func_num_args() === 2) { return array(); } else { return ''; } } $doc = $string->ownerDocument; $string = $doc->saveXML($string); } $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; if (isset($callback) and $callback === "preserveWhiteSpace") { unset($callback); $doc->preserveWhiteSpace = true; } $doc->formatOutput = true; libxml_use_internal_errors(true); $_ = $doc->loadXML($string); if (!$_) { $doc->loadHTML('<?xml encoding="UTF-8">' . $string); } $doc->normalizeDocument(); libxml_clear_errors(); $xpath = new DOMXPath($doc); $tags = $xpath->query($query); if (isset($callback) and !is_numeric($callback)) { if (!$tags instanceof DOMNodeList) { _log("XPATH: INVALID QUERY: {$query}", E_USER_WARNING); return ""; } if (is_string($callback) and isset($predefined[$callback])) { $callback = $predefined[$callback]; } $callback = function ($tag) use($callback) { $_ = $tag; while (isset($_->parentNode)) { $_ = $_->parentNode; } if ($_ instanceof DOMDocument) { $callback($tag); } }; $collector = array(); foreach ($tags as $tag) { $collector[] = $tag; } $tags = $collector; if ($callback === 'attr') { list($tag) = $tags; $tag = $doc->saveXML($tag); $qs = explode('@', $query); return getTagAttr($tag, $qs[count($qs) - 1]); } for ($i = 0; $i < count($tags); $i++) { $callback($tags[$i]); } return $doc->saveXML($doc->documentElement); } $return = array(); if (!$tags instanceof DOMNodeList) { _log("XPATH: INVALID QUERY: {$query}", E_USER_WARNING); return array(); } foreach ($tags as $tag) { $return[] = $doc->saveXML($tag); } if (isset($callback) and is_numeric($callback)) { $int = intval($callback); if ($int < 0) { $int = count($return) + $int; } return isset($return[$int]) ? $return[$int] : ''; } if (func_num_args() === 1) { return implode('', $return); } return $return; }