Passed
Push — master ( 57812a...5787f8 )
by Gaetano
03:38
created

Server::isSyscall()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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 491
    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 491
        if (function_exists('gzinflate')) {
105 491
            $this->accepted_compression = array('gzip', 'deflate');
106 491
            $this->compress_response = true;
107
        }
108
109
        // by default the xml parser can support these 3 charset encodings
110 491
        $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 491
        if ($dispatchMap) {
119 490
            $this->dmap = $dispatchMap;
120 490
            if ($serviceNow) {
121 2
                $this->service();
122
            }
123
        }
124 491
    }
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 490
    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 490
        $out = '';
181 490
        if ($this->debug_info != '') {
182 488
            $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n" . base64_encode($this->debug_info) . "\n-->\n";
183
        }
184 490
        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 490
        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 490
    public function service($data = null, $returnPayload = false)
205
    {
206 490
        if ($data === null) {
207 490
            $data = file_get_contents('php://input');
208
        }
209 490
        $rawData = $data;
210
211
        // reset internal debug info
212 490
        $this->debug_info = '';
213
214
        // Save what we received, before parsing it
215 490
        if ($this->debug > 1) {
216 488
            $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
217
        }
218
219 490
        $r = $this->parseRequestHeaders($data, $reqCharset, $respCharset, $respEncoding);
220 490
        if (!$r) {
221
            // this actually executes the request
222 490
            $r = $this->parseRequest($data, $reqCharset);
223
        }
224
225
        // save full body of request into response, for more debugging usages
226 490
        $r->raw_data = $rawData;
227
228 490
        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 490
        $payload = $this->xml_header($respCharset);
234 490
        if ($this->debug > 0) {
235 490
            $payload = $payload . $this->serializeDebug($respCharset);
236
        }
237
238
        // Do not create response serialization if it has already happened. Helps building json magic
239 490
        if (empty($r->payload)) {
240 490
            $r->serialize($respCharset);
241
        }
242 490
        $payload = $payload . $r->payload;
243
244 490
        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 490
        if (!headers_sent()) {
251 490
            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 490
            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 490
            $phpNoSelfCompress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
260 490
            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
            // Note that Apache/mod_php will add (and even alter!) the Content-Length header on its own, but only for
277
            // responses up to 8000 bytes
278 490
            if ($phpNoSelfCompress) {
279 490
                header('Content-Length: ' . (int)strlen($payload));
280
            }
281
        } else {
282
            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': http headers already sent before response is fully generated. Check for php warning or error messages');
283
        }
284
285 490
        print $payload;
286
287
        // return request, in case subclasses want it
288 490
        return $r;
289
    }
290
291
    /**
292
     * Add a method to the dispatch map.
293
     *
294
     * @param string $methodName the name with which the method will be made available
295
     * @param string $function the php function that will get invoked
296
     * @param array $sig the array of valid method signatures
297
     * @param string $doc method documentation
298
     * @param array $sigDoc the array of valid method signatures docs (one string per param, one for return type)
299
     *
300
     * @todo raise a warning if the user tries to register a 'system.' method
301
     */
302
    public function add_to_map($methodName, $function, $sig = null, $doc = false, $sigDoc = false)
303
    {
304
        $this->dmap[$methodName] = array(
305
            'function' => $function,
306
            'docstring' => $doc,
307
        );
308
        if ($sig) {
309
            $this->dmap[$methodName]['signature'] = $sig;
310
        }
311
        if ($sigDoc) {
312
            $this->dmap[$methodName]['signature_docs'] = $sigDoc;
313
        }
314
    }
315
316
    /**
317
     * Verify type and number of parameters received against a list of known signatures.
318
     *
319
     * @param array|Request $in array of either xmlrpc value objects or xmlrpc type definitions
320
     * @param array $sigs array of known signatures to match against
321
     *
322
     * @return array int, string
323
     */
324 467
    protected function verifySignature($in, $sigs)
