Completed
Push — master ( e093d7...19094c )
by Lars
02:02
created

getIsDOMDocumentCreatedWithoutHtml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace voku\helper;
4
5
use BadMethodCallException;
6
use DOMDocument;
7
use DOMXPath;
8
use InvalidArgumentException;
9
use RuntimeException;
10
11
/**
12
 * Class HtmlDomParser
13
 *
14
 * @package voku\helper
15
 *
16
 * @property-read string outerText Get dom node's outer html (alias for "outerHtml")
17
 * @property-read string outerHtml Get dom node's outer html
18
 * @property-read string innerText Get dom node's inner html (alias for "innerHtml")
19
 * @property-read string innerHtml Get dom node's inner html
20
 * @property-read string plaintext Get dom node's plain text
21
 *
22
 * @method string outerText() Get dom node's outer html (alias for "outerHtml()")
23
 * @method string outerHtml() Get dom node's outer html
24
 * @method string innerText() Get dom node's inner html (alias for "innerHtml()")
25
 * @method HtmlDomParser load() load($html) Load HTML from string
26
 * @method HtmlDomParser load_file() load_file($html) Load HTML from file
27
 *
28
 * @method static HtmlDomParser file_get_html() file_get_html($html, $libXMLExtraOptions = null) Load HTML from file
29
 * @method static HtmlDomParser str_get_html() str_get_html($html, $libXMLExtraOptions = null) Load HTML from string
30
 */
