Passed
Push — master ( da2c1e...110169 )
by Gaetano
04:55 queued 01:13
created

Server::getSystemDispatchMap()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 38
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 2.0006

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 2
eloc 28
c 3
b 1
f 0
nc 2
nop 0
dl 0
loc 38
ccs 17
cts 18
cp 0.9444
crap 2.0006
rs 9.472
1
<?php
2
3
namespace PhpXmlRpc;
4
5
use PhpXmlRpc\Helper\Charset;
6
use PhpXmlRpc\Helper\Logger;
7
use PhpXmlRpc\Helper\XMLParser;
8
9
/**
10
 * Allows effortless implementation of XML-RPC servers
11
 */
12
class Server
13
{
14
    /**
15
     * Array defining php functions exposed as xmlrpc methods by this server.
16
     */
17
    protected $dmap = array();
18
19
    /**
20
     * Defines how functions in dmap will be invoked: either using an xmlrpc request object
21
     * or plain php values.
22
     * Valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
23
     * @todo create class constants for these
24
     */
25
    public $functions_parameters_type = 'xmlrpcvals';
26
27
    /**
28
     * Option used for fine-tuning the encoding the php values returned from
29
     * functions registered in the dispatch map when the functions_parameters_types
30
     * member is set to 'phpvals'
31
     * @see Encoder::encode for a list of values
32
     */
33
    public $phpvals_encoding_options = array('auto_dates');
34
35
    /**
36
     * Controls whether the server is going to echo debugging messages back to the client as comments in response body.
37
     * Valid values: 0,1,2,3
38
     */
39
    public $debug = 1;
40
41
    /**
42
     * Controls behaviour of server when the invoked user function throws an exception:
43
     * 0 = catch it and return an 'internal error' xmlrpc response (default)
44
     * 1 = catch it and return an xmlrpc response with the error corresponding to the exception
45
     * 2 = allow the exception to float to the upper layers
46
     */
47
    public $exception_handling = 0;
48
49
    /**
50
     * When set to true, it will enable HTTP compression of the response, in case
51
     * the client has declared its support for compression in the request.
52
     * Set at constructor time.
53
     */
54
    public $compress_response = false;
55
56
    /**
57
     * List of http compression methods accepted by the server for requests. Set at constructor time.
58
     * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
59
     */
60
    public $accepted_compression = array();
61
62
    /// Shall we serve calls to system.* methods?
63
    public $allow_system_funcs = true;
64
65
    /**
66
     * List of charset encodings natively accepted for requests.
67
     * Set at constructor time.
68
     * UNUSED so far...
69
     */
70
    public $accepted_charset_encodings = array();
71
72
    /**
73
     * Charset encoding to be used for response.
74
     * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
75
     * Can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
76
     * null (leave unspecified in response, convert output stream to US_ASCII),
77
     * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
78
     * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
79
     * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
80
     */
81
    public $response_charset_encoding = '';
82
83
    /**
84
     * Storage for internal debug info.
85
     */
86
    protected $debug_info = '';
87
88
    /**
89
     * Extra data passed at runtime to method handling functions. Used only by EPI layer
90
     */
91
    public $user_data = null;
92
93
    protected static $_xmlrpc_debuginfo = '';
94
    protected static $_xmlrpcs_occurred_errors = '';
95
    protected static $_xmlrpcs_prev_ehandler = '';
96
97
    /**
98
     * @param array $dispatchMap the dispatch map with definition of exposed services
99
     * @param boolean $serviceNow set to false to prevent the server from running upon construction
100
     */
101 491
    public function __construct($dispatchMap = null, $serviceNow = true)
102
    {
103
        // if ZLIB is enabled, let the server by default accept compressed requests,
104
        // and compress responses sent to clients that support them
105 491
        if (function_exists('gzinflate')) {
106 491
            $this->accepted_compression = array('gzip', 'deflate');
107 491
            $this->compress_response = true;
108
        }
109
110
        // by default the xml parser can support these 3 charset encodings
111 491
        $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
112
113
        // dispMap is a dispatch array of methods mapped to function names and signatures.
114
        // If a method doesn't appear in the map then an unknown method error is generated
115
        /* milosch - changed to make passing dispMap optional.
116
        * instead, you can use the class add_to_map() function
117
        * to add functions manually (borrowed from SOAPX4)
118
        */
119 491
        if ($dispatchMap) {
120 490
            $this->dmap = $dispatchMap;
121 490
            if ($serviceNow) {
122 2
                $this->service();
123
            }
124
        }
125 491
    }
126
127
    /**
128
     * Set debug level of server.
129
     *
130
     * @param integer $level debug lvl: determines info added to xmlrpc responses (as xml comments)
131
     *                    0 = no debug info,
132
     *                    1 = msgs set from user with debugmsg(),
133
     *                    2 = add complete xmlrpc request (headers and body),
134
     *                    3 = add also all processing warnings happened during method processing
135
     *                    (NB: this involves setting a custom error handler, and might interfere
136
     *                    with the standard processing of the php function exposed as method. In
137
     *                    particular, triggering an USER_ERROR level error will not halt script
138
     *                    execution anymore, but just end up logged in the xmlrpc response)
139
     *                    Note that info added at level 2 and 3 will be base64 encoded
140
     */
141 488
    public function setDebug($level)
142
    {
143 488
        $this->debug = $level;
144 488
    }
145
146
    /**
147
     * Add a string to the debug info that can be later serialized by the server as part of the response message.
148
     * Note that for best compatibility, the debug string should be encoded using the PhpXmlRpc::$xmlrpc_internalencoding
149
     * character set.
150
     *
151
     * @param string $msg
152
     * @access public
153
     */
154 2
    public static function xmlrpc_debugmsg($msg)
155
    {
156 2
        static::$_xmlrpc_debuginfo .= $msg . "\n";
157 2
    }
158
159
    /**
160
     * @param string $msg
161
     */
162 20
    public static function error_occurred($msg)
163
    {
164 20
        static::$_xmlrpcs_occurred_errors .= $msg . "\n";
165 20
    }
166
167
    /**
168
     * Return a string with the serialized representation of all debug info.
169
     *
170
     * @param string $charsetEncoding the target charset encoding for the serialization
171
     *
172
     * @return string an XML comment (or two)
173
     */
174 490
    public function serializeDebug($charsetEncoding = '')
175
    {
176
        // Tough encoding problem: which internal charset should we assume for debug info?
177
        // It might contain a copy of raw data received from client, ie with unknown encoding,
178
        // intermixed with php generated data and user generated data...
179
        // so we split it: system debug is base 64 encoded,
180
        // user debug info should be encoded by the end user using the INTERNAL_ENCODING
181 490
        $out = '';
182 490
        if ($this->debug_info != '') {
183 488
            $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n" . base64_encode($this->debug_info) . "\n-->\n";
184
        }
185 490
        if (static::$_xmlrpc_debuginfo != '') {
186 2
            $out .= "<!-- DEBUG INFO:\n" . Charset::instance()->encodeEntities(str_replace('--', '_-', static::$_xmlrpc_debuginfo), PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "\n-->\n";
187
            // NB: a better solution MIGHT be to use CDATA, but we need to insert it
188
            // into return payload AFTER the beginning tag
189
            //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', static::$_xmlrpc_debuginfo) . "\n]]>\n";
190
        }
191
192 490
        return $out;
193
    }
194
195
    /**
196
     * Execute the xmlrpc request, printing the response.
197
     *
198
     * @param string $data the request body. If null, the http POST request will be examined
199
     * @param bool $returnPayload When true, return the response but do not echo it or any http header
200
     *
201
     * @return Response|string the response object (usually not used by caller...) or its xml serialization
202
     *
203
     * @throws \Exception in case the executed method does throw an exception (and depending on server configuration)
204
     */
205 490
    public function service($data = null, $returnPayload = false)
206
    {
207 490
        if ($data === null) {
208 490
            $data = file_get_contents('php://input');
209
        }
210 490
        $rawData = $data;
211
212
        // reset internal debug info
213 490
        $this->debug_info = '';
214
215
        // Save what we received, before parsing it
216 490
        if ($this->debug > 1) {
217 488
            $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
218
        }
219
220 490
        $r = $this->parseRequestHeaders($data, $reqCharset, $respCharset, $respEncoding);
221 490
        if (!$r) {
222
            // this actually executes the request
223 490
            $r = $this->parseRequest($data, $reqCharset);
224
        }
225
226
        // save full body of request into response, for more debugging usages
227 490
        $r->raw_data = $rawData;
228
229 490
        if ($this->debug > 2 && static::$_xmlrpcs_occurred_errors) {
230 20
            $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
231 20
                static::$_xmlrpcs_occurred_errors . "+++END+++");
232
        }
233
234 490
        $payload = $this->xml_header($respCharset);
235 490
        if ($this->debug > 0) {
236 490
            $payload = $payload . $this->serializeDebug($respCharset);
237
        }
238
239
        // Do not create response serialization if it has already happened. Helps building json magic
240 490
        if (empty($r->payload)) {
241 490
            $r->serialize($respCharset);
242
        }
243 490
        $payload = $payload . $r->payload;
244
245 490
        if ($returnPayload) {
246
            return $payload;
247
        }
248
249
        // if we get a warning/error that has output some text before here, then we cannot
250
        // add a new header. We cannot say we are sending xml, either...
251 490
        if (!headers_sent()) {
252 490
            header('Content-Type: ' . $r->content_type);
253
            // we do not know if client actually told us an accepted charset, but if he did
254
            // we have to tell him what we did
255 490
            header("Vary: Accept-Charset");
256
257
            // http compression of output: only
258
            // if we can do it, and we want to do it, and client asked us to,
259
            // and php ini settings do not force it already
260 490
            $phpNoSelfCompress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
261 490
            if ($this->compress_response && function_exists('gzencode') && $respEncoding != ''
262 100
                && $phpNoSelfCompress
263
            ) {
264 100
                if (strpos($respEncoding, 'gzip') !== false) {
265 50
                    $payload = gzencode($payload);
266 50
                    header("Content-Encoding: gzip");
267 50
                    header("Vary: Accept-Encoding");
268 50
                } elseif (strpos($respEncoding, 'deflate') !== false) {
269 50
                    $payload = gzcompress($payload);
270 50
                    header("Content-Encoding: deflate");
271 50
                    header("Vary: Accept-Encoding");
272
                }
273
            }
274
275
            // Do not output content-length header if php is compressing output for us:
276
            // it will mess up measurements.
277
            // Note that Apache/mod_php will add (and even alter!) the Content-Length header on its own, but only for
278
            // responses up to 8000 bytes
279 490
            if ($phpNoSelfCompress) {
280 490
                header('Content-Length: ' . (int)strlen($payload));
281
            }
282
        } else {
283
            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': http headers already sent before response is fully generated. Check for php warning or error messages');
284
        }
285
286 490
        print $payload;
287
288
        // return request, in case subclasses want it
289 490
        return $r;
290
    }