325
    {
326
        // check each possible signature in turn
327 467
        if (is_object($in)) {
328 467
            $numParams = $in->getNumParams();
329
        } else {
330
            $numParams = count($in);
331
        }
332 467
        foreach ($sigs as $curSig) {
333 467
            if (count($curSig) == $numParams + 1) {
334 467
                $itsOK = 1;
335 467
                for ($n = 0; $n < $numParams; $n++) {
336 448
                    if (is_object($in)) {
337 448
                        $p = $in->getParam($n);
338 448
                        if ($p->kindOf() == 'scalar') {
339 391
                            $pt = $p->scalartyp();
340
                        } else {
341 134
                            $pt = $p->kindOf();
342
                        }
343
                    } else {
344
                        $pt = ($in[$n] == 'i4') ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
345
                    }
346
347
                    // param index is $n+1, as first member of sig is return type
348 448
                    if ($pt != $curSig[$n + 1] && $curSig[$n + 1] != Value::$xmlrpcValue) {
349 20
                        $itsOK = 0;
350 20
                        $pno = $n + 1;
351 20
                        $wanted = $curSig[$n + 1];
352 20
                        $got = $pt;
353 20
                        break;
354
                    }
355
                }
356 467
                if ($itsOK) {
357 467
                    return array(1, '');
358
                }
359
            }
360
        }
361 20
        if (isset($wanted)) {
362
            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...
363
        } else {
364 20
            return array(0, "No method signature matches number of parameters");
365
        }
366
    }
367
368
    /**
369
     * Parse http headers received along with xmlrpc request. If needed, inflate request.
370
     *
371
     * @return mixed Response|null on success or an error Response
372
     */
373 490
    protected function parseRequestHeaders(&$data, &$reqEncoding, &$respEncoding, &$respCompression)
374
    {
375
        // check if $_SERVER is populated: it might have been disabled via ini file
376
        // (this is true even when in CLI mode)
377 490
        if (count($_SERVER) == 0) {
378
            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': cannot parse request headers as $_SERVER is not populated');
379
        }
380
381 490
        if ($this->debug > 1) {
382 488
            if (function_exists('getallheaders')) {
383 488
                $this->debugmsg(''); // empty line
384 488
                foreach (getallheaders() as $name => $val) {
385 488
                    $this->debugmsg("HEADER: $name: $val");
386
                }
387
            }
388
        }
389
390 490
        if (isset($_SERVER['HTTP_CONTENT_ENCODING'])) {
391 100
            $contentEncoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
392
        } else {
393 390
            $contentEncoding = '';
394
        }
395
396
        // check if request body has been compressed and decompress it
397 490
        if ($contentEncoding != '' && strlen($data)) {
398 100
            if ($contentEncoding == 'deflate' || $contentEncoding == 'gzip') {
399
                // if decoding works, use it. else assume data wasn't gzencoded
400 100
                if (function_exists('gzinflate') && in_array($contentEncoding, $this->accepted_compression)) {
401 100
                    if ($contentEncoding == 'deflate' && $degzdata = @gzuncompress($data)) {
402 50
                        $data = $degzdata;
403 50
                        if ($this->debug > 1) {
404 50
                            $this->debugmsg("\n+++INFLATED REQUEST+++[" . strlen($data) . " chars]+++\n" . $data . "\n+++END+++");
405
                        }
406 50
                    } elseif ($contentEncoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10))) {
407 50
                        $data = $degzdata;
408 50
                        if ($this->debug > 1) {
409 50
                            $this->debugmsg("+++INFLATED REQUEST+++[" . strlen($data) . " chars]+++\n" . $data . "\n+++END+++");
410
                        }
411
                    } else {
412
                        $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_decompress_fail'], PhpXmlRpc::$xmlrpcstr['server_decompress_fail']);
413
414
                        return $r;
415
                    }
416
                } else {
417
                    $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_cannot_decompress'], PhpXmlRpc::$xmlrpcstr['server_cannot_decompress']);
418
419
                    return $r;
420
                }
421
            }
422
        }
423
424
        // check if client specified accepted charsets, and if we know how to fulfill
425
        // the request
426 490
        if ($this->response_charset_encoding == 'auto') {
427
            $respEncoding = '';
428
            if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
429
                // here we should check if we can match the client-requested encoding
430
                // with the encodings we know we can generate.
431
                /// @todo we should parse q=0.x preferences instead of getting first charset specified...
432
                $clientAcceptedCharsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
433
                // Give preference to internal encoding
434
                $knownCharsets = array(PhpXmlRpc::$xmlrpc_internalencoding, 'UTF-8', 'ISO-8859-1', 'US-ASCII');
435
                foreach ($knownCharsets as $charset) {
436
                    foreach ($clientAcceptedCharsets as $accepted) {
437
                        if (strpos($accepted, $charset) === 0) {
438
                            $respEncoding = $charset;
439
                            break;
440
                        }
441
                    }
442
                    if ($respEncoding) {
443
                        break;
444
                    }
445
                }
446
            }
447
        } else {
448 490
            $respEncoding = $this->response_charset_encoding;
449
        }
450
451 490
        if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
452 100
            $respCompression = $_SERVER['HTTP_ACCEPT_ENCODING'];
453
        } else {
454 390
            $respCompression = '';
455
        }
456
457
        // 'guestimate' request encoding
458
        /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