31
class HtmlDomParser
32
{
33
  /**
34
   * @var array
35
   */
36
  protected static $functionAliases = array(
37
      'outertext' => 'html',
38
      'outerhtml' => 'html',
39
      'innertext' => 'innerHtml',
40
      'innerhtml' => 'innerHtml',
41
      'load'      => 'loadHtml',
42
      'load_file' => 'loadHtmlFile',
43
  );
44
45
  /**
46
   * @var string[][]
47
   */
48
  protected static $domLinkReplaceHelper = array(
49
      'orig' => array('[', ']', '{', '}',),
50
      'tmp'  => array(
51
          '!!!!SIMPLE_HTML_DOM__VOKU__SQUARE_BRACKET_LEFT!!!!',
52
          '!!!!SIMPLE_HTML_DOM__VOKU__SQUARE_BRACKET_RIGHT!!!!',
53
          '!!!!SIMPLE_HTML_DOM__VOKU__BRACKET_LEFT!!!!',
54
          '!!!!SIMPLE_HTML_DOM__VOKU__BRACKET_RIGHT!!!!',
55
      ),
56
  );
57
58
  /**
59
   * @var array
60
   */
61
  protected static $domReplaceHelper = array(
62
      'orig' => array('&', '|', '+', '%'),
63
      'tmp'  => array(
64
          '!!!!SIMPLE_HTML_DOM__VOKU__AMP!!!!',
65
          '!!!!SIMPLE_HTML_DOM__VOKU__PIPE!!!!',
66
          '!!!!SIMPLE_HTML_DOM__VOKU__PLUS!!!!',
67
          '!!!!SIMPLE_HTML_DOM__VOKU__PERCENT!!!!',
68
      ),
69
  );
70
71
  /**
72
   * @var Callable
73
   */
74
  protected static $callback;
75
76
  /**
77
   * @var DOMDocument
78
   */
79
  protected $document;
80
81
  /**
82
   * @var string
83
   */
84
  protected $encoding = 'UTF-8';
85
86
  /**
87
   * @var bool
88
   */
89
  protected $isDOMDocumentCreatedWithoutHtml = false;
90
91
  /**
92
   * @var bool
93
   */
94
  protected $isDOMDocumentCreatedWithoutHtmlWrapper = false;
95
96
  /**
97
   * Constructor
98
   *
99
   * @param string|SimpleHtmlDom|\DOMNode $element HTML code or SimpleHtmlDom, \DOMNode
100
   */
101 120
  public function __construct($element = null)
102
  {
103 120
    $this->document = new \DOMDocument('1.0', $this->getEncoding());
104
105
    // DOMDocument settings
106 120
    $this->document->preserveWhiteSpace = true;
107 120
    $this->document->formatOutput = true;
108
109 120
    if ($element instanceof SimpleHtmlDom) {
110 51
      $element = $element->getNode();
111 51
    }
112
113 120
    if ($element instanceof \DOMNode) {
114 51
      $domNode = $this->document->importNode($element, true);
115
116 51
      if ($domNode instanceof \DOMNode) {
117 51
        $this->document->appendChild($domNode);
118 51
      }
119
120 51
      return;
121
    }
122
123 120
    if ($element !== null) {
124 70
      $this->loadHtml($element);
125 69
    }
126 119
  }
127
128
  /**
129
   * @param $name
130
   * @param $arguments
131
   *
132
   * @return bool|mixed
133
   */
134 34 View Code Duplication
  public function __call($name, $arguments)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
  {
136 34
    $name = strtolower($name);
137
138 34
    if (isset(self::$functionAliases[$name])) {
139 33
      return call_user_func_array(array($this, self::$functionAliases[$name]), $arguments);
140
    }
141
142 1
    throw new BadMethodCallException('Method does not exist: ' . $name);
143
  }
144
145
  /**
146
   * @param $name
147
   * @param $arguments
148
   *
149
   * @return HtmlDomParser
150
   */
151 15
  public static function __callStatic($name, $arguments)
152
  {
153 15
    $arguments0 = null;
154 15
    if (isset($arguments[0])) {
155 14
      $arguments0 = $arguments[0];
156 14
    }
157
158 15
    $arguments1 = null;
159 15
    if (isset($arguments[1])) {
160 1
      $arguments1 = $arguments[1];
161 1
    }
162
163 15
    if ($name === 'str_get_html') {
164 10
      $parser = new self();
165
166 10
      return $parser->loadHtml($arguments0, $arguments1);
167
    }
168
169 5
    if ($name === 'file_get_html') {
170 4
      $parser = new self();
171
172 4
      return $parser->loadHtmlFile($arguments0, $arguments1);
173
    }
174
175 1
    throw new BadMethodCallException('Method does not exist');
176
  }
177
178
  /** @noinspection MagicMethodsValidityInspection */
179
  /**
180
   * @param $name
181
   *
182
   * @return string
183
   */
184 15
  public function __get($name)
185
  {
186 15
    $name = strtolower($name);
187
188
    switch ($name) {
189 15
      case 'outerhtml':
190 15
      case 'outertext':
191 8
        return $this->html();
192 7
      case 'innerhtml':
193 7
      case 'innertext':
194 5
        return $this->innerHtml();
195 2
      case 'text':
196 2
      case 'plaintext':
197 1
        return $this->text();
198
    }
199
200 1
    return null;
201
  }
202
203
  /**
204
   * @param string $selector
205
   * @param int    $idx
206
   *
207
   * @return SimpleHtmlDom|SimpleHtmlDomNode|null
208
   */
209 3
  public function __invoke($selector, $idx = null)
210
  {
211 3
    return $this->find($selector, $idx);
212
  }
213
214
  /**
215
   * @return string
216
   */
217 14
  public function __toString()
218
  {
219 14
    return $this->html();
220
  }
221
222
  /**
223
   * does nothing (only for api-compatibility-reasons)
224
   *
225
   * @return bool
226
   */
227 1
  public function clear()
228
  {
229 1
    return true;
230
  }
231
232
  /**
233
   * @param string $html
234
   *
235
   * @return string
236
   */
237 72
  public static function replaceToPreserveHtmlEntities($html)
238
  {
239
    // init
240 72
    $linksNew = array();
241 72
    $linksOld = array();
242
243 72
    if (strpos($html, 'http') !== false) {
244 49
      preg_match_all("/(\bhttps?:\/\/[^\s()<>]+(?:\([\w\d]+\)|[^[:punct:]\s]|\/|\}|\]))/i", $html, $linksOld);
245
246 49
      if (!empty($linksOld[1])) {
247 49
        $linksOld = $linksOld[1];
248 49
        foreach ((array)$linksOld as $linkKey => $linkOld) {
249 49
          $linksNew[$linkKey] = str_replace(
250 49
              self::$domLinkReplaceHelper['orig'],
251 49
              self::$domLinkReplaceHelper['tmp'],
252
              $linkOld
253 49
          );
254 49
        }
255 49
      }
256 49
    }
257
258 72
    $linksNewCount = count($linksNew);
259 72
    if ($linksNewCount > 0 && count($linksOld) === $linksNewCount) {
260 49
      $search = array_merge($linksOld, self::$domReplaceHelper['orig']);
261 49
      $replace = array_merge($linksNew, self::$domReplaceHelper['tmp']);
262 49
    } else {
263 24
      $search = self::$domReplaceHelper['orig'];
264 24
      $replace = self::$domReplaceHelper['tmp'];
265
    }
266
267 72
    return str_replace($search, $replace, $html);
268
  }
269
270
  /**
271
   * @param string $html
272
   *
273
   * @return string
274
   */
275 57
  public static function putReplacedBackToPreserveHtmlEntities($html)
276
  {
277 57
    static $DOM_REPLACE__HELPER_CACHE = null;
278 57
    if ($DOM_REPLACE__HELPER_CACHE === null) {
279 1
      $DOM_REPLACE__HELPER_CACHE['tmp'] = array_merge(
280 1
          self::$domLinkReplaceHelper['tmp'],
281 1
          self::$domReplaceHelper['tmp']
282 1
      );
283 1
      $DOM_REPLACE__HELPER_CACHE['orig'] = array_merge(
284 1
          self::$domLinkReplaceHelper['orig'],
285 1
          self::$domReplaceHelper['orig']
286 1
      );
287 1
    }
288
289 57
    return str_replace($DOM_REPLACE__HELPER_CACHE['tmp'], $DOM_REPLACE__HELPER_CACHE['orig'], $html);
290
  }
291
292
  /**
293
   * create DOMDocument from HTML
294
   *
295
   * @param string   $html
296
   * @param int|null $libXMLExtraOptions
297
   *
298
   * @return \DOMDocument
299
   */
300 108
  private function createDOMDocument($html, $libXMLExtraOptions = null)
301
  {
302 108
    if (strpos($html, '<') === false) {
303 6
      $this->isDOMDocumentCreatedWithoutHtml = true;
304 6
    }
305
306 108
    if (strpos($html, '<html') === false) {
307 60
      $this->isDOMDocumentCreatedWithoutHtmlWrapper = true;
308 60
    }
309
310
    // set error level
311 108
    $internalErrors = libxml_use_internal_errors(true);
312 108
    $disableEntityLoader = libxml_disable_entity_loader(true);
313 108
    libxml_clear_errors();
314
315 108
    $optionsSimpleXml = LIBXML_DTDLOAD | LIBXML_DTDATTR | LIBXML_NONET;
316 108
    $optionsXml = 0;
317
318 108
    if (defined('LIBXML_BIGLINES')) {
319
      $optionsSimpleXml |= LIBXML_BIGLINES;
320
    }
321
322 108
    if (defined('LIBXML_COMPACT')) {
323 108
      $optionsSimpleXml |= LIBXML_COMPACT;
324 108
    }
325
326 108
    if (defined('LIBXML_HTML_NOIMPLIED')) {
327 108
      $optionsSimpleXml |= LIBXML_HTML_NOIMPLIED;
328 108
    }
329
330 108
    if (defined('LIBXML_HTML_NODEFDTD')) {
331 108
      $optionsSimpleXml |= LIBXML_HTML_NODEFDTD;
332 108
    }
333
334 108
    if ($libXMLExtraOptions !== null) {
335 1
      $optionsSimpleXml |= $libXMLExtraOptions;
336 1
      $optionsXml |= $libXMLExtraOptions;
337 1
    }
338
339 108
    $sxe = simplexml_load_string($html, 'SimpleXMLElement', $optionsSimpleXml);
340 108
    if ($sxe !== false && count(libxml_get_errors()) === 0) {
341 38
      $this->document = dom_import_simplexml($sxe)->ownerDocument;
342 38
    } else {
343
344
      // UTF-8 hack: http://php.net/manual/en/domdocument.loadhtml.php#95251
345 72
      $html = trim($html);
346 72
      $xmlHackUsed = false;
347 72
      if (stripos('<?xml', $html) !== 0) {
348 72
        $xmlHackUsed = true;
349 72
        $html = '<?xml encoding="' . $this->getEncoding() . '" ?>' . $html;
350 72
      }
351
352 72
      $html = self::replaceToPreserveHtmlEntities($html);
353
354 72
      if ($optionsXml && Bootup::is_php('5.4')) {
355 1
        $this->document->loadHTML($html, $optionsXml);
356 1
      } else {
357 72
        $this->document->loadHTML($html);
358
      }
359
360
      // remove the "xml-encoding" hack
361 72
      if ($xmlHackUsed === true) {
362 72
        foreach ($this->document->childNodes as $child) {
363 72
          if ($child->nodeType === XML_PI_NODE) {
364 72
            $this->document->removeChild($child);
365 72
          }
366 72
        }
367 72
      }
368
369 72
      libxml_clear_errors();
370
    }
371
372
    // set encoding
373 108
    $this->document->encoding = $this->getEncoding();
374
375
    // restore lib-xml settings
376 108
    libxml_use_internal_errors($internalErrors);
377 108
    libxml_disable_entity_loader($disableEntityLoader);
378
379 108
    return $this->document;
380
  }
381
382
  /**
383
   * Return SimpleHtmlDom by id.
384
   *
385
   * @param string $id
386
   *
387
   * @return SimpleHtmlDom|SimpleHtmlDomNodeBlank
388
   */
389 2
  public function getElementById($id)
390
  {
391 2
    return $this->find("#$id", 0);
392
  }
393
394
  /**
395
   * Return SimpleHtmlDom by tag name.
396
   *
397
   * @param string $name
398
   *
399
   * @return SimpleHtmlDom|SimpleHtmlDomNodeBlank
400
   */
401 1
  public function getElementByTagName($name)
402
  {
403 1
    $node = $this->document->getElementsByTagName($name)->item(0);
404
405 1
    if ($node !== null) {
406 1
      return new SimpleHtmlDom($node);
407
    } else {
408
      return new SimpleHtmlDomNodeBlank();
409
    }
410
  }
411
412
  /**
413
   * Returns Elements by id
414
   *
415
   * @param string   $id
416
   * @param null|int $idx
417
   *
418
   * @return SimpleHtmlDomNode|SimpleHtmlDomNode[]|SimpleHtmlDomNodeBlank
419
   */
420
  public function getElementsById($id, $idx = null)
421
  {
422
    return $this->find("#$id", $idx);
423
  }
424
425
  /**
426
   * Returns Elements by tag name
427
   *
428
   * @param string   $name
429
   * @param null|int $idx
430
   *
431
   * @return SimpleHtmlDomNode|SimpleHtmlDomNode[]|SimpleHtmlDomNodeBlank
432
   */
433 3 View Code Duplication
  public function getElementsByTagName($name, $idx = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
434
  {
435 3
    $nodesList = $this->document->getElementsByTagName($name);
436
437 3
    $elements = new SimpleHtmlDomNode();
438
439 3
    foreach ($nodesList as $node) {
440 3
      $elements[] = new SimpleHtmlDom($node);
441 3
    }
442
443 3
    if (null === $idx) {
444 2
      return $elements;
445
    } else {
446 1
      if ($idx < 0) {
447
        $idx = count($elements) + $idx;
448
      }
449
    }
450
451 1
    if (isset($elements[$idx])) {
452 1
      return $elements[$idx];
453
    } else {
454
      return new SimpleHtmlDomNodeBlank();
455
    }
456
  }
457
458
  /**
459
   * Find list of nodes with a CSS selector.
460
   *
461
   * @param string $selector
462
   * @param int    $idx
463
   *
464
   * @return SimpleHtmlDom|SimpleHtmlDom[]|SimpleHtmlDomNodeBlank
465
   */
466 78
  public function find($selector, $idx = null)
467
  {
468 78
    $xPathQuery = SelectorConverter::toXPath($selector);
469
470 78
    $xPath = new DOMXPath($this->document);
471 78
    $nodesList = $xPath->query($xPathQuery);
472 78
    $elements = new SimpleHtmlDomNode();
473
474 78
    foreach ($nodesList as $node) {
475 74
      $elements[] = new SimpleHtmlDom($node);
476 78
    }
477
478 78
    if (null === $idx) {
479 51
      return $elements;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $elements; (voku\helper\SimpleHtmlDomNode) is incompatible with the return type documented by voku\helper\HtmlDomParser::find of type voku\helper\SimpleHtmlDo...\SimpleHtmlDomNodeBlank.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
480
    } else {
481 39
      if ($idx < 0) {
482 11
        $idx = count($elements) + $idx;
483 11
      }
484
    }
485
486 39
    if (isset($elements[$idx])) {
487 36
      return $elements[$idx];
488
    } else {
489 5
      return new SimpleHtmlDomNodeBlank();
490
    }
491
  }
492
493
  /**
494
   * @param string $content
495
   *
496
   * @return string
497
   */
498 48
  protected function fixHtmlOutput($content)
499
  {
500
    // INFO: DOMDocument will encapsulate plaintext into a paragraph tag (<p>),
501
    //          so we try to remove it here again ...
502
503 48
    if ($this->isDOMDocumentCreatedWithoutHtmlWrapper === true) {
504 21
      $content = str_replace(
505
          array(
506 21
              "\n",
507 21
              "\r\n",
508 21
              "\r",
509 21
              '<simpleHtmlDomP>',
510 21
              '</simpleHtmlDomP>',
511 21
              '<body>',
512 21
              '</body>',
513 21
              '<html>',
514 21
              '</html>',
515 21
          ),
516 21
          '',
517
          $content
518 21
      );
519 21
    }
520
521 48
    if ($this->isDOMDocumentCreatedWithoutHtml === true) {
522 5
      $content = str_replace(
523
          array(
524 5
              '<p>',
525 5
              '</p>',
526 5
              '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">',
527 5
          ),
528 5
          '',
529
          $content
530 5
      );
531 5
    }
532
533 48
    $content = UTF8::html_entity_decode($content);
534 48
    $content = trim($content);
535 48
    $content = UTF8::rawurldecode($content);
536
537 48
    $content = self::putReplacedBackToPreserveHtmlEntities($content);
538
539 48
    return $content;
540
  }
541
542
  /**
543
   * @return DOMDocument
544
   */
545 35
  public function getDocument()
546
  {
547 35
    return $this->document;
548
  }
549
550
  /**
551
   * Get the encoding to use
552
   *
553
   * @return string
554
   */
555 120
  private function getEncoding()
556
  {
557 120
    return $this->encoding;
558
  }
559
560
  /**
561
   * @return bool
562
   */
563 6
  public function getIsDOMDocumentCreatedWithoutHtml()
564
  {
565 6
    return $this->isDOMDocumentCreatedWithoutHtml;
566
  }
567
568
  /**
569
   * @return bool
570
   */
571 34
  public function getIsDOMDocumentCreatedWithoutHtmlWrapper()
572
  {
573 34
    return $this->isDOMDocumentCreatedWithoutHtmlWrapper;
574
  }
575
576
  /**
577
   * Get dom node's outer html
578
   *
579
   * @return string
580
   */
581 34
  public function html()
582
  {
583 34
    if ($this::$callback !== null) {
584
      call_user_func($this::$callback, array($this));
585
    }
586
587 34
    if ($this->getIsDOMDocumentCreatedWithoutHtmlWrapper()) {
588 15
      $content = $this->document->saveHTML($this->document->documentElement);
589 15
    } else {
590 22
      $content = $this->document->saveHTML();
591
    }
592
593 34
    return $this->fixHtmlOutput($content);
594
  }
595
596
  /**
597
   * Get the HTML as XML.
598
   *
599
   * @return string
600
   */
601 1
  public function xml()
602
  {
603 1
    $xml = $this->document->saveXML(null, LIBXML_NOEMPTYTAG);
604
605
    // remove the XML-header
606 1
    $xml = ltrim(preg_replace('/<\?xml.*\?>/', '', $xml));
607
608 1
    return $this->fixHtmlOutput($xml);
609
  }
610
611
  /**
612
   * Get dom node's inner html
613
   *
614
   * @return string
615
   */
616 15
  public function innerHtml()
617
  {
618 15
    $text = '';
619
620 15
    foreach ($this->document->documentElement->childNodes as $node) {
621 15
      $text .= $this->fixHtmlOutput($this->document->saveHTML($node));
622 15
    }
623
624 15
    return $text;
625
  }
626
627
  /**
628
   * Load HTML from string
629
   *
630
   * @param string   $html
631
   * @param int|null $libXMLExtraOptions
632
   *
633
   * @return HtmlDomParser
634
   *
635
   * @throws InvalidArgumentException if argument is not string
636
   */
637 111
  public function loadHtml($html, $libXMLExtraOptions = null)
638
  {
639 111
    if (!is_string($html)) {
640 3
      throw new InvalidArgumentException(__METHOD__ . ' expects parameter 1 to be string.');
641
    }
642
643 108
    $this->document = $this->createDOMDocument($html, $libXMLExtraOptions);
644
645 108
    return $this;
646
  }
647
648
  /**
649
   * Load HTML from file
650
   *
651
   * @param string   $filePath
652
   * @param int|null $libXMLExtraOptions
653
   *
654
   * @return HtmlDomParser
655
   */
656 12
  public function loadHtmlFile($filePath, $libXMLExtraOptions = null)
657
  {
658 12
    if (!is_string($filePath)) {
659 2
      throw new InvalidArgumentException(__METHOD__ . ' expects parameter 1 to be string.');
660
    }
661
662 10
    if (!preg_match("/^https?:\/\//i", $filePath) && !file_exists($filePath)) {
663 1
      throw new RuntimeException("File $filePath not found");
664
    }
665
666
    try {
667 9
      $html = UTF8::file_get_contents($filePath);
668
669 9
    } catch (\Exception $e) {
670 1
      throw new RuntimeException("Could not load file $filePath");
671
    }
672
673 8
    if ($html === false) {
674
      throw new RuntimeException("Could not load file $filePath");
675
    }
676
677 8
    $this->loadHtml($html, $libXMLExtraOptions);
678
679 8
    return $this;
680
  }
681
682
  /**
683
   * Save dom as string
684
   *
685
   * @param string $filepath
686
   *
687
   * @return string
688
   */
689 1
  public function save($filepath = '')
690
  {
691 1
    $string = $this->innerHtml();
692 1
    if ($filepath !== '') {
693
      file_put_contents($filepath, $string, LOCK_EX);
694
    }
695
696 1
    return $string;
697
  }
698
699
  /**
700
   * @param $functionName
701
   */
702
  public function set_callback($functionName)
703
  {
704
    $this::$callback = $functionName;
705
  }
706
707
  /**
708
   * Get dom node's plain text
709
   *
710
   * @return string
711
   */
712 2
  public function text()
713
  {
714 2
    return $this->fixHtmlOutput($this->document->textContent);
715
  }
716
}
717