291
292
    /**
293
     * Add a method to the dispatch map.
294
     *
295
     * @param string $methodName the name with which the method will be made available
296
     * @param string $function the php function that will get invoked
297
     * @param array $sig the array of valid method signatures
298
     * @param string $doc method documentation
299
     * @param array $sigDoc the array of valid method signatures docs (one string per param, one for return type)
300
     *
301
     * @todo raise a warning if the user tries to register a 'system.' method
302
     */
303
    public function add_to_map($methodName, $function, $sig = null, $doc = false, $sigDoc = false)
304
    {
305
        $this->dmap[$methodName] = array(
306
            'function' => $function,
307
            'docstring' => $doc,
308
        );
309
        if ($sig) {
310
            $this->dmap[$methodName]['signature'] = $sig;
311
        }
312
        if ($sigDoc) {
313
            $this->dmap[$methodName]['signature_docs'] = $sigDoc;
314
        }
315
    }
316
317
    /**
318
     * Verify type and number of parameters received against a list of known signatures.
319
     *
320
     * @param array|Request $in array of either xmlrpc value objects or xmlrpc type definitions
321
     * @param array $sigs array of known signatures to match against
322
     *
323
     * @return array int, string
324
     */
325 467
    protected function verifySignature($in, $sigs)
326
    {
327
        // check each possible signature in turn
328 467
        if (is_object($in)) {
329 467
            $numParams = $in->getNumParams();
330
        } else {
331
            $numParams = count($in);
332
        }
333 467
        foreach ($sigs as $curSig) {
334 467
            if (count($curSig) == $numParams + 1) {
335 467
                $itsOK = 1;
336 467
                for ($n = 0; $n < $numParams; $n++) {
337 448
                    if (is_object($in)) {
338 448
                        $p = $in->getParam($n);
339 448
                        if ($p->kindOf() == 'scalar') {
340 391
                            $pt = $p->scalartyp();
341
                        } else {
342 134
                            $pt = $p->kindOf();
343
                        }
344
                    } else {
345
                        $pt = ($in[$n] == 'i4') ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
346
                    }
347
348
                    // param index is $n+1, as first member of sig is return type
349 448
                    if ($pt != $curSig[$n + 1] && $curSig[$n + 1] != Value::$xmlrpcValue) {
350 20
                        $itsOK = 0;
351 20
                        $pno = $n + 1;
352 20
                        $wanted = $curSig[$n + 1];
353 20
                        $got = $pt;
354 20
                        break;
355
                    }
356
                }
357 467
                if ($itsOK) {
358 467
                    return array(1, '');
359
                }
360
            }
361
        }
362 20
        if (isset($wanted)) {
363
            return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $got does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $pno does not seem to be defined for all execution paths leading up to this point.
Loading history...
364
        } else {
365 20
            return array(0, "No method signature matches number of parameters");
366
        }
367
    }