459 490
        $reqEncoding = XMLParser::guessEncoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
460
            $data);
461
462 490
        return null;
463
    }
464
465
    /**
466
     * Parse an xml chunk containing an xmlrpc request and execute the corresponding
467
     * php function registered with the server.
468
     *
469
     * @param string $data the xml request
470
     * @param string $reqEncoding (optional) the charset encoding of the xml request
471
     *
472
     * @return Response
473
     *
474
     * @throws \Exception in case the executed method does throw an exception (and depending on server configuration)
475
     */
476 491
    public function parseRequest($data, $reqEncoding = '')
477
    {
478
        // decompose incoming XML into request structure
479
480 491
        if ($reqEncoding != '') {
481
            // Since parsing will fail if
482
            // - charset is not specified in the xml prologue,
483
            // - the encoding is not UTF8 and
484
            // - there are non-ascii chars in the text,
485
            // we try to work round that...
486
            // The following code might be better for mb_string enabled installs, but
487
            // makes the lib about 200% slower...
488
            //if (!is_valid_charset($reqEncoding, array('UTF-8')))
489 490
            if (!in_array($reqEncoding, array('UTF-8', 'US-ASCII')) && !XMLParser::hasEncoding($data)) {
490 4
                if ($reqEncoding == 'ISO-8859-1') {
491 2
                    $data = utf8_encode($data);
492
                } else {
493 2
                    if (extension_loaded('mbstring')) {
494 2
                        $data = mb_convert_encoding($data, 'UTF-8', $reqEncoding);
495
                    } else {
496
                        Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $reqEncoding);
497
                    }
498
                }
499
            }
500
        }
501
502
        // PHP internally might use ISO-8859-1, so we have to tell the xml parser to give us back data in the expected charset.
503
        // What if internal encoding is not in one of the 3 allowed? We use the broadest one, ie. utf8
504
        // This allows to send data which is native in various charset,
505
        // by extending xmlrpc_encode_entities() and setting xmlrpc_internalencoding
506 491
        if (!in_array(PhpXmlRpc::$xmlrpc_internalencoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) {
507
            $options = array(XML_OPTION_TARGET_ENCODING => 'UTF-8');
508
        } else {
509 491
            $options = array(XML_OPTION_TARGET_ENCODING => PhpXmlRpc::$xmlrpc_internalencoding);
510
        }
511
512 491
        $xmlRpcParser = new XMLParser($options);
513 491
        $xmlRpcParser->parse($data, $this->functions_parameters_type, XMLParser::ACCEPT_REQUEST);
514 491
        if ($xmlRpcParser->_xh['isf'] > 2) {
515
            // (BC) we return XML error as a faultCode
516 2
            preg_match('/^XML error ([0-9]+)/', $xmlRpcParser->_xh['isf_reason'], $matches);
517 2
            $r = new Response(0,
518 2
                PhpXmlRpc::$xmlrpcerrxml + $matches[1],
519 2
                $xmlRpcParser->_xh['isf_reason']);
520 489
        } elseif ($xmlRpcParser->_xh['isf']) {
521 1
            $r = new Response(0,
522 1
                PhpXmlRpc::$xmlrpcerr['invalid_request'],
523 1
                PhpXmlRpc::$xmlrpcstr['invalid_request'] . ' ' . $xmlRpcParser->_xh['isf_reason']);
524
        } else {
525
            // small layering violation in favor of speed and memory usage:
526
            // we should allow the 'execute' method handle this, but in the
527
            // most common scenario (xmlrpc values type server with some methods
528
            // registered as phpvals) that would mean a useless encode+decode pass
529 488
            if ($this->functions_parameters_type != 'xmlrpcvals' ||
530 488
                (isset($this->dmap[$xmlRpcParser->_xh['method']]['parameters_type']) &&
531
                    ($this->dmap[$xmlRpcParser->_xh['method']]['parameters_type'] == 'phpvals')
532
                )
533
            ) {
534
                if ($this->debug > 1) {
535
                    $this->debugmsg("\n+++PARSED+++\n" . var_export($xmlRpcParser->_xh['params'], true) . "\n+++END+++");
536
                }
537
                $r = $this->execute($xmlRpcParser->_xh['method'], $xmlRpcParser->_xh['params'], $xmlRpcParser->_xh['pt']);
538
            } else {
539
                // build a Request object with data parsed from xml
540 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

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

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

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

842
    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...
843
    {
844
        $encoder = new Encoder();
845
        return new Response($encoder->encode($server->getCapabilities()));
846
    }
847
848
    /**
849
     * @param Server $server
850
     * @param Request $req if called in plain php values mode, second param is missing
851
     * @return Response
852
     */
853 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

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