1: <?php
2: // $Id: saxparser.php,v 1.1 2007/05/15 02:35:35 minahito Exp $
3: /*******************************************************************************
4: Location: <b>xml/SaxParser.class</b><br>
5: <br>
6: Provides basic functionality to read and parse XML documents. Subclasses
7: must implement all the their custom handlers by using add* function methods.
8: They may also use the handle*() methods to parse a specific XML begin and end
9: tags, but this is not recommended as it is more difficult.<br>
10: <br>
11: Copyright © 2001 eXtremePHP. All rights reserved.<br>
12: <br>
13: @author Ken Egervari<br>
14: *******************************************************************************/
15:
16: class SaxParser
17: {
18: var $level;
19: var $parser;
20:
21: var $isCaseFolding;
22: var $targetEncoding;
23:
24: /* Custom Handler Variables */
25: var $tagHandlers = array();
26:
27: /* Tag stack */
28: var $tags = array();
29:
30: /* Xml Source Input */
31: var $xmlInput;
32:
33: var $errors = array();
34:
35: /****************************************************************************
36: Creates a SaxParser object using a FileInput to represent the stream
37: of XML data to parse. Use the static methods createFileInput or
38: createStringInput to construct xml input source objects to supply
39: to the constructor, or the implementor can construct them individually.
40: ****************************************************************************/
41: function SaxParser(&$input)
42: {
43: $this->level = 0;
44: $this->parser = xml_parser_create('UTF-8');
45: xml_set_object($this->parser, $this);
46: $this->input =& $input;
47: $this->setCaseFolding(false);
48: $this->useUtfEncoding();
49: xml_set_element_handler($this->parser, 'handleBeginElement','handleEndElement');
50: xml_set_character_data_handler($this->parser, 'handleCharacterData');
51: xml_set_processing_instruction_handler($this->parser, 'handleProcessingInstruction');
52: xml_set_default_handler($this->parser, 'handleDefault');
53: xml_set_unparsed_entity_decl_handler($this->parser, 'handleUnparsedEntityDecl');
54: xml_set_notation_decl_handler($this->parser, 'handleNotationDecl');
55: xml_set_external_entity_ref_handler($this->parser, 'handleExternalEntityRef');
56: }
57:
58: /*---------------------------------------------------------------------------
59: Property Methods
60: ---------------------------------------------------------------------------*/
61:
62: function getCurrentLevel()
63: {
64: return $this->level;
65: }
66:
67: /****************************************************************************
68: * @param $isCaseFolding
69: * @returns void
70: ****************************************************************************/
71: function setCaseFolding($isCaseFolding)
72: {
73: assert(is_bool($isCaseFolding));
74:
75: $this->isCaseFolding = $isCaseFolding;
76: xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, $this->isCaseFolding);
77: }
78:
79: /****************************************************************************
80: * @returns void
81: ****************************************************************************/
82: function useIsoEncoding()
83: {
84: $this->targetEncoding = 'ISO-8859-1';
85: xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->targetEncoding);
86: }
87:
88: /****************************************************************************
89: * @returns void
90: ****************************************************************************/
91: function useAsciiEncoding()
92: {
93: $this->targetEncoding = 'US-ASCII';
94: xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->targetEncoding);
95: }
96:
97: /****************************************************************************
98: * @returns void
99: ****************************************************************************/
100: function useUtfEncoding()
101: {
102: $this->targetEncoding = 'UTF-8';
103: xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->targetEncoding);
104: }
105:
106: /****************************************************************************
107: Returns the name of the xml tag being parsed
108: * @returns string
109: ****************************************************************************/
110: function getCurrentTag()
111: {
112: return $this->tags[count($this->tags) - 1];
113: }
114:
115: function getParentTag()
116: {
117: if (isset($this->tags[count($this->tags) - 2])) {
118: return $this->tags[count($this->tags) - 2];
119: }
120: return false;
121: }
122:
123:
124:
125: /*---------------------------------------------------------------------------
126: Parser methods
127: ---------------------------------------------------------------------------*/
128:
129: /****************************************************************************
130: * @returns void
131: ****************************************************************************/
132: function parse()
133: {
134: if (!is_resource($this->input)) {
135: if (!xml_parse($this->parser, $this->input)) {
136: $this->setErrors($this->getXmlError());
137: return false;
138: }
139: //if (!$fp = fopen($this->input, 'r')) {
140: // $this->setErrors('Could not open file: '.$this->input);
141: // return false;
142: //}
143: } else {
144: while ($data = fread($this->input, 4096)) {
145: if (!xml_parse($this->parser, str_replace("'", "'", $data), feof($this->input))) {
146: $this->setErrors($this->getXmlError());
147: fclose($this->input);
148: return false;
149: }
150: }
151: fclose($this->input);
152: }
153: return true;
154: }
155:
156: /****************************************************************************
157: * @returns void
158: ****************************************************************************/
159: function free()
160: {
161: xml_parser_free($this->parser);
162:
163: if (!method_exists($this, '__destruct')) {
164: unset($this);
165: } else {
166: $this->__destruct();
167: }
168: }
169:
170: /****************************************************************************
171: * @private
172: * @returns string
173: ****************************************************************************/
174: function getXmlError()
175: {
176: return sprintf("XmlParse error: %s at line %d", xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser));
177: }
178:
179: /*---------------------------------------------------------------------------
180: Custom Handler Methods
181: ---------------------------------------------------------------------------*/
182:
183: /****************************************************************************
184: Adds a callback function to be called when a tag is encountered.<br>
185: Functions that are added must be of the form:<br>
186: <b>functionName( $attributes )</b>
187: * @param $tagName string. The name of the tag currently being parsed.
188: * @param $functionName string. The name of the function in XmlDocument's
189: subclass.
190: * @returns void
191: ****************************************************************************/
192: function addTagHandler(&$tagHandler)
193: {
194: $name = $tagHandler->getName();
195: if (is_array($name)) {
196: foreach ($name as $n) {
197: $this->tagHandlers[$n] =& $tagHandler;
198: }
199: } else {
200: $this->tagHandlers[$name] =& $tagHandler;
201: }
202: }
203:
204:
205: /*---------------------------------------------------------------------------
206: Private Handler Methods
207: ---------------------------------------------------------------------------*/
208:
209: /****************************************************************************
210: Callback function that executes whenever a the start of a tag
211: occurs when being parsed.
212: * @param $parser int. The handle to the parser.
213: * @param $tagName string. The name of the tag currently being parsed.
214: * @param $attributesArray attay. The list of attributes associated with
215: the tag.
216: * @private
217: * @returns void
218: ****************************************************************************/
219: function handleBeginElement($parser, $tagName, $attributesArray)
220: {
221: array_push($this->tags, $tagName);
222: $this->level++;
223: if (isset($this->tagHandlers[$tagName]) && is_subclass_of($this->tagHandlers[$tagName], 'xmltaghandler')) {
224: $this->tagHandlers[$tagName]->handleBeginElement($this, $attributesArray);
225: } else {
226: $this->handleBeginElementDefault($parser, $tagName, $attributesArray);
227: }
228: }
229:
230: /****************************************************************************
231: Callback function that executes whenever the end of a tag
232: occurs when being parsed.
233: * @param $parser int. The handle to the parser.
234: * @param $tagName string. The name of the tag currently being parsed.
235: * @private
236: * @returns void
237: ****************************************************************************/
238: function handleEndElement($parser, $tagName)
239: {
240: array_pop($this->tags);
241: if (isset($this->tagHandlers[$tagName]) && is_subclass_of($this->tagHandlers[$tagName], 'xmltaghandler')) {
242: $this->tagHandlers[$tagName]->handleEndElement($this);
243: } else {
244: $this->handleEndElementDefault($parser, $tagName);
245: }
246: $this->level--;
247: }
248:
249: /****************************************************************************
250: Callback function that executes whenever character data is encountered
251: while being parsed.
252: * @param $parser int. The handle to the parser.
253: * @param $data string. Character data inside the tag
254: * @returns void
255: ****************************************************************************/
256: function handleCharacterData($parser, $data)
257: {
258: $tagHandler =& $this->tagHandlers[$this->getCurrentTag()];
259: if (isset($tagHandler) && is_subclass_of($tagHandler, 'xmltaghandler')) {
260: $tagHandler->handleCharacterData($this, $data);
261: } else {
262: $this->handleCharacterDataDefault($parser, $data);
263: }
264: }
265:
266: /****************************************************************************
267: * @param $parser int. The handle to the parser.
268: * @returns void
269: ****************************************************************************/
270: function handleProcessingInstruction($parser, &$target, &$data)
271: {
272: // if($target == 'php') {
273: // eval($data);
274: // }
275: }
276:
277: /****************************************************************************
278: * @param $parser int. The handle to the parser.
279: * @returns void
280: ****************************************************************************/
281: function handleDefault($parser, $data)
282: {
283:
284: }
285:
286: /****************************************************************************
287: * @param $parser int. The handle to the parser.
288: * @returns void
289: ****************************************************************************/
290: function handleUnparsedEntityDecl($parser, $entityName, $base, $systemId, $publicId, $notationName)
291: {
292:
293: }
294:
295: /****************************************************************************
296: * @param $parser int. The handle to the parser.
297: * @returns void
298: ****************************************************************************/
299: function handleNotationDecl($parser, $notationName, $base, $systemId, $publicId)
300: {
301:
302: }
303:
304: /****************************************************************************
305: * @param $parser int. The handle to the parser.
306: * @returns void
307: ****************************************************************************/
308: function handleExternalEntityRef($parser, $openEntityNames, $base, $systemId, $publicId)
309: {
310:
311: }
312:
313: /**
314: * The default tag handler method for a tag with no handler
315: *
316: * @abstract
317: */
318: function handleBeginElementDefault($parser, $tagName, $attributesArray)
319: {
320: }
321:
322: /**
323: * The default tag handler method for a tag with no handler
324: *
325: * @abstract
326: */
327: function handleEndElementDefault($parser, $tagName)
328: {
329: }
330:
331: /**
332: * The default tag handler method for a tag with no handler
333: *
334: * @abstract
335: */
336: function handleCharacterDataDefault($parser, $data)
337: {
338: }
339:
340: /**
341: * Sets error messages
342: *
343: * @param $error string an error message
344: */
345: function setErrors($error)
346: {
347: $this->errors[] = trim($error);
348: }
349:
350: /**
351: * Gets all the error messages
352: *
353: * @param $ashtml bool return as html?
354: * @return mixed
355: */
356: function &getErrors($ashtml = true)
357: {
358: if (!$ashtml) {
359: return $this->errors;
360: } else {
361: $ret = '';
362: if (count($this->errors) > 0) {
363: foreach ($this->errors as $error) {
364: $ret .= $error.'<br />';
365: }
366: }
367: return $ret;
368: }
369: }
370: }
371:
372: ?>