root/trunk/patXMLPretty/Engine/DOM.php

Revision 46, 7.1 kB (checked in by argh, 3 years ago)

Updated not to throw notices on PHP4, added additional PHP version check.

Line 
1 <?php
2 /**
3  * File containing the patXMLPretty_Engine_DOM class.
4  *
5  * @package        patXMLPretty
6  * @subpackage     Engine
7  * @author        Sebastian 'The Argh' Mordziol <argh@php-tools.net>
8  * @see         patXMLPretty_Engine_DOM
9  */
10
11 /**
12  * Notice: unknown/unsupported node type
13  */
14  define( 'PATXMLPRETTY_ENGINE_DOM_NOTICE_UNKNOWN_NODE_TYPE', 'patXMLPretty_Engine_DOM:01' );
15
16 /**
17  * Error: invalid source XML document, could not be parsed
18  *
19  */
20  define( 'PATXMLPRETTY_ENGINE_DOM_ERROR_INVALID_SOURCE_DOCUMENT', 'patXMLPretty_Engine_DOM:02' );
21
22 /**
23  * Warning: wrong PHP version
24  */
25  define( 'PATXMLPRETTY_ENGINE_DOM_WARNING_WRONG_PHPVERSION', 'patXMLPretty_Engine_DOM:01' );
26
27 /**
28  * DOM parsing engine that used the PHP5 DOM parser to create the
29  * elements structure used by patXMLPretty renderers, as well as
30  * some meta information about the document.
31  *
32  * @package        patXMLPretty
33  * @subpackage     Engine
34  * @author        Sebastian 'The Argh' Mordziol <argh@php-tools.net>
35  * @version        0.1.0
36  * @license        LGPL
37  * @link        http://www.php-tools.net
38  * @see         patXMLPretty_Engine
39  */
40 class patXMLPretty_Engine_DOM extends patXMLPretty_Engine
41 {
42    /**
43     * Stores the name of the engine
44     *
45     * @access    private
46     * @var        string
47     */
48     var $name = 'DOM';
49
50    /**
51     * Stores the engine's options:
52     *
53     * <ul>
54     *     <li>
55     *         <b>parseDoctype</b> [yes|no] default: no<br/>
56     *         Sets whether to parse a doctype declaration if present. If the renderer supports it, the doctype will be included in the generated highlighted source.
57     *    </li>
58     *     <li>
59     *         <b>inputType</b> [xml|html] default: xml<br/>
60     *         Sets the type of input to handle.
61     *     </li>
62     * </ul>
63     *
64     * @access    private
65     * @var         array
66     */
67     var $options = array(
68         'parseDoctype' => 'no',
69         'inputType' => 'xml'
70     );
71
72    /**
73     * Parses the XML source and creates the array-based
74     * elements tree used internally by the renderers to
75     * create the highlighted XML source.
76     *
77     * @access    public
78     * @param    string
79     * @return    array|patError
80     */
81     function parse( $xmlSource )
82     {
83         // check for php5
84         if( substr( phpversion(), 0, 1 ) < 5 ) {
85             $error = patErrorManager::raiseWarning(
86                 PATXMLPRETTY_ENGINE_DOM_WARNING_WRONG_PHPVERSION,
87                 $this->userMessages['warning'],
88                 'You cannot use the DOM parsing engine with PHP versions prior to PHP5.'
89             );
90
91             return $error;
92         }
93
94         // create the DOM object
95         $dom = new DOMDocument();
96         $dom->preserveWhiteSpace = true;
97         $dom->substituteEntities = false;
98
99         // manage the selected input type to parse
100         // either HTML or XML.
101         $method = 'loadXML';
102         if( $this->getOption( 'inputType' ) == 'html' ) {
103             $method = 'loadHTML';
104         }
105
106         // use output buffering to capture parsing messages
107         ob_start();
108         $success = $dom->$method( $xmlSource );
109         $messages = ob_get_contents();
110         ob_end_clean();
111
112         // parsing successful?
113         if( !$success ) {
114             $error = patErrorManager::raiseError(
115                 PATXMLPRETTY_ENGINE_DOM_ERROR_INVALID_SOURCE_DOCUMENT,
116                 $this->userMessages['error'],
117                 'The XML source could not be parsed properly, the DOM parser '.
118                 'reported errors. Original parser messages:<br/><br/>'.strip_tags( $messages )
119             );
120
121             return $error;
122         }
123
124         $doctype = null;
125         if( $this->getOption( 'parseDoctype' ) == 'yes' ) {
126             $doctype = $this->_detectDoctype( $xmlSource );
127         }
128
129         // create the output array with document
130         // information and the elements tree.
131         $output = array(
132             'document' => $this->_create_document( $dom ),
133             'elements' => $this->_create_tree( $dom->documentElement ),
134             'doctype' => $doctype
135         );
136
137         return $output;
138     }
139
140    /**
141     * Creates the array containing information about the
142     * XML document, like its encoding or version.
143     *
144     * @access    private
145     * @param    DOMDocument
146     * @return    array
147     */
148     function _create_document( $dom )
149     {
150         $encoding = 'UTF-8';
151         if( !empty( $dom->encoding ) ) {
152             $encoding = $dom->encoding;
153         }
154
155         $standalone = 'no';
156         if( $dom->standalone ) {
157             $standalone = 'yes';
158         }
159
160         $document = array(
161             'encoding' => $encoding,
162             'version' => $dom->version,
163             'standalone' => $standalone,
164         );
165
166         return $document;
167     }
168
169    /**
170     * Creates the elements tree recursively.
171     *
172     * @access    private
173     * @param    DOMNode
174     * @return    array|bool
175     */
176     function _create_tree( $node )
177     {
178         $nodeType = $this->_translate_node_type( $node->nodeType );
179         if( !$nodeType ) {
180             return false;
181         }
182
183         // the node data collection
184         $nodeData = array();
185         $nodeData['name'] = $node->nodeName;
186         $nodeData['type'] = $nodeType;
187         $nodeData['text'] = null;
188         $nodeData['attributes'] = null;
189         $nodeData['children'] = null;
190
191         $method = '_handleNode_'.$nodeType;
192         if( method_exists( $this, $method ) ) {
193             $success = $this->$method( $node, $nodeData );
194             if( !$success ) {
195                 $result = false;
196                 return $result;
197             }
198         }
199
200         // check for attributes
201         if( $node->hasAttributes() ) {
202             $nodeData['attributes'] = array();
203             foreach( $node->attributes as $attribute ) {
204                 $nodeData['attributes'][$attribute->name] = $attribute->value;
205             }
206         }
207
208         // recurse into subnodes
209         if( $node->hasChildNodes() ) {
210             $children = array();
211             foreach( $node->childNodes as $childNode ) {
212                 // check for weird errors with attribute declaration nodes
213                 // which have child nodes with a NULL value...
214                 if( !is_object( $childNode ) ) {
215                     continue;
216                 }
217
218                 if( $childNode->nodeType == XML_TEXT_NODE ) {
219                     $nodeData['text'] .= $childNode->textContent;
220                     continue;
221                 }
222
223                 $child = $this->_create_tree( $childNode );
224                 if( $child ) {
225                     $children[] = $child;
226                 }
227             }
228
229             if( !empty( $children ) ) {
230                 $nodeData['children'] = $children;
231             }
232         }
233
234         return $nodeData;
235     }
236
237    /**
238     * Special handler for comment nodes
239     *
240     * @access    private
241     * @param    DOMNode
242     * @param     array
243     * @return    bool
244     */
245     function _handleNode_comment( $node, &$nodeData )
246     {
247         $nodeData['text'] = $node->textContent;
248
249         $result = true;
250         return $result;
251     }
252
253    /**
254     * Special handler for cdata sections
255     *
256     * @access    private
257     * @param    DOMNode
258     * @param     array
259     * @return    bool
260     */
261     function _handleNode_cdata( $node, &$nodeData )
262     {
263         $nodeData['text'] = $node->textContent;
264
265         $result = true;
266         return $result;
267     }
268
269    /**
270     * Special handler for entity references (like &amp;)
271     *
272     * @access    private
273     * @param    DOMNode
274     * @param     array
275     * @return    bool
276     */
277     function _handleNode_entityReference( $node, &$nodeData )
278     {
279         $nodeData['text'] = $node->textContent;
280
281         $result = true;
282         return $result;
283     }
284
285    /**
286     * Translates DOM-specific node codes to the internal
287     * node designations.
288     *
289     * @access    private
290     * @param    int
291     * @return    string|bool false
292     */
293     function _translate_node_type( $nodeType )
294     {
295         $nodeTypes = array(
296             XML_COMMENT_NODE => 'comment',
297             XML_ELEMENT_NODE => 'default',
298             XML_CDATA_SECTION_NODE => 'cdata',
299             XML_ENTITY_REF_NODE => 'entityReference',
300             XML_ENTITY_DECL_NODE => 'entityDeclaration',
301         );
302
303         if( !isset( $nodeTypes[$nodeType] ) ) {
304             patErrorManager::raiseNotice(
305                 PATXMLPRETTY_ENGINE_DOM_NOTICE_UNKNOWN_NODE_TYPE,
306                 $this->userMessages['warning'],
307                 'Unknown DOM node type ['.$nodeType.'], the patXMLPretty DOM engine does '.
308                 'not handle that node type yet, it will simply be ignored (including all '.
309                 'child nodes).'
310             );
311
312             $result = false;
313             return $result;
314         }
315
316         return $nodeTypes[$nodeType];
317     }
318 }
319
320 ?>
Note: See TracBrowser for help on using the browser.