368
369
    /**
370
     * Parse http headers received along with xmlrpc request. If needed, inflate request.
371
     *
372
     * @return mixed Response|null on success or an error Response
373
     */
374 490
    protected function parseRequestHeaders(&$data, &$reqEncoding, &$respEncoding, &$respCompression)
375
    {
376
        // check if $_SERVER is populated: it might have been disabled via ini file
377
        // (this is true even when in CLI mode)
378 490
        if (count($_SERVER) == 0) {
379
            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': cannot parse request headers as $_SERVER is not populated');
380
        }
381
382 490
        if ($this->debug > 1) {
383 488
            if (function_exists('getallheaders')) {
384 488
                $this->debugmsg(''); // empty line
385 488
                foreach (getallheaders() as $name => $val) {
386 488
                    $this->debugmsg("HEADER: $name: $val");
387
                }
388
            }
389
        }
390
391 490
        if (isset($_SERVER['HTTP_CONTENT_ENCODING'])) {
392 100
            $contentEncoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
393
        } else {
394 390
            $contentEncoding = '';
395
        }
396
397
        // check if request body has been compressed and decompress it
398 490
        if ($contentEncoding != '' && strlen($data)) {
399 100
            if ($contentEncoding == 'deflate' || $contentEncoding == 'gzip') {
400
                // if decoding works, use it. else assume data wasn't gzencoded
401 100
                if (function_exists('gzinflate') && in_array($contentEncoding, $this->accepted_compression)) {
402 100
                    if ($contentEncoding == 'deflate' && $degzdata = @gzuncompress($data)) {
403 50
                        $data = $degzdata;
404 50
                        if ($this->debug > 1) {
405 50
                            $this->debugmsg("\n+++INFLATED REQUEST+++[" . strlen($data) . " chars]+++\n" . $data . "\n+++END+++");
406
                        }
407 50
                    } elseif ($contentEncoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10))) {
408 50
                        $data = $degzdata;
409 50
                        if ($this->debug > 1) {
410 50
                            $this->debugmsg("+++INFLATED REQUEST+++[" . strlen($data) . " chars]+++\n" . $data . "\n+++END+++");
411
                        }
412
                    } else {
413
                        $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_decompress_fail'], PhpXmlRpc::$xmlrpcstr['server_decompress_fail']);
414
415
                        return $r;
416
                    }
417
                } else {
418
                    $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_cannot_decompress'], PhpXmlRpc::$xmlrpcstr['server_cannot_decompress']);
419
420
                    return $r;
421
                }
422
            }
423
        }
424
425
        // check if client specified accepted charsets, and if we know how to fulfill
426
        // the request
427 490
        if ($this->response_charset_encoding == 'auto') {
428
            $respEncoding = '';
429
            if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
430
                // here we should check if we can match the client-requested encoding
431
                // with the encodings we know we can generate.
432
                /// @todo we should parse q=0.x preferences instead of getting first charset specified...
433
                $clientAcceptedCharsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
434
                // Give preference to internal encoding
435
                $knownCharsets = array(PhpXmlRpc::$xmlrpc_internalencoding, 'UTF-8', 'ISO-8859-1', 'US-ASCII');
436
                foreach ($knownCharsets as $charset) {
437
                    foreach ($clientAcceptedCharsets as $accepted) {
438
                        if (strpos($accepted, $charset) === 0) {
439
                            $respEncoding = $charset;
440
                            break;
441
                        }
442
                    }
443
                    if ($respEncoding) {
444
                        break;
445
                    }
446
                }
447
            }
