PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/sites/all/modules/contrib/querypath/QueryPath/QueryPath.php

https://bitbucket.org/antisocnet/drupal
PHP | 2253 lines | 1691 code | 543 blank | 19 comment | 295 complexity | 304126f4700613db5de52c4186d1c9f0 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. define('ML_EXP','/^[^<]*(<(.|\s)+>)[^>]*$/');
  3. require_once 'CssEventHandler.php';
  4. require_once 'QueryPathExtension.php';
  5. function qp($document = NULL, $string = NULL, $options = array()) {
  6. $qpClass = isset($options['QueryPath_class']) ? $options['QueryPath_class'] : 'QueryPath';
  7. $qp = new $qpClass($document, $string, $options);
  8. return $qp;
  9. }
  10. function htmlqp($document = NULL, $selector = NULL, $options = array()) {
  11. $options += array(
  12. 'ignore_parser_warnings' => TRUE,
  13. 'convert_to_encoding' => 'ISO-8859-1',
  14. 'convert_from_encoding' => 'auto',
  15. 'use_parser' => 'html',
  16. 'strip_low_ascii' => TRUE,
  17. );
  18. return @qp($document, $selector, $options);
  19. }
  20. class QueryPath implements IteratorAggregate {
  21. const VERSION = '2.1.0';
  22. const HTML_STUB = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  23. <html lang="en">
  24. <head>
  25. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  26. <title>Untitled</title>
  27. </head>
  28. <body></body>
  29. </html>';
  30. const XHTML_STUB = '<?xml version="1.0"?>
  31. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  32. <html xmlns="http://www.w3.org/1999/xhtml">
  33. <head>
  34. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  35. <title>Untitled</title>
  36. </head>
  37. <body></body>
  38. </html>';
  39. const DEFAULT_PARSER_FLAGS = NULL;
  40. private $errTypes = 771;
  41. protected $document = NULL;
  42. private $options = array(
  43. 'parser_flags' => NULL,
  44. 'omit_xml_declaration' => FALSE,
  45. 'replace_entities' => FALSE,
  46. 'exception_level' => 771,
  47. 'ignore_parser_warnings' => FALSE,
  48. );
  49. protected $matches = array();
  50. protected $last = array();
  51. private $ext = array();
  52. public $length = 0;
  53. public function __construct($document = NULL, $string = NULL, $options = array()) {
  54. $string = trim($string);
  55. $this->options = $options + QueryPathOptions::get() + $this->options;
  56. $parser_flags = isset($options['parser_flags']) ? $options['parser_flags'] : self::DEFAULT_PARSER_FLAGS;
  57. if (!empty($this->options['ignore_parser_warnings'])) {
  58. $this->errTypes = 257;
  59. }
  60. elseif (isset($this->options['exception_level'])) {
  61. $this->errTypes = $this->options['exception_level'];
  62. }
  63. if (empty($document)) {
  64. $this->document = isset($this->options['encoding']) ? new DOMDocument('1.0', $this->options['encoding']) : new DOMDocument();
  65. $this->setMatches(new SplObjectStorage());
  66. }
  67. elseif (is_object($document)) {
  68. if ($document instanceof QueryPath) {
  69. $this->matches = $document->get(NULL, TRUE);
  70. if ($this->matches->count() > 0)
  71. $this->document = $this->getFirstMatch()->ownerDocument;
  72. }
  73. elseif ($document instanceof DOMDocument) {
  74. $this->document = $document;
  75. $this->setMatches($document->documentElement);
  76. }
  77. elseif ($document instanceof DOMNode) {
  78. $this->document = $document->ownerDocument;
  79. $this->setMatches($document);
  80. }
  81. elseif ($document instanceof SimpleXMLElement) {
  82. $import = dom_import_simplexml($document);
  83. $this->document = $import->ownerDocument;
  84. $this->setMatches($import);
  85. }
  86. elseif ($document instanceof SplObjectStorage) {
  87. if ($document->count() == 0) {
  88. throw new QueryPathException('Cannot initialize QueryPath from an empty SplObjectStore');
  89. }
  90. $this->matches = $document;
  91. $this->document = $this->getFirstMatch()->ownerDocument;
  92. }
  93. else {
  94. throw new QueryPathException('Unsupported class type: ' . get_class($document));
  95. }
  96. }
  97. elseif (is_array($document)) {
  98. if (!empty($document) && $document[0] instanceof DOMNode) {
  99. $found = new SplObjectStorage();
  100. foreach ($document as $item) $found->attach($item);
  101. $this->setMatches($found);
  102. $this->document = $this->getFirstMatch()->ownerDocument;
  103. }
  104. }
  105. elseif ($this->isXMLish($document)) {
  106. $this->document = $this->parseXMLString($document);
  107. $this->setMatches($this->document->documentElement);
  108. }
  109. else {
  110. $context = empty($options['context']) ? NULL : $options['context'];
  111. $this->document = $this->parseXMLFile($document, $parser_flags, $context);
  112. $this->setMatches($this->document->documentElement);
  113. }
  114. if (isset($string) && strlen($string) > 0) {
  115. $this->find($string);
  116. }
  117. }
  118. public static function encodeDataURL($data, $mime = 'application/octet-stream', $context = NULL) {
  119. if (is_resource($data)) {
  120. $data = stream_get_contents($data);
  121. }
  122. elseif (filter_var($data, FILTER_VALIDATE_URL)) {
  123. $data = file_get_contents($data, FALSE, $context);
  124. }
  125. $encoded = base64_encode($data);
  126. return 'data:' . $mime . ';base64,' . $encoded;
  127. }
  128. public function getOptions() {
  129. return $this->options;
  130. }
  131. public function top($selector = NULL) {
  132. $this->setMatches($this->document->documentElement);
  133. return !empty($selector) ? $this->find($selector) : $this;
  134. }
  135. public function find($selector) {
  136. $ids = array();
  137. $regex = '/^#([\w-]+)$|^\.([\w-]+)$/';
  138. if (preg_match($regex, $selector, $ids) === 1) {
  139. if (!empty($ids[1])) {
  140. $xpath = new DOMXPath($this->document);
  141. foreach ($this->matches as $item) {
  142. if ($item->isSameNode($this->document->documentElement) ) {
  143. $xpathQuery = "//*[@id='{$ids[1]}']";
  144. }
  145. else {
  146. $xpathQuery = ".//*[@id='{$ids[1]}']";
  147. }
  148. $nl = $xpath->query($xpathQuery, $item);
  149. if ($nl->length > 0) {
  150. $this->setMatches($nl->item(0));
  151. break;
  152. }
  153. else {
  154. $this->noMatches();
  155. }
  156. }
  157. }
  158. else {
  159. $xpath = new DOMXPath($this->document);
  160. $found = new SplObjectStorage();
  161. foreach ($this->matches as $item) {
  162. if ($item->isSameNode($this->document->documentElement) ) {
  163. $xpathQuery = "//*[@class]";
  164. }
  165. else {
  166. $xpathQuery = ".//*[@class]";
  167. }
  168. $nl = $xpath->query($xpathQuery, $item);
  169. for ($i = 0; $i < $nl->length; ++$i) {
  170. $vals = explode(' ', $nl->item($i)->getAttribute('class'));
  171. if (in_array($ids[2], $vals)) $found->attach($nl->item($i));
  172. }
  173. }
  174. $this->setMatches($found);
  175. }
  176. return $this;
  177. }
  178. $query = new QueryPathCssEventHandler($this->matches);
  179. $query->find($selector);
  180. $this->setMatches($query->getMatches());
  181. return $this;
  182. }
  183. public function xpath($query) {
  184. $xpath = new DOMXPath($this->document);
  185. $found = new SplObjectStorage();
  186. foreach ($this->matches as $item) {
  187. $nl = $xpath->query($query, $item);
  188. if ($nl->length > 0) {
  189. for ($i = 0; $i < $nl->length; ++$i) $found->attach($nl->item($i));
  190. }
  191. }
  192. $this->setMatches($found);
  193. return $this;
  194. }
  195. public function size() {
  196. return $this->matches->count();
  197. }
  198. public function get($index = NULL, $asObject = FALSE) {
  199. if (isset($index)) {
  200. return ($this->size() > $index) ? $this->getNthMatch($index) : NULL;
  201. }
  202. if (!$asObject) {
  203. $matches = array();
  204. foreach ($this->matches as $m) $matches[] = $m;
  205. return $matches;
  206. }
  207. return $this->matches;
  208. }
  209. public function toArray() {
  210. return $this->get();
  211. }
  212. public function attr($name = NULL, $value = NULL) {
  213. if (is_null($name)) {
  214. if ($this->matches->count() == 0) return NULL;
  215. $ele = $this->getFirstMatch();
  216. $buffer = array();
  217. foreach ($ele->attributes as $name => $attrNode) {
  218. $buffer[$name] = $attrNode->value;
  219. }
  220. return $buffer;
  221. }
  222. if (is_array($name)) {
  223. foreach ($name as $k => $v) {
  224. foreach ($this->matches as $m) $m->setAttribute($k, $v);
  225. }
  226. return $this;
  227. }
  228. if (isset($value)) {
  229. foreach ($this->matches as $m) $m->setAttribute($name, $value);
  230. return $this;
  231. }
  232. if ($this->matches->count() == 0) return NULL;
  233. if ($name == 'nodeType') {
  234. return $this->getFirstMatch()->nodeType;
  235. }
  236. return $this->getFirstMatch()->getAttribute($name);
  237. }
  238. public function hasAttr($attrName) {
  239. foreach ($this->matches as $match) {
  240. if (!$match->hasAttribute($attrName)) return FALSE;
  241. }
  242. return TRUE;
  243. }
  244. public function css($name = NULL, $value = '') {
  245. if (empty($name)) {
  246. return $this->attr('style');
  247. }
  248. $css = array();
  249. foreach ($this->matches as $match) {
  250. $style = $match->getAttribute('style');
  251. if (!empty($style)) {
  252. $style_array = explode(';', $style);
  253. foreach ($style_array as $item) {
  254. $item = trim($item);
  255. if (strlen($item) == 0) continue;
  256. list($css_att, $css_val) = explode(':',$item, 2);
  257. $css[$css_att] = trim($css_val);
  258. }
  259. }
  260. }
  261. if (is_array($name)) {
  262. $css = array_merge($css, $name);
  263. }
  264. else {
  265. $css[$name] = $value;
  266. }
  267. $format = '%s: %s;';
  268. $css_string = '';
  269. foreach ($css as $n => $v) {
  270. $css_string .= sprintf($format, $n, trim($v));
  271. }
  272. $this->attr('style', $css_string);
  273. return $this;
  274. }
  275. public function dataURL($attr, $data = NULL, $mime = 'application/octet-stream', $context = NULL) {
  276. if (is_null($data)) {
  277. $data = $this->attr($attr);
  278. if (empty($data) || is_array($data) || strpos($data, 'data:') !== 0) {
  279. return;
  280. }
  281. $regex = '/^data:([a-zA-Z0-9]+)\/([a-zA-Z0-9]+);base64,(.*)$/';
  282. $matches = array();
  283. preg_match($regex, $data, $matches);
  284. if (!empty($matches)) {
  285. $result = array(
  286. 'mime' => $matches[1] . '/' . $matches[2],
  287. 'data' => base64_decode($matches[3]),
  288. );
  289. return $result;
  290. }
  291. }
  292. else {
  293. $attVal = self::encodeDataURL($data, $mime, $context);
  294. return $this->attr($attr, $attVal);
  295. }
  296. }
  297. public function removeAttr($name) {
  298. foreach ($this->matches as $m) {
  299. $m->removeAttribute($name);
  300. }
  301. return $this;
  302. }
  303. public function eq($index) {
  304. $this->setMatches($this->getNthMatch($index));
  305. return $this;
  306. }
  307. public function is($selector) {
  308. foreach ($this->matches as $m) {
  309. $q = new QueryPathCssEventHandler($m);
  310. if ($q->find($selector)->getMatches()->count()) {
  311. return TRUE;
  312. }
  313. }
  314. return FALSE;
  315. }
  316. public function filter($selector) {
  317. $found = new SplObjectStorage();
  318. foreach ($this->matches as $m) if (qp($m, NULL, $this->options)->is($selector)) $found->attach($m);
  319. $this->setMatches($found);
  320. return $this;
  321. }
  322. public function filterLambda($fn) {
  323. $function = create_function('$index, $item', $fn);
  324. $found = new SplObjectStorage();
  325. $i = 0;
  326. foreach ($this->matches as $item)
  327. if ($function($i++, $item) !== FALSE) $found->attach($item);
  328. $this->setMatches($found);
  329. return $this;
  330. }
  331. public function filterPreg($regex) {
  332. $found = new SplObjectStorage();
  333. foreach ($this->matches as $item) {
  334. if (preg_match($regex, $item->textContent) > 0) {
  335. $found->attach($item);
  336. }
  337. }
  338. $this->setMatches($found);
  339. return $this;
  340. }
  341. public function filterCallback($callback) {
  342. $found = new SplObjectStorage();
  343. $i = 0;
  344. if (is_callable($callback)) {
  345. foreach($this->matches as $item)
  346. if (call_user_func($callback, $i++, $item) !== FALSE) $found->attach($item);
  347. }
  348. else {
  349. throw new QueryPathException('The specified callback is not callable.');
  350. }
  351. $this->setMatches($found);
  352. return $this;
  353. }
  354. public function not($selector) {
  355. $found = new SplObjectStorage();
  356. if ($selector instanceof DOMElement) {
  357. foreach ($this->matches as $m) if ($m !== $selector) $found->attach($m);
  358. }
  359. elseif (is_array($selector)) {
  360. foreach ($this->matches as $m) {
  361. if (!in_array($m, $selector, TRUE)) $found->attach($m);
  362. }
  363. }
  364. elseif ($selector instanceof SplObjectStorage) {
  365. foreach ($this->matches as $m) if ($selector->contains($m)) $found->attach($m);
  366. }
  367. else {
  368. foreach ($this->matches as $m) if (!qp($m, NULL, $this->options)->is($selector)) $found->attach($m);
  369. }
  370. $this->setMatches($found);
  371. return $this;
  372. }
  373. public function index($subject) {
  374. $i = 0;
  375. foreach ($this->matches as $m) {
  376. if ($m === $subject) {
  377. return $i;
  378. }
  379. ++$i;
  380. }
  381. return FALSE;
  382. }
  383. public function map($callback) {
  384. $found = new SplObjectStorage();
  385. if (is_callable($callback)) {
  386. $i = 0;
  387. foreach ($this->matches as $item) {
  388. $c = call_user_func($callback, $i, $item);
  389. if (isset($c)) {
  390. if (is_array($c) || $c instanceof Iterable) {
  391. foreach ($c as $retval) {
  392. if (!is_object($retval)) {
  393. $tmp = new stdClass();
  394. $tmp->textContent = $retval;
  395. $retval = $tmp;
  396. }
  397. $found->attach($retval);
  398. }
  399. }
  400. else {
  401. if (!is_object($c)) {
  402. $tmp = new stdClass();
  403. $tmp->textContent = $c;
  404. $c = $tmp;
  405. }
  406. $found->attach($c);
  407. }
  408. }
  409. ++$i;
  410. }
  411. }
  412. else {
  413. throw new QueryPathException('Callback is not callable.');
  414. }
  415. $this->setMatches($found, FALSE);
  416. return $this;
  417. }
  418. public function slice($start, $length = 0) {
  419. $end = $length;
  420. $found = new SplObjectStorage();
  421. if ($start >= $this->size()) {
  422. $this->setMatches($found);
  423. return $this;
  424. }
  425. $i = $j = 0;
  426. foreach ($this->matches as $m) {
  427. if ($i >= $start) {
  428. if ($end > 0 && $j >= $end) {
  429. break;
  430. }
  431. $found->attach($m);
  432. ++$j;
  433. }
  434. ++$i;
  435. }
  436. $this->setMatches($found);
  437. return $this;
  438. }
  439. public function each($callback) {
  440. if (is_callable($callback)) {
  441. $i = 0;
  442. foreach ($this->matches as $item) {
  443. if (call_user_func($callback, $i, $item) === FALSE) return $this;
  444. ++$i;
  445. }
  446. }
  447. else {
  448. throw new QueryPathException('Callback is not callable.');
  449. }
  450. return $this;
  451. }
  452. public function eachLambda($lambda) {
  453. $index = 0;
  454. foreach ($this->matches as $item) {
  455. $fn = create_function('$index, &$item', $lambda);
  456. if ($fn($index, $item) === FALSE) return $this;
  457. ++$index;
  458. }
  459. return $this;
  460. }
  461. public function append($data) {
  462. $data = $this->prepareInsert($data);
  463. if (isset($data)) {
  464. if (empty($this->document->documentElement) && $this->matches->count() == 0) {
  465. $this->document->appendChild($data);
  466. $found = new SplObjectStorage();
  467. $found->attach($this->document->documentElement);
  468. $this->setMatches($found);
  469. }
  470. else {
  471. foreach ($this->matches as $m) {
  472. if ($data instanceof DOMDocumentFragment) {
  473. foreach ($data->childNodes as $n)
  474. $m->appendChild($n->cloneNode(TRUE));
  475. }
  476. else {
  477. $m->appendChild($data->cloneNode(TRUE));
  478. }
  479. }
  480. }
  481. }
  482. return $this;
  483. }
  484. public function appendTo(QueryPath $dest) {
  485. foreach ($this->matches as $m) $dest->append($m);
  486. return $this;
  487. }
  488. public function prepend($data) {
  489. $data = $this->prepareInsert($data);
  490. if (isset($data)) {
  491. foreach ($this->matches as $m) {
  492. $ins = $data->cloneNode(TRUE);
  493. if ($m->hasChildNodes())
  494. $m->insertBefore($ins, $m->childNodes->item(0));
  495. else
  496. $m->appendChild($ins);
  497. }
  498. }
  499. return $this;
  500. }
  501. public function prependTo(QueryPath $dest) {
  502. foreach ($this->matches as $m) $dest->prepend($m);
  503. return $this;
  504. }
  505. public function before($data) {
  506. $data = $this->prepareInsert($data);
  507. foreach ($this->matches as $m) {
  508. $ins = $data->cloneNode(TRUE);
  509. $m->parentNode->insertBefore($ins, $m);
  510. }
  511. return $this;
  512. }
  513. public function insertBefore(QueryPath $dest) {
  514. foreach ($this->matches as $m) $dest->before($m);
  515. return $this;
  516. }
  517. public function insertAfter(QueryPath $dest) {
  518. foreach ($this->matches as $m) $dest->after($m);
  519. return $this;
  520. }
  521. public function after($data) {
  522. $data = $this->prepareInsert($data);
  523. foreach ($this->matches as $m) {
  524. $ins = $data->cloneNode(TRUE);
  525. if (isset($m->nextSibling))
  526. $m->parentNode->insertBefore($ins, $m->nextSibling);
  527. else
  528. $m->parentNode->appendChild($ins);
  529. }
  530. return $this;
  531. }
  532. public function replaceWith($new) {
  533. $data = $this->prepareInsert($new);
  534. $found = new SplObjectStorage();
  535. foreach ($this->matches as $m) {
  536. $parent = $m->parentNode;
  537. $parent->insertBefore($data->cloneNode(TRUE), $m);
  538. $found->attach($parent->removeChild($m));
  539. }
  540. $this->setMatches($found);
  541. return $this;
  542. }
  543. public function unwrap() {
  544. $parents = new SplObjectStorage();
  545. foreach ($this->matches as $m) {
  546. if ($m->isSameNode($m->ownerDocument->documentElement)) {
  547. throw new QueryPathException('Cannot unwrap the root element.');
  548. }
  549. $parent = $m->parentNode;
  550. $old = $parent->removeChild($m);
  551. $parent->parentNode->insertBefore($old, $parent);
  552. $parents->attach($parent);
  553. }
  554. foreach ($parents as $ele) {
  555. $ele->parentNode->removeChild($ele);
  556. }
  557. return $this;
  558. }
  559. public function wrap($markup) {
  560. $data = $this->prepareInsert($markup);
  561. if (empty($data)) {
  562. return $this;
  563. }
  564. foreach ($this->matches as $m) {
  565. $copy = $data->firstChild->cloneNode(TRUE);
  566. if ($copy->hasChildNodes()) {
  567. $deepest = $this->deepestNode($copy);
  568. $bottom = $deepest[0];
  569. }
  570. else
  571. $bottom = $copy;
  572. $parent = $m->parentNode;
  573. $parent->insertBefore($copy, $m);
  574. $m = $parent->removeChild($m);
  575. $bottom->appendChild($m);
  576. }
  577. return $this;
  578. }
  579. public function wrapAll($markup) {
  580. if ($this->matches->count() == 0) return;
  581. $data = $this->prepareInsert($markup);
  582. if (empty($data)) {
  583. return $this;
  584. }
  585. if ($data->hasChildNodes()) {
  586. $deepest = $this->deepestNode($data);
  587. $bottom = $deepest[0];
  588. }
  589. else
  590. $bottom = $data;
  591. $first = $this->getFirstMatch();
  592. $parent = $first->parentNode;
  593. $parent->insertBefore($data, $first);
  594. foreach ($this->matches as $m) {
  595. $bottom->appendChild($m->parentNode->removeChild($m));
  596. }
  597. return $this;
  598. }
  599. public function wrapInner($markup) {
  600. $data = $this->prepareInsert($markup);
  601. if (empty($data)) return $this;
  602. if ($data->hasChildNodes()) {
  603. $deepest = $this->deepestNode($data);
  604. $bottom = $deepest[0];
  605. }
  606. else
  607. $bottom = $data;
  608. foreach ($this->matches as $m) {
  609. if ($m->hasChildNodes()) {
  610. while($m->firstChild) {
  611. $kid = $m->removeChild($m->firstChild);
  612. $bottom->appendChild($kid);
  613. }
  614. }
  615. $m->appendChild($data);
  616. }
  617. return $this;
  618. }
  619. public function deepest() {
  620. $deepest = 0;
  621. $winner = new SplObjectStorage();
  622. foreach ($this->matches as $m) {
  623. $local_deepest = 0;
  624. $local_ele = $this->deepestNode($m, 0, NULL, $local_deepest);
  625. if ($local_deepest > $deepest) {
  626. $winner = new SplObjectStorage();
  627. foreach ($local_ele as $lele) $winner->attach($lele);
  628. $deepest = $local_deepest;
  629. }
  630. elseif ($local_deepest == $deepest) {
  631. foreach ($local_ele as $lele)
  632. $winner->attach($lele);
  633. }
  634. }
  635. $this->setMatches($winner);
  636. return $this;
  637. }
  638. protected function deepestNode(DOMNode $ele, $depth = 0, $current = NULL, &$deepest = NULL) {
  639. if (!isset($current)) $current = array($ele);
  640. if (!isset($deepest)) $deepest = $depth;
  641. if ($ele->hasChildNodes()) {
  642. foreach ($ele->childNodes as $child) {
  643. if ($child->nodeType === XML_ELEMENT_NODE) {
  644. $current = $this->deepestNode($child, $depth + 1, $current, $deepest);
  645. }
  646. }
  647. }
  648. elseif ($depth > $deepest) {
  649. $current = array($ele);
  650. $deepest = $depth;
  651. }
  652. elseif ($depth === $deepest) {
  653. $current[] = $ele;
  654. }
  655. return $current;
  656. }
  657. /**
  658. * Prepare an item for insertion into a DOM.
  659. *
  660. * This handles a variety of boilerplate tasks that need doing before an
  661. * indeterminate object can be inserted into a DOM tree.
  662. * - If item is a string, this is converted into a document fragment and returned.
  663. * - If item is a QueryPath, then the first item is retrieved and this call function
  664. * is called recursivel.
  665. * - If the item is a DOMNode, it is imported into the current DOM if necessary.
  666. * - If the item is a SimpleXMLElement, it is converted into a DOM node and then
  667. * imported.
  668. *
  669. * @param mixed $item
  670. * Item to prepare for insert.
  671. * @return mixed
  672. * Returns the prepared item.
  673. * @throws QueryPathException
  674. * Thrown if the object passed in is not of a supprted object type.
  675. */
  676. protected function prepareInsert($item) {
  677. if(empty($item)) {
  678. return;
  679. }
  680. elseif (is_string($item)) {
  681. if ($this->options['replace_entities']) {
  682. $item = QueryPathEntities::replaceAllEntities($item);
  683. }
  684. $frag = $this->document->createDocumentFragment();
  685. try {
  686. set_error_handler(array('QueryPathParseException', 'initializeFromError'), $this->errTypes);
  687. $frag->appendXML($item);
  688. }
  689. catch (Exception $e) {
  690. restore_error_handler();
  691. throw $e;
  692. }
  693. restore_error_handler();
  694. return $frag;
  695. }
  696. elseif ($item instanceof QueryPath) {
  697. if ($item->size() == 0)
  698. return;
  699. return $this->prepareInsert($item->get(0));
  700. }
  701. elseif ($item instanceof DOMNode) {
  702. if ($item->ownerDocument !== $this->document) {
  703. $item = $this->document->importNode($item, TRUE);
  704. }
  705. return $item;
  706. }
  707. elseif ($item instanceof SimpleXMLElement) {
  708. $element = dom_import_simplexml($item);
  709. return $this->document->importNode($element, TRUE);
  710. }
  711. throw new QueryPathException("Cannot prepare item of unsupported type: " . gettype($item));
  712. }
  713. public function tag() {
  714. return ($this->size() > 0) ? $this->getFirstMatch()->tagName : '';
  715. }
  716. public function remove($selector = NULL) {
  717. if(!empty($selector))
  718. $this->find($selector);
  719. $found = new SplObjectStorage();
  720. foreach ($this->matches as $item) {
  721. $found->attach($item->parentNode->removeChild($item));
  722. }
  723. $this->setMatches($found);
  724. return $this;
  725. }
  726. public function replaceAll($selector, DOMDocument $document) {
  727. $replacement = $this->size() > 0 ? $this->getFirstMatch() : $this->document->createTextNode('');
  728. $c = new QueryPathCssEventHandler($document);
  729. $c->find($selector);
  730. $temp = $c->getMatches();
  731. foreach ($temp as $item) {
  732. $node = $replacement->cloneNode();
  733. $node = $document->importNode($node);
  734. $item->parentNode->replaceChild($node, $item);
  735. }
  736. return qp($document, NULL, $this->options);
  737. }
  738. public function add($selector) {
  739. $this->last = $this->matches;
  740. foreach (qp($this->document, $selector, $this->options)->get() as $item)
  741. $this->matches->attach($item);
  742. return $this;
  743. }
  744. public function end() {
  745. $this->matches = $this->last;
  746. $this->last = new SplObjectStorage();
  747. return $this;
  748. }
  749. public function andSelf() {
  750. $last = $this->matches;
  751. foreach ($this->last as $item) $this->matches->attach($item);
  752. $this->last = $last;
  753. return $this;
  754. }
  755. public function removeChildren() {
  756. foreach ($this->matches as $m) {
  757. while($kid = $m->firstChild) {
  758. $m->removeChild($kid);
  759. }
  760. }
  761. return $this;
  762. }
  763. public function children($selector = NULL) {
  764. $found = new SplObjectStorage();
  765. foreach ($this->matches as $m) {
  766. foreach($m->childNodes as $c) {
  767. if ($c->nodeType == XML_ELEMENT_NODE) $found->attach($c);
  768. }
  769. }
  770. if (empty($selector)) {
  771. $this->setMatches($found);
  772. }
  773. else {
  774. $this->matches = $found;
  775. $this->filter($selector);
  776. }
  777. return $this;
  778. }
  779. public function contents() {
  780. $found = new SplObjectStorage();
  781. foreach ($this->matches as $m) {
  782. foreach ($m->childNodes as $c) {
  783. $found->attach($c);
  784. }
  785. }
  786. $this->setMatches($found);
  787. return $this;
  788. }
  789. public function siblings($selector = NULL) {
  790. $found = new SplObjectStorage();
  791. foreach ($this->matches as $m) {
  792. $parent = $m->parentNode;
  793. foreach ($parent->childNodes as $n) {
  794. if ($n->nodeType == XML_ELEMENT_NODE && $n !== $m) {
  795. $found->attach($n);
  796. }
  797. }
  798. }
  799. if (empty($selector)) {
  800. $this->setMatches($found);
  801. }
  802. else {
  803. $this->matches = $found;
  804. $this->filter($selector);
  805. }
  806. return $this;
  807. }
  808. public function closest($selector) {
  809. $found = new SplObjectStorage();
  810. foreach ($this->matches as $m) {
  811. if (qp($m, NULL, $this->options)->is($selector) > 0) {
  812. $found->attach($m);
  813. }
  814. else {
  815. while ($m->parentNode->nodeType !== XML_DOCUMENT_NODE) {
  816. $m = $m->parentNode;
  817. if ($m->nodeType === XML_ELEMENT_NODE && qp($m, NULL, $this->options)->is($selector) > 0) {
  818. $found->attach($m);
  819. break;
  820. }
  821. }
  822. }
  823. }
  824. $this->setMatches($found);
  825. return $this;
  826. }
  827. public function parent($selector = NULL) {
  828. $found = new SplObjectStorage();
  829. foreach ($this->matches as $m) {
  830. while ($m->parentNode->nodeType !== XML_DOCUMENT_NODE) {
  831. $m = $m->parentNode;
  832. if ($m->nodeType === XML_ELEMENT_NODE) {
  833. if (!empty($selector)) {
  834. if (qp($m, NULL, $this->options)->is($selector) > 0) {
  835. $found->attach($m);
  836. break;
  837. }
  838. }
  839. else {
  840. $found->attach($m);
  841. break;
  842. }
  843. }
  844. }
  845. }
  846. $this->setMatches($found);
  847. return $this;
  848. }
  849. public function parents($selector = NULL) {
  850. $found = new SplObjectStorage();
  851. foreach ($this->matches as $m) {
  852. while ($m->parentNode->nodeType !== XML_DOCUMENT_NODE) {
  853. $m = $m->parentNode;
  854. if ($m->nodeType === XML_ELEMENT_NODE) {
  855. if (!empty($selector)) {
  856. if (qp($m, NULL, $this->options)->is($selector) > 0)
  857. $found->attach($m);
  858. }
  859. else
  860. $found->attach($m);
  861. }
  862. }
  863. }
  864. $this->setMatches($found);
  865. return $this;
  866. }
  867. public function html($markup = NULL) {
  868. if (isset($markup)) {
  869. if ($this->options['replace_entities']) {
  870. $markup = QueryPathEntities::replaceAllEntities($markup);
  871. }
  872. $doc = $this->document->createDocumentFragment();
  873. $doc->appendXML($markup);
  874. $this->removeChildren();
  875. $this->append($doc);
  876. return $this;
  877. }
  878. $length = $this->size();
  879. if ($length == 0) {
  880. return NULL;
  881. }
  882. $first = $this->getFirstMatch();
  883. if (!($first instanceof DOMNode)) {
  884. return NULL;
  885. }
  886. if(!$first->ownerDocument->documentElement) {
  887. return NULL;
  888. }
  889. if ($first instanceof DOMDocument || $first->isSameNode($first->ownerDocument->documentElement)) {
  890. return $this->document->saveHTML();
  891. }
  892. return $this->document->saveXML($first);
  893. }
  894. public function innerHTML() {
  895. return $this->innerXML();
  896. }
  897. public function innerXHTML() {
  898. $length = $this->size();
  899. if ($length == 0) {
  900. return NULL;
  901. }
  902. $first = $this->getFirstMatch();
  903. if (!($first instanceof DOMNode)) {
  904. return NULL;
  905. }
  906. elseif (!$first->hasChildNodes()) {
  907. return '';
  908. }
  909. $buffer = '';
  910. foreach ($first->childNodes as $child) {
  911. $buffer .= $this->document->saveXML($child, LIBXML_NOEMPTYTAG);
  912. }
  913. return $buffer;
  914. }
  915. public function innerXML() {
  916. $length = $this->size();
  917. if ($length == 0) {
  918. return NULL;
  919. }
  920. $first = $this->getFirstMatch();
  921. if (!($first instanceof DOMNode)) {
  922. return NULL;
  923. }
  924. elseif (!$first->hasChildNodes()) {
  925. return '';
  926. }
  927. $buffer = '';
  928. foreach ($first->childNodes as $child) {
  929. $buffer .= $this->document->saveXML($child);
  930. }
  931. return $buffer;
  932. }
  933. public function textImplode($sep = ', ', $filterEmpties = TRUE) {
  934. $tmp = array();
  935. foreach ($this->matches as $m) {
  936. $txt = $m->textContent;
  937. $trimmed = trim($txt);
  938. if ($filterEmpties) {
  939. if (strlen($trimmed) > 0) $tmp[] = $txt;
  940. }
  941. else {
  942. $tmp[] = $txt;
  943. }
  944. }
  945. return implode($sep, $tmp);
  946. }
  947. public function text($text = NULL) {
  948. if (isset($text)) {
  949. $this->removeChildren();
  950. $textNode = $this->document->createTextNode($text);
  951. foreach ($this->matches as $m) $m->appendChild($textNode);
  952. return $this;
  953. }
  954. $buf = '';
  955. foreach ($this->matches as $m) $buf .= $m->textContent;
  956. return $buf;
  957. }
  958. public function textBefore($text = NULL) {
  959. if (isset($text)) {
  960. $textNode = $this->document->createTextNode($text);
  961. return $this->before($textNode);
  962. }
  963. $buffer = '';
  964. foreach ($this->matches as $m) {
  965. $p = $m;
  966. while (isset($p->previousSibling) && $p->previousSibling->nodeType == XML_TEXT_NODE) {
  967. $p = $p->previousSibling;
  968. $buffer .= $p->textContent;
  969. }
  970. }
  971. return $buffer;
  972. }
  973. public function textAfter($text = NULL) {
  974. if (isset($text)) {
  975. $textNode = $this->document->createTextNode($text);
  976. return $this->after($textNode);
  977. }
  978. $buffer = '';
  979. foreach ($this->matches as $m) {
  980. $n = $m;
  981. while (isset($n->nextSibling) && $n->nextSibling->nodeType == XML_TEXT_NODE) {
  982. $n = $n->nextSibling;
  983. $buffer .= $n->textContent;
  984. }
  985. }
  986. return $buffer;
  987. }
  988. public function val($value = NULL) {
  989. if (isset($value)) {
  990. $this->attr('value', $value);
  991. return $this;
  992. }
  993. return $this->attr('value');
  994. }
  995. public function xhtml($markup = NULL) {
  996. $omit_xml_decl = $this->options['omit_xml_declaration'];
  997. if ($markup === TRUE) {
  998. $omit_xml_decl = TRUE;
  999. }
  1000. elseif (isset($markup)) {
  1001. return $this->xml($markup);
  1002. }
  1003. $length = $this->size();
  1004. if ($length == 0) {
  1005. return NULL;
  1006. }
  1007. $first = $this->getFirstMatch();
  1008. if (!($first instanceof DOMNode)) {
  1009. return NULL;
  1010. }
  1011. if ($first instanceof DOMDocument || $first->isSameNode($first->ownerDocument->documentElement)) {
  1012. return ($omit_xml_decl ? $this->document->saveXML($first->ownerDocument->documentElement, LIBXML_NOEMPTYTAG) : $this->document->saveXML(NULL, LIBXML_NOEMPTYTAG));
  1013. }
  1014. return $this->document->saveXML($first, LIBXML_NOEMPTYTAG);
  1015. }
  1016. public function xml($markup = NULL) {
  1017. $omit_xml_decl = $this->options['omit_xml_declaration'];
  1018. if ($markup === TRUE) {
  1019. $omit_xml_decl = TRUE;
  1020. }
  1021. elseif (isset($markup)) {
  1022. if ($this->options['replace_entities']) {
  1023. $markup = QueryPathEntities::replaceAllEntities($markup);
  1024. }
  1025. $doc = $this->document->createDocumentFragment();
  1026. $doc->appendXML($markup);
  1027. $this->removeChildren();
  1028. $this->append($doc);
  1029. return $this;
  1030. }
  1031. $length = $this->size();
  1032. if ($length == 0) {
  1033. return NULL;
  1034. }
  1035. $first = $this->getFirstMatch();
  1036. if (!($first instanceof DOMNode)) {
  1037. return NULL;
  1038. }
  1039. if ($first instanceof DOMDocument || $first->isSameNode($first->ownerDocument->documentElement)) {
  1040. return ($omit_xml_decl ? $this->document->saveXML($first->ownerDocument->documentElement) : $this->document->saveXML());
  1041. }
  1042. return $this->document->saveXML($first);
  1043. }
  1044. public function writeXML($path = NULL, $options = NULL) {
  1045. if ($path == NULL) {
  1046. print $this->document->saveXML(NULL, $options);
  1047. }
  1048. else {
  1049. try {
  1050. set_error_handler(array('QueryPathIOException', 'initializeFromError'));
  1051. $this->document->save($path, $options);
  1052. }
  1053. catch (Exception $e) {
  1054. restore_error_handler();
  1055. throw $e;
  1056. }
  1057. restore_error_handler();
  1058. }
  1059. return $this;
  1060. }
  1061. public function writeHTML($path = NULL) {
  1062. if ($path == NULL) {
  1063. print $this->document->saveHTML();
  1064. }
  1065. else {
  1066. try {
  1067. set_error_handler(array('QueryPathParseException', 'initializeFromError'));
  1068. $this->document->saveHTMLFile($path);
  1069. }
  1070. catch (Exception $e) {
  1071. restore_error_handler();
  1072. throw $e;
  1073. }
  1074. restore_error_handler();
  1075. }
  1076. return $this;
  1077. }
  1078. public function writeXHTML($path = NULL) {
  1079. return $this->writeXML($path, LIBXML_NOEMPTYTAG);
  1080. }
  1081. public function next($selector = NULL) {
  1082. $found = new SplObjectStorage();
  1083. foreach ($this->matches as $m) {
  1084. while (isset($m->nextSibling)) {
  1085. $m = $m->nextSibling;
  1086. if ($m->nodeType === XML_ELEMENT_NODE) {
  1087. if (!empty($selector)) {
  1088. if (qp($m, NULL, $this->options)->is($selector) > 0) {
  1089. $found->attach($m);
  1090. break;
  1091. }
  1092. }
  1093. else {
  1094. $found->attach($m);
  1095. break;
  1096. }
  1097. }
  1098. }
  1099. }
  1100. $this->setMatches($found);
  1101. return $this;
  1102. }
  1103. public function nextAll($selector = NULL) {
  1104. $found = new SplObjectStorage();
  1105. foreach ($this->matches as $m) {
  1106. while (isset($m->nextSibling)) {
  1107. $m = $m->nextSibling;
  1108. if ($m->nodeType === XML_ELEMENT_NODE) {
  1109. if (!empty($selector)) {
  1110. if (qp($m, NULL, $this->options)->is($selector) > 0) {
  1111. $found->attach($m);
  1112. }
  1113. }
  1114. else {
  1115. $found->attach($m);
  1116. }
  1117. }
  1118. }
  1119. }
  1120. $this->setMatches($found);
  1121. return $this;
  1122. }
  1123. public function prev($selector = NULL) {
  1124. $found = new SplObjectStorage();
  1125. foreach ($this->matches as $m) {
  1126. while (isset($m->previousSibling)) {
  1127. $m = $m->previousSibling;
  1128. if ($m->nodeType === XML_ELEMENT_NODE) {
  1129. if (!empty($selector)) {
  1130. if (qp($m, NULL, $this->options)->is($selector)) {
  1131. $found->attach($m);
  1132. break;
  1133. }
  1134. }
  1135. else {
  1136. $found->attach($m);
  1137. break;
  1138. }
  1139. }
  1140. }
  1141. }
  1142. $this->setMatches($found);
  1143. return $this;
  1144. }
  1145. public function prevAll($selector = NULL) {
  1146. $found = new SplObjectStorage();
  1147. foreach ($this->matches as $m) {
  1148. while (isset($m->previousSibling)) {
  1149. $m = $m->previousSibling;
  1150. if ($m->nodeType === XML_ELEMENT_NODE) {
  1151. if (!empty($selector)) {
  1152. if (qp($m, NULL, $this->options)->is($selector)) {
  1153. $found->attach($m);
  1154. }
  1155. }
  1156. else {
  1157. $found->attach($m);
  1158. }
  1159. }
  1160. }
  1161. }
  1162. $this->setMatches($found);
  1163. return $this;
  1164. }
  1165. public function peers($selector = NULL) {
  1166. $found = new SplObjectStorage();
  1167. foreach ($this->matches as $m) {
  1168. foreach ($m->parentNode->childNodes as $kid) {
  1169. if ($kid->nodeType == XML_ELEMENT_NODE && $m !== $kid) {
  1170. if (!empty($selector)) {
  1171. if (qp($kid, NULL, $this->options)->is($selector)) {
  1172. $found->attach($kid);
  1173. }
  1174. }
  1175. else {
  1176. $found->attach($kid);
  1177. }
  1178. }
  1179. }
  1180. }
  1181. $this->setMatches($found);
  1182. return $this;
  1183. }
  1184. public function addClass($class) {
  1185. foreach ($this->matches as $m) {
  1186. if ($m->hasAttribute('class')) {
  1187. $val = $m->getAttribute('class');
  1188. $m->setAttribute('class', $val . ' ' . $class);
  1189. }
  1190. else {
  1191. $m->setAttribute('class', $class);
  1192. }
  1193. }
  1194. return $this;
  1195. }
  1196. public function removeClass($class) {
  1197. foreach ($this->matches as $m) {
  1198. if ($m->hasAttribute('class')) {
  1199. $vals = explode(' ', $m->getAttribute('class'));
  1200. if (in_array($class, $vals)) {
  1201. $buf = array();
  1202. foreach ($vals as $v) {
  1203. if ($v != $class) $buf[] = $v;
  1204. }
  1205. if (count($buf) == 0)
  1206. $m->removeAttribute('class');
  1207. else
  1208. $m->setAttribute('class', implode(' ', $buf));
  1209. }
  1210. }
  1211. }
  1212. return $this;
  1213. }
  1214. public function hasClass($class) {
  1215. foreach ($this->matches as $m) {
  1216. if ($m->hasAttribute('class')) {
  1217. $vals = explode(' ', $m->getAttribute('class'));
  1218. if (in_array($class, $vals)) return TRUE;
  1219. }
  1220. }
  1221. return FALSE;
  1222. }
  1223. public function branch($selector = NULL) {
  1224. $temp = qp($this->matches, NULL, $this->options);
  1225. if (isset($selector)) $temp->find($selector);
  1226. return $temp;
  1227. }
  1228. public function cloneAll() {
  1229. $found = new SplObjectStorage();
  1230. foreach ($this->matches as $m) $found->attach($m->cloneNode(TRUE));
  1231. $this->setMatches($found, FALSE);
  1232. return $this;
  1233. }
  1234. public function __clone() {
  1235. $this->cloneAll();
  1236. }
  1237. public function detach($selector = NULL) {
  1238. if(!empty($selector))
  1239. $this->find($selector);
  1240. $found = new SplObjectStorage();
  1241. $this->last = $this->matches;
  1242. foreach ($this->matches as $item) {
  1243. $found->attach($item->parentNode->removeChild($item));
  1244. }
  1245. $this->setMatches($found);
  1246. return $this;
  1247. }
  1248. public function attach(QueryPath $dest) {
  1249. foreach ($this->last as $m) $dest->append($m);
  1250. return $this;
  1251. }
  1252. public function has($contained) {
  1253. $found = new SplObjectStorage();
  1254. $nodes = array();
  1255. if (is_string($contained)) {
  1256. $nodes = $this->branch($contained)->get();
  1257. }
  1258. elseif ($contained instanceof DOMNode) {
  1259. $nodes = array($contained);
  1260. }
  1261. foreach ($nodes as $original_node) {
  1262. $node = $original_node;
  1263. while (!empty($node)) {
  1264. if ($this->matches->contains($node)) {
  1265. $found->attach($node);
  1266. }
  1267. $node = $node->parentNode;
  1268. }
  1269. }
  1270. $this->setMatches($found);
  1271. return $this;
  1272. }
  1273. public function emptyElement() {
  1274. $this->removeChildren();
  1275. return $this;
  1276. }
  1277. public function even() {
  1278. $found = new SplObjectStorage();
  1279. $even = false;
  1280. foreach ($this->matches as $m) {
  1281. if ($even && $m->nodeType == XML_ELEMENT_NODE) $found->attach($m);
  1282. $even = ($even) ? false : true;
  1283. }
  1284. $this->setMatches($found);
  1285. $this->matches = $found;
  1286. return $this;
  1287. }
  1288. public function odd() {
  1289. $found = new SplObjectStorage();
  1290. $odd = true;
  1291. foreach ($this->matches as $m) {
  1292. if ($odd && $m->nodeType == XML_ELEMENT_NODE) $found->attach($m);
  1293. $odd = ($odd) ? false : true;
  1294. }
  1295. $this->setMatches($found);
  1296. $this->matches = $found;
  1297. return $this;
  1298. }
  1299. public function first() {
  1300. $found = new SplObjectStorage();
  1301. foreach ($this->matches as $m) {
  1302. if ($m->nodeType == XML_ELEMENT_NODE) {
  1303. $found->attach($m);
  1304. break;
  1305. }
  1306. }
  1307. $this->setMatches($found);
  1308. $this->matches = $found;
  1309. return $this;
  1310. }
  1311. public function firstChild() {
  1312. $found = new SplObjectStorage();
  1313. $flag = false;
  1314. foreach ($this->matches as $m) {
  1315. foreach($m->childNodes as $c) {
  1316. if ($c->nodeType == XML_ELEMENT_NODE) {
  1317. $found->attach($c);
  1318. $flag = true;
  1319. break;
  1320. }
  1321. }
  1322. if($flag) break;
  1323. }
  1324. $this->setMatches($found);
  1325. $this->matches = $found;
  1326. return $this;
  1327. }
  1328. public function last() {
  1329. $found = new SplObjectStorage();
  1330. $item = null;
  1331. foreach ($this->matches as $m) {
  1332. if ($m->nodeType == XML_ELEMENT_NODE) {
  1333. $item = $m;
  1334. }
  1335. }
  1336. if ($item) {
  1337. $found->attach($item);
  1338. }
  1339. $this->setMatches($found);
  1340. $this->matches = $found;
  1341. return $this;
  1342. }
  1343. public function lastChild() {
  1344. $found = new SplObjectStorage();
  1345. $item = null;
  1346. foreach ($this->matches as $m) {
  1347. foreach($m->childNodes as $c) {
  1348. if ($c->nodeType == XML_ELEMENT_NODE) {
  1349. $item = $c;
  1350. }
  1351. }
  1352. if ($item) {
  1353. $found->attach($item);
  1354. $item = null;
  1355. }
  1356. }
  1357. $this->setMatches($found);
  1358. $this->matches = $found;
  1359. return $this;
  1360. }
  1361. public function nextUntil($selector = NULL) {
  1362. $found = new SplObjectStorage();
  1363. foreach ($this->matches as $m) {
  1364. while (isset($m->nextSibling)) {
  1365. $m = $m->nextSibling;
  1366. if ($m->nodeType === XML_ELEMENT_NODE) {
  1367. if (!empty($selector)) {
  1368. if (qp($m, NULL, $this->options)->is($selector) > 0) {
  1369. break;
  1370. }
  1371. else {
  1372. $found->attach($m);
  1373. }
  1374. }
  1375. else {
  1376. $found->attach($m);
  1377. }
  1378. }
  1379. }
  1380. }
  1381. $this->setMatches($found);
  1382. return $this;
  1383. }
  1384. public function prevUntil($selector = NULL) {
  1385. $found = new SplObjectStorage();
  1386. foreach ($this->matches as $m) {
  1387. while (isset($m->previousSibling)) {
  1388. $m = $m->previousSibling;
  1389. if ($m->nodeType === XML_ELEMENT_NODE) {
  1390. if (!empty($selector) && qp($m, NULL, $this->options)->is($selector))
  1391. break;
  1392. else
  1393. $found->attach($m);
  1394. }
  1395. }
  1396. }
  1397. $this->setMatches($found);
  1398. return $this;
  1399. }
  1400. public function parentsUntil($selector = NULL) {
  1401. $found = new SplObjectStorage();
  1402. foreach ($this->matches as $m) {
  1403. while ($m->parentNode->nodeType !== XML_DOCUMENT_NODE) {
  1404. $m = $m->parentNode;
  1405. if ($m->nodeType === XML_ELEMENT_NODE) {
  1406. if (!empty($selector)) {
  1407. if (qp($m, NULL, $this->options)->is($selector) > 0)
  1408. break;
  1409. else
  1410. $found->attach($m);
  1411. }
  1412. else
  1413. $found->attach($m);
  1414. }
  1415. }
  1416. }
  1417. $this->setMatches($found);
  1418. return $this;
  1419. }
  1420. protected function isXMLish($string) {
  1421. return (strpos($string, '<') !== FALSE && strpos($string, '>') !== FALSE);
  1422. }
  1423. private function parseXMLString($string, $flags = NULL) {
  1424. $document = new DOMDocument('1.0');
  1425. $lead = strtolower(substr($string, 0, 5));
  1426. try {
  1427. set_error_handler(array('QueryPathParseException', 'initializeFromError'), $this->errTypes);
  1428. if (isset($this->options['convert_to_encoding'])) {
  1429. $from_enc = isset($this->options['convert_from_encoding']) ? $this->options['convert_from_encoding'] : 'auto';
  1430. $to_enc = $this->options['convert_to_encoding'];
  1431. if (function_exists('mb_convert_encoding')) {
  1432. $string = mb_convert_encoding($string, $to_enc, $from_enc);
  1433. }
  1434. }
  1435. if (!empty($this->options['strip_low_ascii'])) {
  1436. $string = filter_var($string, FILTER_UNSAFE_RAW, FILTER_FLAG_ENCODE_LOW);
  1437. }
  1438. if (empty($this->options['use_parser'])) {
  1439. $useParser = '';
  1440. }
  1441. else {
  1442. $useParser = strtolower($this->options['use_parser']);
  1443. }
  1444. if ($useParser == 'html') {
  1445. $document->loadHTML($string);
  1446. }
  1447. elseif ($lead == '<?xml' || $useParser == 'xml') {
  1448. if ($this->options['replace_entities']) {
  1449. $string = QueryPathEntities::replaceAllEntities($string);
  1450. }
  1451. $document->loadXML($string, $flags);
  1452. }
  1453. else {
  1454. $document->loadHTML($string);
  1455. }
  1456. }
  1457. catch (Exception $e) {
  1458. restore_error_handler();
  1459. throw $e;
  1460. }
  1461. restore_error_handler();
  1462. if (empty($document)) {
  1463. throw new QueryPathParseException('Unknown parser exception.');
  1464. }
  1465. return $document;
  1466. }
  1467. public function setMatches($matches, $unique = TRUE) {
  1468. $this->last = $this->matches;
  1469. if ($matches instanceof SplObjectStorage) {
  1470. $this->matches = $matches;
  1471. }
  1472. elseif (is_array($matches)) {
  1473. trigger_error('Legacy array detected.');
  1474. $tmp = new SplObjectStorage();
  1475. foreach ($matches as $m) $tmp->attach($m);
  1476. $this->matches = $tmp;
  1477. }
  1478. else {
  1479. $found = new SplObjectStorage();
  1480. if (isset($matches)) $found->attach($matches);
  1481. $this->matches = $found;
  1482. }
  1483. $this->length = $this->matches->count();
  1484. }
  1485. private function noMatches() {
  1486. $this->setMatches(NULL);
  1487. }
  1488. private function getNthMatch($index) {
  1489. if ($index > $this->matches->count() || $index < 0) return;
  1490. $i = 0;
  1491. foreach ($this->matches as $m) {
  1492. if ($i++ == $index) return $m;
  1493. }
  1494. }
  1495. private function getFirstMatch() {
  1496. $this->matches->rewind();
  1497. return $this->matches->current();
  1498. }
  1499. private function parseXMLFile($filename, $flags = NULL, $context = NULL) {
  1500. if (!empty($context)) {
  1501. try {
  1502. set_error_handler(array('QueryPathParseException', 'initializeFromError'), $this->errTypes);
  1503. $contents = file_get_contents($filename, FALSE, $context);
  1504. }
  1505. catch(Exception $e) {
  1506. restore_error_handler();
  1507. throw $e;
  1508. }
  1509. restore_error_handler();
  1510. if ($contents == FALSE) {
  1511. throw new QueryPathParseException(sprintf('Contents of the file %s could not be retrieved.', $filename));
  1512. }
  1513. return $this->parseXMLString($contents, $flags);
  1514. }
  1515. $document = new DOMDocument();
  1516. $lastDot = strrpos($filename, '.');
  1517. $htmlExtensions = array(
  1518. '.html' => 1,
  1519. '.htm' => 1,
  1520. );
  1521. if (empty($this->options['use_parser'])) {
  1522. $useParser = '';
  1523. }
  1524. else {
  1525. $useParser = strtolower($this->options['use_parser']);
  1526. }
  1527. $ext = $lastDot !== FALSE ? strtolower(substr($filename, $lastDot)) : '';
  1528. try {
  1529. set_error_handler(array('QueryPathParseException', 'initializeFromError'), $this->errTypes);
  1530. if ($useParser == 'xml') {
  1531. $r = $document->load($filename, $flags);
  1532. }
  1533. elseif (isset($htmlExtensions[$ext]) || $useParser == 'html') {
  1534. $r = $document->loadHTMLFile($filename);
  1535. }
  1536. else {
  1537. $r = $document->load($filename, $flags);
  1538. }
  1539. }
  1540. catch (Exception $e) {
  1541. restore_error_handler();
  1542. throw $e;
  1543. }
  1544. restore_error_handler();
  1545. return $document;
  1546. }
  1547. public function __call($name, $arguments) {
  1548. if (!QueryPathExtensionRegistry::$useRegistry) {
  1549. throw new QueryPathException("No method named $name found (Extensions disabled).");
  1550. }
  1551. if (empty($this->ext)) {
  1552. $this->ext = QueryPathExtensionRegistry::getExtensions($this);
  1553. }
  1554. if (!empty($this->ext) && QueryPathExtensionRegistry::hasMethod($name)) {
  1555. $owner = QueryPathExtensionRegistry::getMethodClass($name);
  1556. $method = new ReflectionMethod($owner, $name);
  1557. return $method->invokeArgs($this->ext[$owner], $arguments);
  1558. }
  1559. throw new QueryPathException("No method named $name found. Possibly missing an extension.");
  1560. }
  1561. public function getIterator() {
  1562. $i = new QueryPathIterator($this->matches);
  1563. $i->options = $this->options;
  1564. return $i;
  1565. }
  1566. }
  1567. class QueryPathEntities {
  1568. protected static $regex = '/&([\w]+);|&#([\d]+);|&#(x[0-9a-fA-F]+);|(&)/m';
  1569. public static function replaceAllEntities($string) {
  1570. return preg_replace_callback(self::$regex, 'QueryPathEntities::doReplacement', $string);
  1571. }
  1572. protected static function doReplacement($matches) {
  1573. $count = count($matches);
  1574. switch ($count) {
  1575. case 2:
  1576. return '&#' . self::replaceEntity($matches[1]) . ';';
  1577. case 3:
  1578. case 4:
  1579. return '&#' . $matches[$count-1] . ';';
  1580. case 5:

Large files files are truncated, but you can click here to view the full file