Subversion-Projekte lars-tiefland.content-management

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// $Id: EbatNs_ResponseParser.php 4066 2011-11-03 08:13:59Z tiefland $
3
// $Log: EbatNs_ResponseParser.php,v $
4
// Revision 1.3  2008-05-28 16:53:46  michael
5
// fixed bug for not available types
6
//
7
// Revision 1.2  2008/05/02 15:04:05  carsten
8
// Initial, PHP5
9
//
10
//
11
define('EBATNS_PARSEMODE_CALL', 1);
12
define('EBATNS_PARSEMODE_NOTIFICATION', 2);
13
define('EBATNS_PARSEMODE_CALLEXTENSION', 3);
14
 
15
define('EBATNS_PSTATENOT_INITIAL', 0);
16
define('EBATNS_PSTATENOT_HAS_SIGNATURE', 1);
17
define('EBATNS_PSTATENOT_FOUND_ENVBODY', 2);
18
define('EBATNS_PSTATENOT_IN_RESPONSE', 3);
19
 
20
class EbatNs_ResponseParser
21
{
22
    protected $_client;
23
 
24
    protected $_options;
25
 
26
    protected $_responseObject = null;
27
 
28
    protected $_waitForResponseTag = null;
29
 
30
    protected $_responseTypeName = null;
31
 
32
    protected $_inResponse = false;
33
 
34
    protected $_stData = array();
35
 
36
    protected $_stValue = array();
37
 
38
    protected $_stMap = array();
39
 
40
    protected $_depth = 0;
41
 
42
    protected $_typeNs = null;
43
 
44
    protected $_typeMap = array();
45
 
46
    protected $_hasFault = false;
47
 
48
    protected $_hasError = false;
49
 
50
    protected $_parseMode = EBATNS_PARSEMODE_CALL;
51
 
52
    protected $_tmpNotificationSignature = null;
53
 
54
    protected $_notificationParseState = EBATNS_PSTATENOT_INITIAL;
55
 
56
    protected $_extensionPrefix = null;
57
 
58
    function __construct ($client, $typeNs, $options = null)
59
    {
60
        $this->_client = $client;
61
 
62
        $this->_typeNs = $typeNs;
63
 
64
        $this->setOption('NO_ATTRIBUTES', false);
65
        $this->setOption('NO_UNSET_METADATA', false);
66
        $this->setOption('NO_REDUCE', false);
67
        $this->setOption('NO_EMPTY_ARRAYS');
68
        $this->setOption('NO_EMPTY_VALUES');
69
        $this->setOption('FLATTEN_ON_ARRAYTYPE');
70
 
71
        if ($options)
72
        {
73
            $this->_options = array_merge($this->_options, $options);
74
        }
75
    }
76
 
77
    function setOption ($name, $value = true)
78
    {
79
        $this->_options[$name] = $value;
80
    }
81
 
82
    function setMode ($newMode)
83
    {
84
        $this->_parseMode = $newMode;
85
    }
86
 
87
    function setExtensionPrefix ($extensionPrefix)
88
    {
89
        $this->_extensionPrefix = $extensionPrefix;
90
    }
91
 
92
    /**
93
     * Will reduce an object
94
     *
95
     * @param EbatNs_SimpleType $element
96
     * @return integer	Number of attributes remaining
97
     */
98
    function _reduceElement ($element)
99
    {
100
        if ($this->_options['NO_REDUCE'])
101
            return true;
102
 
103
        return $element->reduceElement($this->_options['NO_EMPTY_VALUES']);
104
    }
105
 
106
    function _includeType ($typeName)
107
    {
108
        if (! class_exists($typeName))
109
        {
110
            $typeFileName = basename($typeName);
111
            require_once $typeFileName . '.php';
112
        }
113
 
114
        return (class_exists($typeName));
115
    }
116
 
117
    protected function _makeValue ($typeName)
118
    {
119
        if ($this->_includeType($typeName))
120
        {
121
            $t = new $typeName();
122
 
123
            // transfer the typeInfo to the typeMap
124
            $typeInfo['typeName'] = $typeName;
125
            $typeInfo['elements'] = $t->getMetaDataElements();
126
            $typeInfo['attributes'] = $t->getMetaDataAttributes();
127
 
128
            $this->_typeMap[strtolower($typeName)] = $typeInfo;
129
 
130
            // unset the type-information from
131
            // the resulting value
132
            if (! $this->_options['NO_UNSET_METADATA'])
133
            {
134
                $t->unsetMetaData();
135
            }
136
            return $t;
137
        } else
138
            return null;
139
    }
140
 
141
    function decode ($responseName, $messageText, $parseMode = EBATNS_PARSEMODE_CALL, $responseTypeName = null)
142
    {
143
        $this->_responseObject = null;
144
        $this->_stValue = array();
145
        $this->_stData = array();
146
        $this->_stMap = array();
147
        $this->_depth = 0;
148
 
149
        $this->_hasError = false;
150
        $this->_hasFault = false;
151
 
152
        $this->_waitForResponseTag = $responseName;
153
 
154
        if ($responseTypeName != null)
155
            $this->_responseTypeName = $responseTypeName; else
156
            $this->_responseTypeName = $responseName . 'Type';
157
 
158
        if ($parseMode != EBATNS_PARSEMODE_CALL)
159
            $this->setMode($parseMode);
160
 
161
        $encoding = 'UTF-8';
162
 
163
        $parser = xml_parser_create($encoding);
164
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
165
        xml_set_object($parser, $this);
166
        xml_set_element_handler($parser, '_startElement', '_endElement');
167
        xml_set_character_data_handler($parser, '_cData');
168
 
169
        if (! xml_parse($parser, $messageText, true))
170
        {
171
            $errMsg = sprintf('XML error on line %d col %d byte %d %s', xml_get_current_line_number($parser), xml_get_current_column_number($parser), xml_get_current_byte_index($parser), xml_error_string(xml_get_error_code($parser)));
172
 
173
            // create a error-object
174
            $errResponse = new EbatNs_ResponseError();
175
            $errResponse->raise($errMsg, 90000 + 1);
176
            return $errResponse;
177
        }
178
        xml_parser_free($parser);
179
 
180
        if ($this->_hasFault)
181
        {
182
            return $this->_decodeFault($messageText);
183
        }
184
 
185
        return $this->_responseObject;
186
    }
187
 
188
    function _decodeFault ($msg)
189
    {
190
        $errResponse = new EbatNs_ResponseError();
191
 
192
        $p = xml_parser_create();
193
        xml_parse_into_struct($p, $msg, $lstValues, $index);
194
        xml_parser_free($p);
195
 
196
        foreach ($lstValues as $value)
197
        {
198
            if ($value['type'] == 'complete')
199
            {
200
                switch ($value['tag'])
201
                {
202
                    case 'FAULTSTRING':
203
                        $errResponse->raise('soap-fault: ' .
204
                             utf8_decode($value['value']), 90000 +
205
                             2);
206
                    break;
207
                case 'ERRORCODE':
208
                    $code = $value['value'];
209
                    break;
210
                case 'SEVERITY':
211
                    $severity = $value['value'];
212
                    break;
213
                case 'DETAILEDMESSAGE':
214
                    $errResponse->raise($value['value'], $code, $severity);
215
                    break;
216
                }
217
            }
218
        }
219
        $this->_reduceElement($errResponse);
220
        return $errResponse;
221
    }
222
 
223
    function _startElement ($parser, $name, $attrs)
224
    {
225
        // wait for the starting-element
226
        if ((($this->_parseMode == EBATNS_PARSEMODE_CALL || $this->_parseMode == EBATNS_PARSEMODE_CALLEXTENSION)
227
            && !$this->_inResponse && $name != $this->_waitForResponseTag)
228
            || ($this->_parseMode == EBATNS_PARSEMODE_NOTIFICATION && $this->_notificationParseState < EBATNS_PSTATENOT_IN_RESPONSE))
229
            {
230
                if ($this->_parseMode == EBATNS_PARSEMODE_CALL || $this->_parseMode == EBATNS_PARSEMODE_CALLEXTENSION)
231
                {
232
                    if ($name == 'soapenv:Fault')
233
                        $this->_hasFault = true;
234
                    return;
235
                }
236
                else
237
                {
238
                    if (strstr($name, ':NotificationSignature') !== false)
239
                    {
240
                        $this->_notificationParseState = EBATNS_PSTATENOT_HAS_SIGNATURE;
241
                        return;
242
                    }
243
 
244
                    if ($name == 'soapenv:Body')
245
                    {
246
                        $this->_notificationParseState = EBATNS_PSTATENOT_FOUND_ENVBODY;
247
                        return;
248
                    }
249
 
250
                    if ($this->_notificationParseState == EBATNS_PSTATENOT_FOUND_ENVBODY)
251
                    {
252
                        // know we will have the name of the response in $name
253
                        // so we just set the state and recall the method again !
254
                        $this->_notificationParseState = EBATNS_PSTATENOT_IN_RESPONSE;
255
                        $this->_waitForResponseTag = $name;
256
                        $this->_responseTypeName = $name . 'Type';
257
 
258
                        $this->_depth = 0;
259
 
260
                        return $this->_startElement($parser, $name, $attrs);
261
                    }
262
                }
263
            }
264
            else
265
            {
266
                // setup the response-object
267
                if (! $this->_inResponse)
268
                {
269
                    $parent = null;
270
                    $current = $this->_makeValue($this->_responseTypeName);
271
                    $this->_inResponse = true;
272
                    $mapName = null;
273
                }
274
                else
275
                {
276
                    $mapName = $name;
277
 
278
                    $parent = $this->_stValue[$this->_depth];
279
                    $typeInfo = $this->_typeMap[strtolower(get_class($parent))];
280
 
281
                    $elementInfo = $typeInfo['elements'][$name];
282
 
283
                    if ($elementInfo['nsURI'] == $this->_typeNs)
284
                    {
285
                        // let CodeTypes (Facets/enums) be result in just
286
                        // plain strings but child-objects
287
                        if (strpos($elementInfo['type'], 'CodeType') === false)
288
                        {
289
                            $current = $this->_makeValue($elementInfo['type']);
290
                            if ($attrs)
291
                            {
292
                                foreach ($attrs as $attKey => $attValue)
293
                                {
294
                                    $current->setTypeAttribute($attKey, $attValue);
295
                                }
296
                            }
297
                        }
298
                        else
299
                        {
300
                            $current = null;
301
                        }
302
                    }
303
                    else
304
                    {
305
                        $current = null;
306
                    }
307
                }
308
            }
309
 
310
        $this->_depth ++;
311
        $this->_stData[$this->_depth] = null;
312
        $this->_stValue[$this->_depth] = $current;
313
        $this->_stMap[$this->_depth] = $mapName;
314
    }
315
 
316
    function _endElement ($parser, $name)
317
    {
318
        if ($this->_parseMode == EBATNS_PARSEMODE_NOTIFICATION && $this->_notificationParseState == EBATNS_PSTATENOT_HAS_SIGNATURE)
319
        {
320
            $this->_tmpNotificationSignature = $this->_stData[$this->_depth];
321
            return;
322
        }
323
 
324
        if (! $this->_inResponse)
325
        {
326
            return;
327
        }
328
 
329
        if ($name == $this->_waitForResponseTag)
330
        {
331
            $this->_responseObject = $this->_stValue[1];
332
 
333
            if ($this->_parseMode == EBATNS_PARSEMODE_NOTIFICATION)
334
                $this->_responseObject->NotificationSignature = $this->_tmpNotificationSignature;
335
 
336
            $this->_reduceElement($this->_responseObject);
337
            // switch off the parsing again
338
            $this->_inResponse = false;
339
            return;
340
        }
341
 
342
        $current = $this->_stValue[$this->_depth];
343
        $parent = $this->_stValue[$this->_depth - 1];
344
        $mapName = $this->_stMap[$this->_depth];
345
        $data = $this->_stData[$this->_depth];
346
        $this->_depth --;
347
 
348
        if ($current)
349
            $typeInfoCurrent = $this->_typeMap[strtolower(get_class($current))];
350
        if ($parent)
351
            $typeInfoParent = $this->_typeMap[strtolower(get_class($parent))];
352
 
353
        $infoMember = $typeInfoParent['elements'][$mapName];
354
        $data = $this->_decodeData($data, $infoMember['type']);
355
 
356
        if ($mapName)
357
        {
358
            if ($current)
359
            {
360
                if (count($typeInfoCurrent['elements']) == 0)
361
                {
362
                    $currentIsEmpty = false;
363
                    if (count($typeInfoCurrent['attributes']) ==
364
 
365
                        {
366
                            // in case there neither child-elements nor attributes
367
                            // we reduced the data to a plain-value !
368
                            $current = $data;
369
                    } else
370
                    {
371
                        $current->setTypeValue($data);
372
                    }
373
                }
374
                if (is_object($current))
375
                {
376
                    $currentIsEmpty = ! $this->_reduceElement($current);
377
 
378
                    if (! $currentIsEmpty)
379
                    {
380
                        if ($this->_client->hasCallbacks())
381
                        {
382
                            if ($this->_client->_handleDataType($infoMember['type'], $current, $mapName))
383
                                return;
384
                        }
385
                    }
386
                }
387
            } else
388
            {
389
                $current = $data;
390
                $currentIsEmpty = false;
391
 
392
				if (!$infoMember['type'])
393
					 $currentIsEmpty = true;
394
            }
395
 
396
            if ($infoMember)
397
            {
398
                list ($lower, $upper) = split("\.\.", $infoMember['cardinality']);
399
                if ($upper == '*' || $upper > 1)
400
                {
401
                    if ($this->_options['NO_EMPTY_ARRAYS'] &&
402
                         $currentIsEmpty)
403
                        {
404
                            // do not add an "empty" object
405
                            return;
406
                    }
407
                    else
408
                    {
409
                        $parent->__addArray($mapName, $current);
410
                        // $parent->{$mapName}[] = $current;
411
                        return;
412
                    }
413
                }
414
            }
415
 
416
            // do not set an "empty" value
417
            if ($this->_options['NO_EMPTY_VALUES'] &&
418
                 $currentIsEmpty)
419
                    return;
420
 
421
            if ($this->_options['FLATTEN_ON_ARRAYTYPE'])
422
            {
423
                // using this option will flatten type that contain "ArrayType"
424
                // so instead of CategoryArray -> Category -> php-array()
425
                // you will just get CategoryArray -> php-array(), so leaving out the
426
                // "Category" - subelement
427
 
428
 
429
                // check if the parents-element's typename
430
                // contains arraytype
431
                if (strpos($infoMember['type'], 'ArrayType') !==
432
                     false)
433
                    {
434
                        $arrayTypeInfo = $this->_typeMap[strtolower($infoMember['type'])];
435
                    if (count($arrayTypeInfo['elements']) == 1)
436
                    {
437
                        $key = array_keys($arrayTypeInfo['elements']);
438
 
439
                        // $current = $current->{$key[0]};
440
                        $current = $current->__getByKey($key[0]);
441
                    }
442
                }
443
            }
444
 
445
            if ($parent)
446
				$parent->__setByKey($mapName, $current);
447
        }
448
    }
449
 
450
    function _cData ($parser, $data)
451
    {
452
        if ($this->_parseMode == EBATNS_PARSEMODE_NOTIFICATION && $this->_notificationParseState == EBATNS_PSTATENOT_HAS_SIGNATURE)
453
        {
454
            $this->_stData[$this->_depth] .= $data;
455
        }
456
 
457
        if (! $this->_inResponse)
458
            return;
459
 
460
        $this->_stData[$this->_depth] .= $data;
461
    }
462
 
463
    function _decodeData ($data, $type = 'string')
464
    {
465
        if ($this->_client->hasDataConverter())
466
        {
467
            return $this->_client->getDataConverter()->decodeData($data, $type);
468
        }
469
        else
470
        {
471
            return $data;
472
        }
473
    }
474
}
475
?>