448
        } else {
449 490
            $respEncoding = $this->response_charset_encoding;
450
        }
451
452 490
        if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
453 100
            $respCompression = $_SERVER['HTTP_ACCEPT_ENCODING'];
454
        } else {
455 390
            $respCompression = '';
456
        }
457
458
        // 'guestimate' request encoding
459
        /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
460 490
        $reqEncoding = XMLParser::guessEncoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
461
            $data);
462
463 490
        return null;
464
    }
465
466
    /**
467
     * Parse an xml chunk containing an xmlrpc request and execute the corresponding
468
     * php function registered with the server.
469
     *
470
     * @param string $data the xml request
471
     * @param string $reqEncoding (optional) the charset encoding of the xml request
472
     *
473
     * @return Response
474
     *
475
     * @throws \Exception in case the executed method does throw an exception (and depending on server configuration)
476
     */
477 491
    public function parseRequest($data, $reqEncoding = '')
478
    {
479
        // decompose incoming XML into request structure
480
481 491
        if ($reqEncoding != '') {
482
            // Since parsing will fail if
483
            // - charset is not specified in the xml prologue,
484
            // - the encoding is not UTF8 and
485
            // - there are non-ascii chars in the text,
486
            // we try to work round that...
487
            // The following code might be better for mb_string enabled installs, but
488
            // makes the lib about 200% slower...
489
            //if (!is_valid_charset($reqEncoding, array('UTF-8')))
490 490
            if (!in_array($reqEncoding, array('UTF-8', 'US-ASCII')) && !XMLParser::hasEncoding($data)) {
491 4
                if ($reqEncoding == 'ISO-8859-1') {
492 2
                    $data = utf8_encode($data);
493
                } else {
494 2
                    if (extension_loaded('mbstring')) {
495 2
                        $data = mb_convert_encoding($data, 'UTF-8', $reqEncoding);
496
                    } else {
497
                        Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $reqEncoding);
498
                    }
499
                }
500
            }
501
        }
502
503
        // PHP internally might use ISO-8859-1, so we have to tell the xml parser to give us back data in the expected charset.
504
        // What if internal encoding is not in one of the 3 allowed? We use the broadest one, ie. utf8
505
        // This allows to send data which is native in various charset,
506
        // by extending xmlrpc_encode_entities() and setting xmlrpc_internalencoding
507 491
        if (!in_array(PhpXmlRpc::$xmlrpc_internalencoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) {
508
            $options = array(XML_OPTION_TARGET_ENCODING => 'UTF-8');
509
        } else {
510 491
            $options = array(XML_OPTION_TARGET_ENCODING => PhpXmlRpc::$xmlrpc_internalencoding);
511
        }
512
513 491
        $xmlRpcParser = new XMLParser($options);
514 491
        $xmlRpcParser->parse($data, $this->functions_parameters_type, XMLParser::ACCEPT_REQUEST);
515 491
        if ($xmlRpcParser->_xh['isf'] > 2) {
516
            // (BC) we return XML error as a faultCode
517 2
            preg_match('/^XML error ([0-9]+)/', $xmlRpcParser->_xh['isf_reason'], $matches);
518 2
            $r = new Response(0,
519 2
                PhpXmlRpc::$xmlrpcerrxml + $matches[1],
520 2
                $xmlRpcParser->_xh['isf_reason']);
521 489
        } elseif ($xmlRpcParser->_xh['isf']) {
522 1
            $r = new Response(0,
523 1
                PhpXmlRpc::$xmlrpcerr['invalid_request'],
524 1
                PhpXmlRpc::$xmlrpcstr['invalid_request'] . ' ' . $xmlRpcParser->_xh['isf_reason']);
525
        } else {
526
            // small layering violation in favor of speed and memory usage:
527
            // we should allow the 'execute' method handle this, but in the
528
            // most common scenario (xmlrpc values type server with some methods
529
            // registered as phpvals) that would mean a useless encode+decode pass
530 488
            if ($this->functions_parameters_type != 'xmlrpcvals' ||
531 488
                (isset($this->dmap[$xmlRpcParser->_xh['method']]['parameters_type']) &&
532
                    ($this->dmap[$xmlRpcParser->_xh['method']]['parameters_type'] == 'phpvals')
533
                )
534
            ) {
535
                if ($this->debug > 1) {
536
                    $this->debugmsg("\n+++PARSED+++\n" . var_export($xmlRpcParser->_xh['params'], true) . "\n+++END+++");
537
                }
538
                $r = $this->execute($xmlRpcParser->_xh['method'], $xmlRpcParser->_xh['params'], $xmlRpcParser->_xh['pt']);
539
            } else {
540
                // build a Request object with data parsed from xml
541 488
                $req = new Request($xmlRpcParser->_xh['method']);
0 ignored issues
show
Bug introduced by
$xmlRpcParser->_xh['method'] of type false is incompatible with the type string expected by parameter $methodName of PhpXmlRpc\Request::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

541
                $req = new Request(/** @scrutinizer ignore-type */ $xmlRpcParser->_xh['method']);
Loading history...
542
                // now add parameters in
543 488
                for ($i = 0; $i < count($xmlRpcParser->_xh['params']); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
544 469
                    $req->addParam($xmlRpcParser->_xh['params'][$i]);
545
                }
546
547 488
                if ($this->debug > 1) {
548 488
                    $this->debugmsg("\n+++PARSED+++\n" . var_export($req, true) . "\n+++END+++");
549
                }
550 488
                $r = $this->execute($req);
551
            }
552
        }
553
554 491
        return $r;
555
    }
