Completed
Push — master ( 312590...670503 )
by Marco
03:19
created

XmlrpcEncoder::encodeError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 6
nc 1
nop 2
crap 1
1
<?php namespace Comodojo\Xmlrpc;
2
3
use \Comodojo\Exception\XmlrpcException;
4
use \XMLWriter;
5
use \Exception;
6
7
/** 
8
 * XML-RPC encoder
9
 *
10
 * Main features:
11
 * - support for <nil /> and <ex:nil />
12
 * - implements true, XML compliant, HTML numeric entities conversion
13
 * - support for CDATA values
14
 *
15
 * @package     Comodojo Spare Parts
16
 * @author      Marco Giovinazzi <[email protected]>
17
 * @license     MIT
18
 *
19
 * LICENSE:
20
 * 
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 */
29
30
class XmlrpcEncoder {
31
32
    /**
33
     * Request/Response encoding
34
     *
35
     * @var string
36
     */
37
    private $encoding;
38
39
    /**
40
     * References of special values (base64, datetime, cdata)
41
     *
42
     * @var array
43
     */
44
    private $special_types = array();
45
46
    /**
47
     * ex:nil switch (for apache rpc compatibility)
48
     *
49
     * @var bool
50
     */
51
    private $use_ex_nil = false;
52
53
    /**
54
     * Constructor method
55
     */
56 27
    final public function __construct() {
57
58 27
        $this->encoding = defined("XMLRPC_DEFAULT_ENCODING") ? strtolower(XMLRPC_DEFAULT_ENCODING) : 'utf-8';
59
60 27
    }
61
62
    /**
63
     * Set encoding 
64
     *
65
     * @param   string   $encoding
66
     *
67
     * @return  \Comodojo\Xmlrpc\XmlrpcEncoder
68
     */
69
    final public function setEncoding($encoding) {
70
71
        $this->encoding = $encoding;
72
73
        return $this;
74
75
    }
76
77
    /**
78
     * Get encoding 
79
     *
80
     * @return  string
81
     */
82
    final public function getEncoding() {
83
84
        return $this->encoding;
85
86
    }
87
88
    /**
89
     * Handle base64 and datetime values 
90
     *
91
     * @param   mixed    $value  The referenced value
92
     * @param   string   $type   The type of value
93
     *
94
     * @return  \Comodojo\Xmlrpc\XmlrpcEncoder
95
     */
96 6
    final public function setValueType(&$value, $type) {
97
98 6
        if ( empty($value) || !in_array(strtolower($type), array("base64", "datetime", "cdata")) ) throw new XmlrpcException("Invalid value type");
99
100 6
        $this->special_types[$value] = strtolower($type);
101
102 6
        return $this;
103
104
    }
105
106
    /**
107
     * Use <ex:nil /> instead of <nil /> (apache xmlrpc compliant)
108
     *
109
     * @param   bool    $mode
110
     *
111
     * @return  \Comodojo\Xmlrpc\XmlrpcEncoder 
112
     */
113 3
    final public function useExNil($mode = true) {
114
115 3
        $this->use_ex_nil = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
116
117 3
        return $this;
118
119
    }
120
121
    /**
122
     * Encode an xmlrpc response
123
     *
124
     * It expects a scalar, array or NULL as $data and will try to encode it as a valid xmlrpc response.
125
     *
126
     * @param   mixed   $data
127
     *
128
     * @return  string  xmlrpc formatted response
129
     *
130
     * @throws  \Comodojo\Exception\XmlrpcException
131
     * @throws  \Exception
132
     */
133 6
    public function encodeResponse($data) {
134
135 6
        $xml = new XMLWriter(); 
136
137 6
        $xml->openMemory();
138
139 6
        $xml->setIndent(false);
140
141 6
        $xml->startDocument('1.0', $this->encoding); 
142
143 6
        $xml->startElement("methodResponse");
144
145 6
            $xml->startElement("params");
146
147 6
                $xml->startElement("param");
148
149 6
                    $xml->startElement("value");
150
151
                    try {
152
                    
153 6
                        $this->encodeValue($xml, $data);
154
155 2
                    } catch (XmlrpcException $xe) {
156
                        
157
                        throw $xe;
158
159
                    } catch (Exception $e) {
160
                        
161
                        throw $e;
162
163
                    }
164
165 6
                    $xml->endElement();
166
167 6
                $xml->endElement();
168
169 6
            $xml->endElement();
170
171 6
        $xml->endElement();
172
173 6
        $xml->endDocument();
174
175 6
        return trim($xml->outputMemory());
176
177
    }
178
179
    /**
180
     * Encode an xmlrpc call
181
     *
182
     * It expects an array of values as $data and will try to encode it as a valid xmlrpc call.
183
     *
184
     * @param   string  $method
185
     * @param   array   $data
186
     *
187
     * @return  string  xmlrpc formatted call
188
     *
189
     * @throws  \Comodojo\Exception\XmlrpcException
190
     * @throws  \Exception
191
     */
192 18
    public function encodeCall($method, $data = array()) {
193
194 18
        $xml = new XMLWriter(); 
195
196 18
        $xml->openMemory();
197
198 18
        $xml->setIndent(false);
199
200 18
        $xml->startDocument('1.0', $this->encoding); 
201
202 18
        $xml->startElement("methodCall");
203
204 18
            $xml->writeElement("methodName", trim($method));
205
206 18
            $xml->startElement("params");
207
208
            try {
209
                        
210 18
                foreach ( $data as $d ) {
211
212 15
                    $xml->startElement("param");
213
214 15
                        $xml->startElement("value");
215
216 15
                            $this->encodeValue($xml, $d);
217
218 15
                        $xml->endElement();
219
220 17
                    $xml->endElement();
221
222 6
                }
223
224 6
            } catch (XmlrpcException $xe) {
225
                        
226
                throw $xe;
227
228
            }
229
230 18
            $xml->endElement();
231
232 18
        $xml->endElement();
233
234 18
        $xml->endDocument();
235
236 18
        return trim($xml->outputMemory());
237
238
    }
239
240
    /**
241
     * Encode an xmlrpc multicall
242
     *
243
     * It expects in input a key->val array where key
244
     * represent the method and val the parameters.
245
     *
246
     * @param   array   $data
247
     *
248
     * @return  string  xmlrpc formatted call
249
     *
250
     * @throws  \Comodojo\Exception\XmlrpcException
251
     * @throws  \Exception
252
     */
253 6
    public function encodeMulticall($data) {
254
255 6
        $packed_requests = array();
256
257 6
        foreach ( $data as $methodName => $params ) {
258
259 6
            if ( is_int($methodName) && count($params) == 2 ) {
260
261 3
                array_push($packed_requests, array(
262 3
                    "methodName"    =>  $params[0],
263 3
                    "params"        =>  $params[1]
264 1
                ));    
265
266 1
            } else {
267
268 3
                array_push($packed_requests, array(
269 3
                    "methodName"    =>  $methodName,
270 4
                    "params"        =>  $params
271 1
                ));
272
273
            }
274
275 2
        }
276
277 6
        return $this->encodeCall("system.multicall", array($packed_requests));
278
279
    }
280
281
    /**
282
     * Encode an xmlrpc error
283
     *
284
     * @param   int     $error_code
285
     * @param   string  $error_message
286
     *
287
     * @return  string  xmlrpc formatted error
288
     */
289 3
    public function encodeError($error_code, $error_message) {
290
291 3
        $payload  = '<?xml version="1.0" encoding="'.$this->encoding.'"?>';
292 3
        $payload .= "<methodResponse>";
293 3
        $payload .= $this->encodeFault($error_code, $error_message);
294 3
        $payload .= "</methodResponse>";
295
296 3
        return $payload;
297
298
    }
299
300
    /**
301
     * Encode an xmlrpc fault (without full xml document body)
302
     *
303
     * @param   int     $error_code
304
     * @param   string  $error_message
305
     *
306
     * @return  string  xmlrpc formatted error
307
     */
308 3
    private function encodeFault($error_code, $error_message) {
309
310 3
        $value = htmlentities($error_message, ENT_QUOTES, $this->encoding, false);
311
312 3
        $string = preg_replace_callback('/&([a-zA-Z][a-zA-Z0-9]+);/S', 'self::numericEntities', $value);
313
314 3
        $payload = "<fault>";
315 3
        $payload .= "<value>";
316 3
        $payload .= "<struct>";
317 3
        $payload .= "<member>";
318 3
        $payload .= "<name>faultCode</name>";
319 3
        $payload .= "<value><int>".$error_code."</int></value>";
320 3
        $payload .= "</member>";
321 3
        $payload .= "<member>";
322 3
        $payload .= "<name>faultString</name>";
323 3
        $payload .= "<value><string>".$string."</string></value>";
324 3
        $payload .= "</member>";
325 3
        $payload .= "</struct>";
326 3
        $payload .= "</value>";
327 3
        $payload .= "</fault>";
328
329 3
        return $payload;
330
331
    }
332
333
    /**
334
     * Encode a value using XMLWriter object $xml
335
     *
336
     * @param   XMLWriter    $xml
337
     * @param   mixed        $value
338
     *
339
     * @throws  \Comodojo\Exception\XmlrpcException
340
     */
341 21
    private function encodeValue(XMLWriter $xml, $value) {
342
343 21
        if ( $value === null ) $xml->writeRaw($this->use_ex_nil === true ? '<ex:nil />' : '<nil />');
344
345 21
        else if ( is_array($value) ) {
346
347 12
            if ( !self::catchStruct($value) ) $this->encodeArray($xml, $value);
348
349 11
            else $this->encodeStruct($xml, $value);
350
351 21
        } else if ( @array_key_exists($value, $this->special_types) ) {
352
353 6
            switch ( $this->special_types[$value] ) {
354
355 6
                case 'base64':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
356
                    
357 3
                    $xml->writeElement("base64", $value);
358
359 3
                    break;
360
                
361 3
                case 'datetime':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
362
                    
363
                    $xml->writeElement("dateTime.iso8601", self::timestampToIso8601Time($value));
364
365
                    break;
366
                
367 3
                case 'cdata':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
368
                    
369 3
                    $xml->writeCData($value);
370
371 5
                    break;
372
                
373 2
            }
374
375 21
        } else if ( is_bool($value) ) {
376
377 3
            $xml->writeElement("boolean", $value ? 1 : 0);
378
379 21
        } else if ( is_double($value) ) {
380
381
            $xml->writeElement("double", $value);
382
383 21
        } else if ( is_integer($value) ) {
384
            
385 3
            $xml->writeElement("int", $value);
386
387 21
        } else if ( is_object($value) ) {
388
389
            $this->encodeObject($xml, $value);
390
391 21
        } else if ( is_string($value) ) {
392
393 21
            $value = htmlentities($value, ENT_QUOTES, $this->encoding, false);
394
395 21
            $string = preg_replace_callback('/&([a-zA-Z][a-zA-Z0-9]+);/S', 'self::numericEntities', $value);
396
397 21
            $xml->writeRaw("<string>".$string."</string>");
398
399 7
        } else throw new XmlrpcException("Unknown type for encoding");
400
401 21
    }
402
403
    /**
404
     * Encode an array using XMLWriter object $xml
405
     *
406
     * @param   XMLWriter    $xml
407
     * @param   mixed        $value
408
     */
409 12
    private function encodeArray(XMLWriter $xml, $value) {
410
411 12
        $xml->startElement("array");
412
413 12
            $xml->startElement("data");
414
415 12
                foreach ( $value as $entry ) {
416
417 12
                    $xml->startElement("value");
418
419 12
                    $this->encodeValue($xml, $entry);
420
421 12
                    $xml->endElement();
422
423 4
                }
424
425 12
            $xml->endElement();
426
427 12
        $xml->endElement();
428
429 12
    }
430
431
    /**
432
     * Encode an object using XMLWriter object $xml
433
     *
434
     * @param   XMLWriter    $xml
435
     * @param   mixed        $value
436
     *
437
     * @throws  \Comodojo\Exception\XmlrpcException
438
     */
439
    private function encodeObject(XMLWriter $xml, $value) {
440
441
        if ( $value instanceof \DateTime ) $xml->writeElement("dateTime.iso8601", self::timestampToIso8601Time($value->format('U')));
442
443
        else throw new XmlrpcException("Unknown type for encoding");
444
445
    }
446
447
    /**
448
     * Encode a struct using XMLWriter object $xml
449
     *
450
     * @param   XMLWriter    $xml
451
     * @param   mixed        $value
452
     *
453
     * @throws  \Comodojo\Exception\XmlrpcException
454
     */
455 9
    private function encodeStruct(XMLWriter $xml, $value) {
456
457 9
        $xml->startElement("struct");
458
459 9
        foreach ( $value as $k => $v ) {
460
461 9
            $xml->startElement("member");
462
463 9
                $xml->writeElement("name", $k);
464
465 9
                $xml->startElement("value");
466
467 9
                    $this->encodeValue($xml, $v);
468
469 9
                $xml->endElement();
470
471 9
            $xml->endElement();
472
473 3
        }
474
475 9
        $xml->endElement();
476
477 9
    }
478
479
    /**
480
     * Return true if $value is a struct, false otherwise
481
     *
482
     * @param   mixed   $value
483
     *
484
     * @return  bool
485
     */
486 12
    private static function catchStruct($value) {
487
488 12
        $values = count($value);
489
490 12
        for ( $i = 0; $i < $values; $i++ ) if ( !array_key_exists($i, $value) ) return true;
491
492 12
        return false;
493
494
    }
495
496
    /**
497
     * Convert timestamp to Iso8601
498
     *
499
     * @param   int     $timestamp
500
     *
501
     * @return  string  Iso8601 formatted date
502
     */
503
    private static function timestampToIso8601Time($timestamp) {
504
    
505
        return date("Ymd\TH:i:s", $timestamp);
506
507
    }
508
509
    /**
510
     * Recode named entities into numeric ones
511
     *
512
     * @param   mixed   $matches
513
     *
514
     * @return  string  Iso8601 formatted date
515
     */
516
    private static function numericEntities($matches) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
517
518
        static $table = array(
519
            'quot' => '&#34;', 'amp' => '&#38;', 'lt' => '&#60;', 'gt' => '&#62;', 'OElig' => '&#338;', 'oelig' => '&#339;',
520
            'Scaron' => '&#352;', 'scaron' => '&#353;', 'Yuml' => '&#376;', 'circ' => '&#710;', 'tilde' => '&#732;', 'ensp' => '&#8194;',
521
            'emsp' => '&#8195;', 'thinsp' => '&#8201;', 'zwnj' => '&#8204;', 'zwj' => '&#8205;', 'lrm' => '&#8206;', 'rlm' => '&#8207;',
522
            'ndash' => '&#8211;', 'mdash' => '&#8212;', 'lsquo' => '&#8216;', 'rsquo' => '&#8217;', 'sbquo' => '&#8218;', 'ldquo' => '&#8220;',
523
            'rdquo' => '&#8221;', 'bdquo' => '&#8222;', 'dagger' => '&#8224;', 'Dagger' => '&#8225;', 'permil' => '&#8240;', 'lsaquo' => '&#8249;',
524
            'rsaquo' => '&#8250;', 'euro' => '&#8364;', 'fnof' => '&#402;', 'Alpha' => '&#913;', 'Beta' => '&#914;', 'Gamma' => '&#915;',
525
            'Delta' => '&#916;', 'Epsilon' => '&#917;', 'Zeta' => '&#918;', 'Eta' => '&#919;', 'Theta' => '&#920;', 'Iota' => '&#921;',
526
            'Kappa' => '&#922;', 'Lambda' => '&#923;', 'Mu' => '&#924;', 'Nu' => '&#925;', 'Xi' => '&#926;', 'Omicron' => '&#927;',
527
            'Pi' => '&#928;', 'Rho' => '&#929;', 'Sigma' => '&#931;', 'Tau' => '&#932;', 'Upsilon' => '&#933;', 'Phi' => '&#934;',
528
            'Chi' => '&#935;', 'Psi' => '&#936;', 'Omega' => '&#937;', 'alpha' => '&#945;', 'beta' => '&#946;', 'gamma' => '&#947;',
529
            'delta' => '&#948;', 'epsilon' => '&#949;', 'zeta' => '&#950;', 'eta' => '&#951;', 'theta' => '&#952;', 'iota' => '&#953;',
530
            'kappa' => '&#954;', 'lambda' => '&#955;', 'mu' => '&#956;', 'nu' => '&#957;', 'xi' => '&#958;', 'omicron' => '&#959;',
531
            'pi' => '&#960;', 'rho' => '&#961;', 'sigmaf' => '&#962;', 'sigma' => '&#963;', 'tau' => '&#964;', 'upsilon' => '&#965;',
532
            'phi' => '&#966;', 'chi' => '&#967;', 'psi' => '&#968;', 'omega' => '&#969;', 'thetasym' => '&#977;', 'upsih' => '&#978;',
533
            'piv' => '&#982;', 'bull' => '&#8226;', 'hellip' => '&#8230;', 'prime' => '&#8242;', 'Prime' => '&#8243;', 'oline' => '&#8254;',
534
            'frasl' => '&#8260;', 'weierp' => '&#8472;', 'image' => '&#8465;', 'real' => '&#8476;', 'trade' => '&#8482;', 'alefsym' => '&#8501;',
535
            'larr' => '&#8592;', 'uarr' => '&#8593;', 'rarr' => '&#8594;', 'darr' => '&#8595;', 'harr' => '&#8596;', 'crarr' => '&#8629;',
536
            'lArr' => '&#8656;', 'uArr' => '&#8657;', 'rArr' => '&#8658;', 'dArr' => '&#8659;', 'hArr' => '&#8660;', 'forall' => '&#8704;',
537
            'part' => '&#8706;', 'exist' => '&#8707;', 'empty' => '&#8709;', 'nabla' => '&#8711;', 'isin' => '&#8712;', 'notin' => '&#8713;',
538
            'ni' => '&#8715;', 'prod' => '&#8719;', 'sum' => '&#8721;', 'minus' => '&#8722;', 'lowast' => '&#8727;', 'radic' => '&#8730;',
539
            'prop' => '&#8733;', 'infin' => '&#8734;', 'ang' => '&#8736;', 'and' => '&#8743;', 'or' => '&#8744;', 'cap' => '&#8745;',
540
            'cup' => '&#8746;', 'int' => '&#8747;', 'there4' => '&#8756;', 'sim' => '&#8764;', 'cong' => '&#8773;', 'asymp' => '&#8776;',
541
            'ne' => '&#8800;', 'equiv' => '&#8801;', 'le' => '&#8804;', 'ge' => '&#8805;', 'sub' => '&#8834;', 'sup' => '&#8835;',
542
            'nsub' => '&#8836;', 'sube' => '&#8838;', 'supe' => '&#8839;', 'oplus' => '&#8853;', 'otimes' => '&#8855;', 'perp' => '&#8869;',
543
            'sdot' => '&#8901;', 'lceil' => '&#8968;', 'rceil' => '&#8969;', 'lfloor' => '&#8970;', 'rfloor' => '&#8971;', 'lang' => '&#9001;',
544
            'rang' => '&#9002;', 'loz' => '&#9674;', 'spades' => '&#9824;', 'clubs' => '&#9827;', 'hearts' => '&#9829;', 'diams' => '&#9830;',
545
            'nbsp' => '&#160;', 'iexcl' => '&#161;', 'cent' => '&#162;', 'pound' => '&#163;', 'curren' => '&#164;', 'yen' => '&#165;',
546
            'brvbar' => '&#166;', 'sect' => '&#167;', 'uml' => '&#168;', 'copy' => '&#169;', 'ordf' => '&#170;', 'laquo' => '&#171;',
547
            'not' => '&#172;', 'shy' => '&#173;', 'reg' => '&#174;', 'macr' => '&#175;', 'deg' => '&#176;', 'plusmn' => '&#177;',
548
            'sup2' => '&#178;', 'sup3' => '&#179;', 'acute' => '&#180;', 'micro' => '&#181;', 'para' => '&#182;', 'middot' => '&#183;',
549
            'cedil' => '&#184;', 'sup1' => '&#185;', 'ordm' => '&#186;', 'raquo' => '&#187;', 'frac14' => '&#188;', 'frac12' => '&#189;',
550
            'frac34' => '&#190;', 'iquest' => '&#191;', 'Agrave' => '&#192;', 'Aacute' => '&#193;', 'Acirc' => '&#194;', 'Atilde' => '&#195;',
551
            'Auml' => '&#196;', 'Aring' => '&#197;', 'AElig' => '&#198;', 'Ccedil' => '&#199;', 'Egrave' => '&#200;', 'Eacute' => '&#201;',
552
            'Ecirc' => '&#202;', 'Euml' => '&#203;', 'Igrave' => '&#204;', 'Iacute' => '&#205;', 'Icirc' => '&#206;', 'Iuml' => '&#207;',
553
            'ETH' => '&#208;', 'Ntilde' => '&#209;', 'Ograve' => '&#210;', 'Oacute' => '&#211;', 'Ocirc' => '&#212;', 'Otilde' => '&#213;',
554
            'Ouml' => '&#214;', 'times' => '&#215;', 'Oslash' => '&#216;', 'Ugrave' => '&#217;', 'Uacute' => '&#218;', 'Ucirc' => '&#219;',
555
            'Uuml' => '&#220;', 'Yacute' => '&#221;', 'THORN' => '&#222;', 'szlig' => '&#223;', 'agrave' => '&#224;', 'aacute' => '&#225;',
556
            'acirc' => '&#226;', 'atilde' => '&#227;', 'auml' => '&#228;', 'aring' => '&#229;', 'aelig' => '&#230;', 'ccedil' => '&#231;',
557
            'egrave' => '&#232;', 'eacute' => '&#233;', 'ecirc' => '&#234;', 'euml' => '&#235;', 'igrave' => '&#236;', 'iacute' => '&#237;',
558
            'icirc' => '&#238;', 'iuml' => '&#239;', 'eth' => '&#240;', 'ntilde' => '&#241;', 'ograve' => '&#242;', 'oacute' => '&#243;',
559
            'ocirc' => '&#244;', 'otilde' => '&#245;', 'ouml' => '&#246;', 'divide' => '&#247;', 'oslash' => '&#248;', 'ugrave' => '&#249;',
560
            'uacute' => '&#250;', 'ucirc' => '&#251;', 'uuml' => '&#252;', 'yacute' => '&#253;', 'thorn' => '&#254;', 'yuml' => '&#255;'
561
        );
562
  
563
        // cleanup invalid entities
564
        return isset($table[$matches[1]]) ? $table[$matches[1]] : '';
565
566
    }
567
568
}
569