Completed
Push — master ( 1474ca...abb884 )
by Jan-Petter
02:47
created

OPMLParser::validate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 2
eloc 6
nc 2
nop 0
1
<?php
2
namespace vipnytt;
3
4
use DOMDocument;
5
use SimpleXMLElement;
6
use vipnytt\OPMLParser\Exceptions;
7
8
class OPMLParser
9
{
10
    /**
11
     * Encoding
12
     */
13
    const ENCODING = 'UTF-8';
14
15
    /**
16
     * Optional <head> elements
17
     */
18
    const OPTIONAL_HEAD_ELEMENTS = [
19
        'title',
20
        'dateCreated',
21
        'dateModified',
22
        'ownerName',
23
        'ownerEmail',
24
        'ownerId',
25
        'docs',
26
        'expansionState',
27
        'vertScrollState',
28
        'windowTop',
29
        'windowLeft',
30
        'windowBottom',
31
        'windowRight'
32
    ];
33
34
    /**
35
     * XML content
36
     * @var string
37
     */
38
    protected $xml;
39
40
    /**
41
     * Array containing the parsed XML
42
     * @var array
43
     */
44
    protected $result = [];
45
46
    /**
47
     * Constructor
48
     *
49
     * @param string $xml is the string we want to parse
50
     * @throws Exceptions\ParseException
51
     */
52
    public function __construct($xml)
53
    {
54
        $this->xml = $xml;
55
        $dom = new DOMDocument();
56
        $dom->recover = true;
57
        $dom->strictErrorChecking = false;
58
        $dom->loadXML($this->xml, LIBXML_NOCDATA);
59
        $dom->encoding = self::ENCODING;
60
61
        $opml = simplexml_import_dom($dom);
62
63
        if ($opml === false) {
64
            throw new Exceptions\ParseException('Provided XML document is not valid');
65
        }
66
67
        $this->result = [
68
            'version' => (string)$opml['version'],
69
            'head' => [],
70
            'body' => []
71
        ];
72
73
        if (!isset($opml->head)) {
74
            throw new Exceptions\ParseException('Provided XML is not an valid OPML document');
75
        }
76
        // First, we get all "head" elements. Head is required but its sub-elements are optional.
77
        foreach ($opml->head->children() as $key => $value) {
78
            if (in_array($key, self::OPTIONAL_HEAD_ELEMENTS, true)) {
79
                $this->result['head'][$key] = (string)$value;
80
            }
81
        }
82
        if (!isset($opml->body)) {
83
            return;
84
        }
85
        // Then, we get body outlines. Body must contain at least one outline element.
86
        foreach ($opml->body->children() as $key => $value) {
87
            if ($key === 'outline') {
88
                $this->result['body'][] = $this->parse_outline($value);
89
            }
90
        }
91
    }
92
93
    /**
94
     * Parse an XML object as an outline object and return corresponding array
95
     *
96
     * @param SimpleXMLElement $outline_xml the XML object we want to parse
97
     * @return array corresponding to an outline and following format described above
98
     */
99
    protected function parse_outline(SimpleXMLElement $outline_xml)
100
    {
101
        $outline = [];
102
        foreach ($outline_xml->attributes() as $key => $value) {
103
            $outline[$key] = (string)$value;
104
        }
105
        // Bug fix for OPMLs witch contains `title` but not the required `text`
106
        if (empty($outline['text']) && isset($outline['title'])) {
107
            $outline['text'] = $outline['title'];
108
        }
109
        foreach ($outline_xml->children() as $key => $value) {
110
            // An outline may contain any number of outline children
111
            if ($key === 'outline') {
112
                $outline['@outlines'][] = $this->parse_outline($value);
113
            }
114
        }
115
        return $outline;
116
    }
117
118
    /**
119
     * Return the parsed XML as an Array
120
     *
121
     * @return array
122
     */
123
    public function getResult()
124
    {
125
        return $this->result;
126
    }
127
128
    /**
129
     * Validate the parsed XML array
130
     * Note: The parser support parsing of OPMLs with missing content
131
     *
132
     * @return string|false Validated string on success, false on failure
133
     */
134
    public function validate()
135
    {
136
        try {
137
            $render = new OPMLParser\Render($this->result);
138
        } catch (Exceptions\RenderException $e) {
139
            return false;
140
        }
141
        return $render->asString();
142
    }
143
}
144