Completed
Push — master ( 87b7a4...6ce28d )
by Gaetano
11:11 queued 06:38
created

Server::parseRequestHeaders()   F

Complexity

Conditions 26
Paths 1320

Size

Total Lines 90
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 64.9994

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 26
eloc 49
c 2
b 0
f 0
nc 1320
nop 4
dl 0
loc 90
rs 0
ccs 27
cts 44
cp 0.6136
crap 64.9994

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

658
                    array_unshift(/** @scrutinizer ignore-type */ $params, $this);
Loading history...
659
                    $r = call_user_func_array($func, $params);
660
                } else {
661
                    // 3rd API convention for method-handling functions: EPI-style
662
                    if ($this->functions_parameters_type == 'epivals') {
663
                        $r = call_user_func_array($func, array($methName, $params, $this->user_data));
664
                        // mimic EPI behaviour: if we get an array that looks like an error, make it
665
                        // an eror response
666
                        if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r)) {
667
                            $r = new Response(0, (integer)$r['faultCode'], (string)$r['faultString']);
668
                        } else {
669
                            // functions using EPI api should NOT return resp objects,
670
                            // so make sure we encode the return type correctly
671
                            $encoder = new Encoder();
672
                            $r = new Response($encoder->encode($r, array('extension_api')));
673
                        }