556
557
    /**
558
     * Execute a method invoked by the client, checking parameters used.
559
     *
560
     * @param mixed $req either a Request obj or a method name
561
     * @param array $params array with method parameters as php types (if m is method name only)
562
     * @param array $paramTypes array with xmlrpc types of method parameters (if m is method name only)
563
     *
564
     * @return Response
565
     *
566
     * @throws \Exception in case the executed method does throw an exception (and depending on server configuration)
567
     */
568 488
    protected function execute($req, $params = null, $paramTypes = null)
569
    {
570 488
        static::$_xmlrpcs_occurred_errors = '';
571 488
        static::$_xmlrpc_debuginfo = '';
572
573 488
        if (is_object($req)) {
574 488
            $methName = $req->method();
575
        } else {
576
            $methName = $req;
577
        }
578 488
        $sysCall = $this->isSyscall($methName);
579 488
        $dmap = $sysCall ? $this->getSystemDispatchMap() : $this->dmap;
580
581 488
        if (!isset($dmap[$methName]['function'])) {
582
            // No such method
583 77
            return new Response(0,
584 77
                PhpXmlRpc::$xmlrpcerr['unknown_method'],
585 77
                PhpXmlRpc::$xmlrpcstr['unknown_method']);
586
        }
587
588
        // Check signature
589 488
        if (isset($dmap[$methName]['signature'])) {
590 467
            $sig = $dmap[$methName]['signature'];
591 467
            if (is_object($req)) {
592 467
                list($ok, $errStr) = $this->verifySignature($req, $sig);
593
            } else {
594
                list($ok, $errStr) = $this->verifySignature($paramTypes, $sig);
595
            }
596 467
            if (!$ok) {
597
                // Didn't match.
598 20
                return new Response(
599 20
                    0,
600 20
                    PhpXmlRpc::$xmlrpcerr['incorrect_params'],
601 20
                    PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": ${errStr}"
602
                );
603
            }
604
        }
605
606 488
        $func = $dmap[$methName]['function'];
607
        // let the 'class::function' syntax be accepted in dispatch maps
608 488
        if (is_string($func) && strpos($func, '::')) {
609 115
            $func = explode('::', $func);
610
        }
611
612 488
        if (is_array($func)) {
613 136
            if (is_object($func[0])) {
614 22
                $funcName = get_class($func[0]) . '->' . $func[1];
615
            } else {
616 115
                $funcName = implode('::', $func);
617
            }
618 372
        } else if ($func instanceof \Closure) {
619 98
            $funcName = 'Closure';
620
        } else {
621 294
            $funcName = $func;
622
        }
623
624
        // verify that function to be invoked is in fact callable
625 488
        if (!is_callable($func)) {
626
            Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler is not callable");
627
            return new Response(
628
                0,
629
                PhpXmlRpc::$xmlrpcerr['server_error'],
630
                PhpXmlRpc::$xmlrpcstr['server_error'] . ": no function matches method"
631
            );
632
        }
633
634
        // If debug level is 3, we should catch all errors generated during
635
        // processing of user function, and log them as part of response
636 488
        if ($this->debug > 2) {
637 488
            self::$_xmlrpcs_prev_ehandler = set_error_handler(array('\PhpXmlRpc\Server', '_xmlrpcs_errorHandler'));
638
        }
639
640
        try {
641
            // Allow mixed-convention servers
642 488
            if (is_object($req)) {
643 488
                if ($sysCall) {
644 115
                    $r = call_user_func($func, $this, $req);
645
                } else {
646 393
                    $r = call_user_func($func, $req);
647
                }
648 486
                if (!is_a($r, 'PhpXmlRpc\Response')) {
649
                    Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler does not return an xmlrpc response object but a " . gettype($r));
650
                    if (is_a($r, 'PhpXmlRpc\Value')) {
651
                        $r = new Response($r);
652
                    } else {
653
                        $r = new Response(
654
                            0,
655
                            PhpXmlRpc::$xmlrpcerr['server_error'],
656
                            PhpXmlRpc::$xmlrpcstr['server_error'] . ": function does not return xmlrpc response object"
657
                        );
658
                    }
659
                }
660
            } else {
661
                // call a 'plain php' function
662
                if ($sysCall) {
663
                    array_unshift($params, $this);
0 ignored issues
show
Bug introduced by
It seems like $params can also be of type null; however, parameter $array of array_unshift() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

663
                    array_unshift(/** @scrutinizer ignore-type */ $params, $this);
Loading history...
664
                    $r = call_user_func_array($func, $params);
665
                } else {
666
                    // 3rd API convention for method-handling functions: EPI-style
667
                    if ($this->functions_parameters_type == 'epivals') {
668
                        $r = call_user_func_array($func, array($methName, $params, $this->user_data));
669
                        // mimic EPI behaviour: if we get an array that looks like an error, make it
670
                        // an error response
671
                        if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r)) {
672
                            $r = new Response(0, (integer)$r['faultCode'], (string)$r['faultString']);
673
                        } else {
674
                            // functions using EPI api should NOT return resp objects,
675
                            // so make sure we encode the return type correctly
676
                            $encoder = new Encoder();
677
                            $r = new Response($encoder->encode($r, array('extension_api')));
678
                        }
679
                    } else {
680
                        $r = call_user_func_array($func, $params);
0 ignored issues
show
Bug introduced by
It seems like $params can also be of type null; however, parameter $param_arr of call_user_func_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

680
                        $r = call_user_func_array($func, /** @scrutinizer ignore-type */ $params);
Loading history...
681
                    }
682
                }
683
                // the return type can be either a Response object or a plain php value...
684
                if (!is_a($r, '\PhpXmlRpc\Response')) {
685
                    // what should we assume here about automatic encoding of datetimes
686
                    // and php classes instances???
687
                    $encoder = new Encoder();
688
                    $r = new Response($encoder->encode($r, $this->phpvals_encoding_options));
689
                }
690
            }
