§ Код класса

1<?php
2
3libxml_use_internal_errors(true);
4
5class xpath
6{
7    protected DOMDocument $dom;
8    protected DOMXPath    $xpath;
9
10    /**
11     * @param  string  $content
12     * @throws Exception
13     */
14    public function __construct(string $content)
15    {
16        $this->dom = new DOMDocument();
17
18        if ($content == '') {
19            throw new Exception("Content for empty string");
20        }
21
22        $this->dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
23        $this->dom->normalizeDocument();
24        $this->xpath = new DOMXPath($this->dom);
25
26        libxml_clear_errors();
27        return $this;
28    }
29
30    /**
31     * @desc What -- что именно искать, Where - порядковый номер или false, если надо все
32     * # -- HTML-код
33     * $ -- TEXT
34     * @ -- Нода
35     * @param $query
36     * @param  null  $what
37     * @param  int|null  $where
38     * @return array|mixed|string|void|null
39     * @throws Exception
40     */
41    public function query($query, $what = null, mixed $where = -1)
42    {
43        $rows = [];
44        $result = $this->xpath->query($query);
45
46        // Была ошибка при запросе
47        if ($error = libxml_get_last_error()) {
48            throw new Exception("XQuery [" . trim($error->message) . "] for: $query");
49        }
50
51        // Запрос на все поля N-го результата
52        if (is_integer($what)) {
53            [$where, $what] = [$what, null];
54        }
55
56        // Нет результатов
57        if ($result->length === 0) {
58            libxml_clear_errors();
59            return [];
60        }
61
62        foreach ($result as $n => $item)
63        {
64            $row = [
65                '@' => $item,
66                '$' => $item->nodeValue,
67                '#' => '',
68            ];
69
70            $dom = new DOMDocument();
71
72            // Сканировать атрибуты
73            if (is_object($item->attributes)) {
74                foreach ((object)$item->attributes as $ds) {
75                    $row[$ds->nodeName] = $ds->nodeValue;
76                }
77            }
78
79            // HTML-код
80            try {
81
82                $dom->appendChild($dom->importNode($item, true));
83                $row['#'] = preg_replace('~(^<[^>]*>|</[^>]*>$)~', '', trim(html_entity_decode($dom->saveHTML())));
84
85            } catch (Exception) {
86                $row['#'] = '';
87            }
88
89            unset($dom);
90
91            // Выборка определенного сообщения
92            if ($what !== null) {
93                $row = $row[$what] ?? null;
94            }
95
96            // Был достигнут определенный номер
97            if ($where === $n) {
98                $rows = $row;
99                break;
100            } else {
101                $rows[] = $row;
102            }
103        }
104
105        libxml_clear_errors();
106        return $rows;
107    }
108}