674
                    } else {
675
                        $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

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

826
    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...
827
    {
828
        $encoder = new Encoder();
829
        return new Response($encoder->encode($server->getCapabilities()));
830
    }
831
832
    /**
833
     * @param Server $server
834
     * @param Request $req if called in plain php values mode, second param is missing
835
     * @return Response
836
     */
837
    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

837
    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...
838
    {
839 20
        $outAr = array();
840 20
        foreach ($server->dmap as $key => $val) {
841 20
            $outAr[] = new Value($key, 'string');
842
        }
843 20
        if ($server->allow_system_funcs) {
844 20
            foreach ($server->getSystemDispatchMap() as $key => $val) {
845 20
                $outAr[] = new Value($key, 'string');
846
            }
847
        }
848
849 20
        return new Response(new Value($outAr, 'array'));
850
    }
851
852
    /**
853
     * @param Server $server
854
     * @param Request $req
855
     * @return Response
856
     */
857
    public static function _xmlrpcs_methodSignature($server, $req)
858
    {
859
        // let accept as parameter both an xmlrpc value or string
860 96
        if (is_object($req)) {
861 96
            $methName = $req->getParam(0);
862 96
            $methName = $methName->scalarval();
863
        } else {
864
            $methName = $req;
865
        }
866 96
        if (strpos($methName, "system.") === 0) {
867 77
            $dmap = $server->getSystemDispatchMap();
868
        } else {
869 20
            $dmap = $server->dmap;
870
        }
871 96
        if (isset($dmap[$methName])) {
872 96
            if (isset($dmap[$methName]['signature'])) {
873 96
                $sigs = array();
874 96
                foreach ($dmap[$methName]['signature'] as $inSig) {
875 96
                    $curSig = array();
876 96
                    foreach ($inSig as $sig) {
877 96
                        $curSig[] = new Value($sig, 'string');
878
                    }
879 96
                    $sigs[] = new Value($curSig, 'array');
880
                }
881 96
                $r = new Response(new Value($sigs, 'array'));
882
            } else {
883
                // NB: according to the official docs, we should be returning a
884
                // "none-array" here, which means not-an-array
885
                $r = new Response(new Value('undef', 'string'));
886
            }
887
        } else {
888
            $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
889
        }
890
891 96
        return $r;
892
    }
893
894
    /**
895
     * @param Server $server
896
     * @param Request $req
897
     * @return Response
898
     */
899
    public static function _xmlrpcs_methodHelp($server, $req)
900
    {
901
        // let accept as parameter both an xmlrpc value or string
902 77
        if (is_object($req)) {
903 77
            $methName = $req->getParam(0);
904 77
            $methName = $methName->scalarval();
905
        } else {
906
            $methName = $req;
907
        }
908 77
        if (strpos($methName, "system.") === 0) {
909 77
            $dmap = $server->getSystemDispatchMap();
910
        } else {
911 1
            $dmap = $server->dmap;
912
        }
913 77
        if (isset($dmap[$methName])) {
914 77
            if (isset($dmap[$methName]['docstring'])) {
915 77
                $r = new Response(new Value($dmap[$methName]['docstring']), 'string');
0 ignored issues
show
Bug introduced by
'string' of type string is incompatible with the type integer expected by parameter $fCode of PhpXmlRpc\Response::__construct(). ( Ignorable by Annotation )

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

915
                $r = new Response(new Value($dmap[$methName]['docstring']), /** @scrutinizer ignore-type */ 'string');
Loading history...
916
            } else {
917
                $r = new Response(new Value('', 'string'));
918
            }
919
        } else {
920
            $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
921
        }
922
923 77
        return $r;
924
    }
925
926
    public static function _xmlrpcs_multicall_error($err)
927
    {
928 58
        if (is_string($err)) {
929 58
            $str = PhpXmlRpc::$xmlrpcstr["multicall_${err}"];
930 58
            $code = PhpXmlRpc::$xmlrpcerr["multicall_${err}"];
931
        } else {
932 58
            $code = $err->faultCode();
933 58
            $str = $err->faultString();
934
        }
935 58
        $struct = array();
936 58
        $struct['faultCode'] = new Value($code, 'int');
937 58
        $struct['faultString'] = new Value($str, 'string');
938
939 58
        return new Value($struct, 'struct');
940
    }
941
942
    /**
943
     * @param Server $server
944
     * @param Value $call
945
     * @return Value
946
     */
947
    public static function _xmlrpcs_multicall_do_call($server, $call)
948
    {
949 58
        if ($call->kindOf() != 'struct') {
950
            return static::_xmlrpcs_multicall_error('notstruct');
951
        }
952 58
        $methName = @$call['methodName'];
953 58
        if (!$methName) {
954
            return static::_xmlrpcs_multicall_error('nomethod');
955
        }
956 58
        if ($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string') {
957
            return static::_xmlrpcs_multicall_error('notstring');
958
        }
959 58
        if ($methName->scalarval() == 'system.multicall') {
960 58
            return static::_xmlrpcs_multicall_error('recursion');
961
        }
962
963 58
        $params = @$call['params'];
964 58
        if (!$params) {
965
            return static::_xmlrpcs_multicall_error('noparams');
966
        }
967 58
        if ($params->kindOf() != 'array') {
968
            return static::_xmlrpcs_multicall_error('notarray');
969
        }
970
971 58
        $req = new Request($methName->scalarval());
972 58
        foreach($params as $i => $param) {
973 58
            if (!$req->addParam($param)) {
974
                $i++; // for error message, we count params from 1
975
                return static::_xmlrpcs_multicall_error(new Response(0,
976
                    PhpXmlRpc::$xmlrpcerr['incorrect_params'],
977
                    PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": probable xml error in param " . $i));
978
            }
979
        }
980
981 58
        $result = $server->execute($req);
982
983 58
        if ($result->faultCode() != 0) {
984 58
            return static::_xmlrpcs_multicall_error($result); // Method returned fault.
985
        }
986
987 58
        return new Value(array($result->value()), 'array');
988
    }
989
990
    /**
991
     * @param Server $server
992
     * @param Value $call
993
     * @return Value
994
     */
995
    public static function _xmlrpcs_multicall_do_call_phpvals($server, $call)
996
    {
997
        if (!is_array($call)) {
0 ignored issues
show
introduced by
The condition is_array($call) is always false.
Loading history...
998
            return static::_xmlrpcs_multicall_error('notstruct');
999
        }
1000
        if (!array_key_exists('methodName', $call)) {
1001
            return static::_xmlrpcs_multicall_error('nomethod');
1002
        }
1003
        if (!is_string($call['methodName'])) {
1004
            return static::_xmlrpcs_multicall_error('notstring');
1005
        }
1006
        if ($call['methodName'] == 'system.multicall') {
1007
            return static::_xmlrpcs_multicall_error('recursion');
1008
        }
1009
        if (!array_key_exists('params', $call)) {
1010
            return static::_xmlrpcs_multicall_error('noparams');
1011
        }
1012
        if (!is_array($call['params'])) {
1013
            return static::_xmlrpcs_multicall_error('notarray');
1014
        }
1015
1016
        // this is a real dirty and simplistic hack, since we might have received a
1017
        // base64 or datetime values, but they will be listed as strings here...
1018
        $pt = array();
1019
        $wrapper = new Wrapper();
1020
        foreach ($call['params'] as $val) {
1021
            $pt[] = $wrapper->php2XmlrpcType(gettype($val));
1022
        }
1023
1024
        $result = $server->execute($call['methodName'], $call['params'], $pt);
1025
1026
        if ($result->faultCode() != 0) {
1027
            return static::_xmlrpcs_multicall_error($result); // Method returned fault.
1028
        }
1029
1030
        return new Value(array($result->value()), 'array');
1031
    }
1032
1033
    /**
1034
     * @param Server $server
1035
     * @param Request|array $req
1036
     * @return Response
1037
     */
1038
    public static function _xmlrpcs_multicall($server, $req)
1039
    {
1040 77
        $result = array();
1041
        // let accept a plain list of php parameters, beside a single xmlrpc msg object
1042 77
        if (is_object($req)) {
1043 77
            $calls = $req->getParam(0);
1044 77
            foreach($calls as $call) {
1045 58
                $result[] = static::_xmlrpcs_multicall_do_call($server, $call);
1046
            }
1047
        } else {
1048
            $numCalls = count($req);
1049
            for ($i = 0; $i < $numCalls; $i++) {
1050
                $result[$i] = static::_xmlrpcs_multicall_do_call_phpvals($server, $req[$i]);
1051
            }
1052
        }
1053
1054 77
        return new Response(new Value($result, 'array'));
1055
    }
1056
1057
    /**
1058
     * Error handler used to track errors that occur during server-side execution of PHP code.
1059
     * This allows to report back to the client whether an internal error has occurred or not
1060
     * using an xmlrpc response object, instead of letting the client deal with the html junk
1061
     * that a PHP execution error on the server generally entails.
1062
     *
1063
     * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
1064
     */
1065
    public static function _xmlrpcs_errorHandler($errCode, $errString, $filename = null, $lineNo = null, $context = null)
1066
    {
1067
        // obey the @ protocol
1068 39
        if (error_reporting() == 0) {
1069 20
            return;
1070
        }
1071
1072
        //if($errCode != E_NOTICE && $errCode != E_WARNING && $errCode != E_USER_NOTICE && $errCode != E_USER_WARNING)
1073 20
        if ($errCode != E_STRICT) {
1074 20
            \PhpXmlRpc\Server::error_occurred($errString);
1075
        }
1076
        // Try to avoid as much as possible disruption to the previous error handling
1077
        // mechanism in place
1078 20
        if (self::$_xmlrpcs_prev_ehandler == '') {
1079
            // The previous error handler was the default: all we should do is log error
1080
            // to the default error log (if level high enough)
1081 20
            if (ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errCode)) {
1082 20
                Logger::instance()->errorLog($errString);
1083
            }
1084
        } else {
1085
            // Pass control on to previous error handler, trying to avoid loops...
1086
            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...
1087
                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...
1088
                    // the following works both with static class methods and plain object methods as error handler
1089
                    call_user_func_array(self::$_xmlrpcs_prev_ehandler, array($errCode, $errString, $filename, $lineNo, $context));
1090
                } else {
1091
                    $method = self::$_xmlrpcs_prev_ehandler;
1092
                    $method($errCode, $errString, $filename, $lineNo, $context);
1093
                }
1094
            }
1095
        }
1096 20
    }
1097
}
1098