691 41
        } catch (\Exception $e) {
692
            // (barring errors in the lib) an uncatched exception happened
693
            // in the called function, we wrap it in a proper error-response
694 41
            switch ($this->exception_handling) {
695 41
                case 2:
696
                    if ($this->debug > 2) {
697
                        if (self::$_xmlrpcs_prev_ehandler) {
698
                            set_error_handler(self::$_xmlrpcs_prev_ehandler);
699
                        } else {
700
                            restore_error_handler();
701
                        }
702
                    }
703
                    throw $e;
704 41
                case 1:
705 2
                    $r = new Response(0, $e->getCode(), $e->getMessage());
706 2
                    break;
707
                default:
708 41
                    $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_error'], PhpXmlRpc::$xmlrpcstr['server_error']);
709
            }
710
        }
711 488
        if ($this->debug > 2) {
712
            // note: restore the error handler we found before calling the
713
            // user func, even if it has been changed inside the func itself
714 488
            if (self::$_xmlrpcs_prev_ehandler) {
715 58
                set_error_handler(self::$_xmlrpcs_prev_ehandler);
716
            } else {
717 431
                restore_error_handler();
718
            }
719
        }
720
721 488
        return $r;
722
    }
723
724
    /**
725
     * Add a string to the 'internal debug message' (separate from 'user debug message').
726
     *
727
     * @param string $string
728
     */
729 488
    protected function debugmsg($string)
730
    {
731 488
        $this->debug_info .= $string . "\n";
732 488
    }
733
734
    /**
735
     * @param string $charsetEncoding
736
     * @return string
737
     */
738 490
    protected function xml_header($charsetEncoding = '')
739
    {
740 490
        if ($charsetEncoding != '') {
741 50
            return "<?xml version=\"1.0\" encoding=\"$charsetEncoding\"?" . ">\n";
742
        } else {
743 440
            return "<?xml version=\"1.0\"?" . ">\n";
744
        }
745
    }
746
747
    /**
748
     * @param string $methName
749
     * @return bool
750
     */
751 488
    protected function isSyscall($methName)
752
    {
753 488
        return (strpos($methName, "system.") === 0);
754
    }
755
756
    /* Functions that implement system.XXX methods of xmlrpc servers */
757
758
    /**
759
     * @return array[]
760
     */
761 115
    public function getSystemDispatchMap()
762
    {
763 115
        if (!$this->allow_system_funcs) {
764
            return array();
765
        }
766
767
        return array(
768
            'system.listMethods' => array(
769 115
                'function' => 'PhpXmlRpc\Server::_xmlrpcs_listMethods',
770
                // listMethods: signature was either a string, or nothing.
771
                // The useless string variant has been removed
772 115
                'signature' => array(array(Value::$xmlrpcArray)),
773 115
                'docstring' => 'This method lists all the methods that the XML-RPC server knows how to dispatch',
774
                'signature_docs' => array(array('list of method names')),
775
            ),
776
            'system.methodHelp' => array(
777 115
                'function' => 'PhpXmlRpc\Server::_xmlrpcs_methodHelp',
778 115
                'signature' => array(array(Value::$xmlrpcString, Value::$xmlrpcString)),
779 115
                'docstring' => 'Returns help text if defined for the method passed, otherwise returns an empty string',
780
                'signature_docs' => array(array('method description', 'name of the method to be described')),
781
            ),
782
            'system.methodSignature' => array(
783 115
                'function' => 'PhpXmlRpc\Server::_xmlrpcs_methodSignature',
784 115
                'signature' => array(array(Value::$xmlrpcArray, Value::$xmlrpcString)),
785 115
                'docstring' => 'Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)',
786
                'signature_docs' => array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described')),
787
            ),
788
            'system.multicall' => array(
789 115
                'function' => 'PhpXmlRpc\Server::_xmlrpcs_multicall',
790 115
                'signature' => array(array(Value::$xmlrpcArray, Value::$xmlrpcArray)),
791 115
                'docstring' => 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details',
792
                'signature_docs' => array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"')),
793
            ),
794
            'system.getCapabilities' => array(
795 115
                'function' => 'PhpXmlRpc\Server::_xmlrpcs_getCapabilities',
796 115
                'signature' => array(array(Value::$xmlrpcStruct)),
797 115
                'docstring' => 'This method lists all the capabilities that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to',
798
                'signature_docs' => array(array('list of capabilities, described as structs with a version number and url for the spec')),
799
            ),
800
        );
801
    }
802
803
    /**
804
     * @return array[]
805
     */
806
    public function getCapabilities()
807
    {
808
        $outAr = array(
809
            // xmlrpc spec: always supported
810
            'xmlrpc' => array(
811
                'specUrl' => 'http://www.xmlrpc.com/spec',
812
                'specVersion' => 1
813
            ),
814
            // if we support system.xxx functions, we always support multicall, too...
815
            // Note that, as of 2006/09/17, the following URL does not respond anymore
816
            'system.multicall' => array(
817
                'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
818
                'specVersion' => 1
819
            ),
820
            // introspection: version 2! we support 'mixed', too
821
            'introspection' => array(
822
                'specUrl' => 'http://phpxmlrpc.sourceforge.net/doc-2/ch10.html',
823
                'specVersion' => 2,
824
            ),
825
        );
826
827
        // NIL extension
828
        if (PhpXmlRpc::$xmlrpc_null_extension) {
829
            $outAr['nil'] = array(
830
                'specUrl' => 'http://www.ontosys.com/xml-rpc/extensions.php',
831
                'specVersion' => 1
832
            );
833
        }
834
835
        return $outAr;
836
    }
837
838
    /**
839
     * @param Server $server
840
     * @param Request $req
841
     * @return Response
842
     */
843
    public static function _xmlrpcs_getCapabilities($server, $req = null)
0 ignored issues
show
Unused Code introduced by
The parameter $req is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

843
    public static function _xmlrpcs_getCapabilities($server, /** @scrutinizer ignore-unused */ $req = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
844
    {
845
        $encoder = new Encoder();
846
        return new Response($encoder->encode($server->getCapabilities()));
847
    }
848
849
    /**
850
     * @param Server $server
851
     * @param Request $req if called in plain php values mode, second param is missing
852
     * @return Response
853
     */
854 20
    public static function _xmlrpcs_listMethods($server, $req = null)
0 ignored issues
show
Unused Code introduced by
The parameter $req is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

854
    public static function _xmlrpcs_listMethods($server, /** @scrutinizer ignore-unused */ $req = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
855
    {
856 20
        $outAr = array();
857 20
        foreach ($server->dmap as $key => $val) {
858 20
            $outAr[] = new Value($key, 'string');
859
        }
860 20
        foreach ($server->getSystemDispatchMap() as $key => $val) {
861 20
            $outAr[] = new Value($key, 'string');
862
        }
863
864 20
        return new Response(new Value($outAr, 'array'));
865
    }
866
867
    /**
868
     * @param Server $server
869
     * @param Request $req
870
     * @return Response
871
     */
872 96
    public static function _xmlrpcs_methodSignature($server, $req)
873
    {
874
        // let accept as parameter both an xmlrpc value or string
875 96
        if (is_object($req)) {
876 96
            $methName = $req->getParam(0);
877 96
            $methName = $methName->scalarval();
878
        } else {
879
            $methName = $req;
880
        }
881 96
        if ($server->isSyscall($methName)) {
882 77
            $dmap = $server->getSystemDispatchMap();
883
        } else {
884 20
            $dmap = $server->dmap;
885
        }
886 96
        if (isset($dmap[$methName])) {
887 96
            if (isset($dmap[$methName]['signature'])) {
888 96
                $sigs = array();
889 96
                foreach ($dmap[$methName]['signature'] as $inSig) {
890 96
                    $curSig = array();
891 96
                    foreach ($inSig as $sig) {
892 96
                        $curSig[] = new Value($sig, 'string');
893
                    }
894 96
                    $sigs[] = new Value($curSig, 'array');
895
                }
896 96
                $r = new Response(new Value($sigs, 'array'));
897
            } else {
898
                // NB: according to the official docs, we should be returning a
899
                // "none-array" here, which means not-an-array
900
                $r = new Response(new Value('undef', 'string'));
901
            }
902
        } else {
903
            $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
904
        }
905
906 96
        return $r;
907
    }
908
909
    /**
910
     * @param Server $server
911
     * @param Request $req
912
     * @return Response
913
     */
914 77
    public static function _xmlrpcs_methodHelp($server, $req)
915
    {
916
        // let accept as parameter both an xmlrpc value or string
917 77
        if (is_object($req)) {
918 77
            $methName = $req->getParam(0);
919 77
            $methName = $methName->scalarval();
920
        } else {
921
            $methName = $req;
922
        }
923 77
        if ($server->isSyscall($methName)) {
924 77
            $dmap = $server->getSystemDispatchMap();
925
        } else {
926 1
            $dmap = $server->dmap;
927
        }
928 77
        if (isset($dmap[$methName])) {
929 77
            if (isset($dmap[$methName]['docstring'])) {
930 77
                $r = new Response(new Value($dmap[$methName]['docstring'], 'string'));
931
            } else {
932
                $r = new Response(new Value('', 'string'));
933
            }
934
        } else {
935
            $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
936
        }
937
938 77
        return $r;
939
    }
940
941 58
    public static function _xmlrpcs_multicall_error($err)
942
    {
943 58
        if (is_string($err)) {
944 58
            $str = PhpXmlRpc::$xmlrpcstr["multicall_${err}"];
945 58
            $code = PhpXmlRpc::$xmlrpcerr["multicall_${err}"];
946
        } else {
947 58
            $code = $err->faultCode();
948 58
            $str = $err->faultString();
949
        }
950 58
        $struct = array();
951 58
        $struct['faultCode'] = new Value($code, 'int');
952 58
        $struct['faultString'] = new Value($str, 'string');
953
954 58
        return new Value($struct, 'struct');
955
    }
956
957
    /**
958
     * @param Server $server
959
     * @param Value $call
960
     * @return Value
961
     */
962 58
    public static function _xmlrpcs_multicall_do_call($server, $call)
963
    {
964 58
        if ($call->kindOf() != 'struct') {
965
            return static::_xmlrpcs_multicall_error('notstruct');
966
        }
967 58
        $methName = @$call['methodName'];
968 58
        if (!$methName) {
969
            return static::_xmlrpcs_multicall_error('nomethod');
970
        }
971 58
        if ($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string') {
972
            return static::_xmlrpcs_multicall_error('notstring');
973
        }
974 58
        if ($methName->scalarval() == 'system.multicall') {
975 58
            return static::_xmlrpcs_multicall_error('recursion');
976
        }
977
978 58
        $params = @$call['params'];
979 58
        if (!$params) {
980
            return static::_xmlrpcs_multicall_error('noparams');
981
        }
982 58
        if ($params->kindOf() != 'array') {
983
            return static::_xmlrpcs_multicall_error('notarray');
984
        }
985
986 58
        $req = new Request($methName->scalarval());
987 58
        foreach($params as $i => $param) {
988 58
            if (!$req->addParam($param)) {
989
                $i++; // for error message, we count params from 1
990
                return static::_xmlrpcs_multicall_error(new Response(0,
991
                    PhpXmlRpc::$xmlrpcerr['incorrect_params'],
992
                    PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": probable xml error in param " . $i));
993
            }
994
        }
995
996 58
        $result = $server->execute($req);
997
998 58
        if ($result->faultCode() != 0) {
999 58
            return static::_xmlrpcs_multicall_error($result); // Method returned fault.
1000
        }
1001
1002 58
        return new Value(array($result->value()), 'array');
1003
    }
1004
1005
    /**
1006
     * @param Server $server
1007
     * @param Value $call
1008
     * @return Value
1009
     */
1010
    public static function _xmlrpcs_multicall_do_call_phpvals($server, $call)
1011
    {
1012
        if (!is_array($call)) {
0 ignored issues
show
introduced by
The condition is_array($call) is always false.
Loading history...
1013
            return static::_xmlrpcs_multicall_error('notstruct');
1014
        }
1015
        if (!array_key_exists('methodName', $call)) {
1016
            return static::_xmlrpcs_multicall_error('nomethod');
1017
        }
1018
        if (!is_string($call['methodName'])) {
1019
            return static::_xmlrpcs_multicall_error('notstring');
1020
        }
1021
        if ($call['methodName'] == 'system.multicall') {
1022
            return static::_xmlrpcs_multicall_error('recursion');
1023
        }
1024
        if (!array_key_exists('params', $call)) {
1025
            return static::_xmlrpcs_multicall_error('noparams');
1026
        }
1027
        if (!is_array($call['params'])) {
1028
            return static::_xmlrpcs_multicall_error('notarray');
1029
        }
1030
1031
        // this is a simplistic hack, since we might have received
1032
        // base64 or datetime values, but they will be listed as strings here...
1033
        $pt = array();
1034
        $wrapper = new Wrapper();
1035
        foreach ($call['params'] as $val) {
1036
            // support EPI-encoded base64 and datetime values
1037
            if ($val instanceof \stdClass && isset($val->xmlrpc_type)) {
1038
                $pt[] = $val->xmlrpc_type == 'datetime' ? Value::$xmlrpcDateTime : $val->xmlrpc_type;
1039
            } else {
1040
                $pt[] = $wrapper->php2XmlrpcType(gettype($val));
1041
            }
1042
        }
1043
1044
        $result = $server->execute($call['methodName'], $call['params'], $pt);
1045
1046
        if ($result->faultCode() != 0) {
1047
            return static::_xmlrpcs_multicall_error($result); // Method returned fault.
1048
        }
1049
1050
        return new Value(array($result->value()), 'array');
1051
    }
1052
1053
    /**
1054
     * @param Server $server
1055
     * @param Request|array $req
1056
     * @return Response
1057
     */
1058 77
    public static function _xmlrpcs_multicall($server, $req)
1059
    {
1060 77
        $result = array();
1061
        // let accept a plain list of php parameters, beside a single xmlrpc msg object
1062 77
        if (is_object($req)) {
1063 77
            $calls = $req->getParam(0);
1064 77
            foreach($calls as $call) {
1065 58
                $result[] = static::_xmlrpcs_multicall_do_call($server, $call);
1066
            }
1067
        } else {
1068
            $numCalls = count($req);
1069
            for ($i = 0; $i < $numCalls; $i++) {
1070
                $result[$i] = static::_xmlrpcs_multicall_do_call_phpvals($server, $req[$i]);
1071
            }
1072
        }
1073
1074 77
        return new Response(new Value($result, 'array'));
1075
    }
1076
1077
    /**
1078
     * Error handler used to track errors that occur during server-side execution of PHP code.
1079
     * This allows to report back to the client whether an internal error has occurred or not
1080
     * using an xmlrpc response object, instead of letting the client deal with the html junk
1081
     * that a PHP execution error on the server generally entails.
1082
     *
1083
     * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
1084
     */
1085 39
    public static function _xmlrpcs_errorHandler($errCode, $errString, $filename = null, $lineNo = null, $context = null)
1086
    {
1087
        // obey the @ protocol
1088 39
        if (error_reporting() == 0) {
1089 20
            return;
1090
        }
1091
1092
        //if($errCode != E_NOTICE && $errCode != E_WARNING && $errCode != E_USER_NOTICE && $errCode != E_USER_WARNING)
1093 20
        if ($errCode != E_STRICT) {
1094 20
            \PhpXmlRpc\Server::error_occurred($errString);
1095
        }
1096
        // Try to avoid as much as possible disruption to the previous error handling
1097
        // mechanism in place
1098 20
        if (self::$_xmlrpcs_prev_ehandler == '') {
1099
            // The previous error handler was the default: all we should do is log error
1100
            // to the default error log (if level high enough)
1101 20
            if (ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errCode)) {
1102 20
                Logger::instance()->errorLog($errString);
1103
            }
1104
        } else {
1105
            // Pass control on to previous error handler, trying to avoid loops...
1106
            if (self::$_xmlrpcs_prev_ehandler != array('\PhpXmlRpc\Server', '_xmlrpcs_errorHandler')) {
0 ignored issues
show
introduced by
The condition self::_xmlrpcs_prev_ehan..._xmlrpcs_errorHandler') is always true.
Loading history...
1107
                if (is_array(self::$_xmlrpcs_prev_ehandler)) {
0 ignored issues
show
introduced by
The condition is_array(self::_xmlrpcs_prev_ehandler) is always false.
Loading history...
1108
                    // the following works both with static class methods and plain object methods as error handler
1109
                    call_user_func_array(self::$_xmlrpcs_prev_ehandler, array($errCode, $errString, $filename, $lineNo, $context));
1110
                } else {
1111
                    $method = self::$_xmlrpcs_prev_ehandler;
1112
                    $method($errCode, $errString, $filename, $lineNo, $context);
1113
                }
1114
            }
1115
        }
1116 20
    }
1117
}
1118