xmlrpcmsg::getNumParams()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 1
rs 10
c 0
b 0
f 0
eloc 1
nc 1
nop 0
1
<?php
2
// by Edd Dumbill (C) 1999-2002
3
// <[email protected]>
4
// $Id: xmlrpc.inc,v 1.174 2009/03/16 19:36:38 ggiunta Exp $
5
6
// Copyright (c) 1999,2000,2002 Edd Dumbill.
7
// All rights reserved.
8
//
9
// Redistribution and use in source and binary forms, with or without
10
// modification, are permitted provided that the following conditions
11
// are met:
12
//
13
//    * Redistributions of source code must retain the above copyright
14
//      notice, this list of conditions and the following disclaimer.
15
//
16
//    * Redistributions in binary form must reproduce the above
17
//      copyright notice, this list of conditions and the following
18
//      disclaimer in the documentation and/or other materials provided
19
//      with the distribution.
20
//
21
//    * Neither the name of the "XML-RPC for PHP" nor the names of its
22
//      contributors may be used to endorse or promote products derived
23
//      from this software without specific prior written permission.
24
//
25
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
// OF THE POSSIBILITY OF SUCH DAMAGE.
37
38
	if(!function_exists('xml_parser_create'))
39
	{
40
		// For PHP 4 onward, XML functionality is always compiled-in on windows:
41
		// no more need to dl-open it. It might have been compiled out on *nix...
42
		if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
43
		{
44
			dl('xml.so');
45
		}
46
	}
47
48
	// G. Giunta 2005/01/29: declare global these variables,
49
	// so that xmlrpc.inc will work even if included from within a function
50
	// Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
51
	$GLOBALS['xmlrpcI4']='i4';
52
	$GLOBALS['xmlrpcInt']='int';
53
	$GLOBALS['xmlrpcBoolean']='boolean';
54
	$GLOBALS['xmlrpcDouble']='double';
55
	$GLOBALS['xmlrpcString']='string';
56
	$GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
57
	$GLOBALS['xmlrpcBase64']='base64';
58
	$GLOBALS['xmlrpcArray']='array';
59
	$GLOBALS['xmlrpcStruct']='struct';
60
	$GLOBALS['xmlrpcValue']='undefined';
61
62
	$GLOBALS['xmlrpcTypes']=array(
63
		$GLOBALS['xmlrpcI4']       => 1,
64
		$GLOBALS['xmlrpcInt']      => 1,
65
		$GLOBALS['xmlrpcBoolean']  => 1,
66
		$GLOBALS['xmlrpcString']   => 1,
67
		$GLOBALS['xmlrpcDouble']   => 1,
68
		$GLOBALS['xmlrpcDateTime'] => 1,
69
		$GLOBALS['xmlrpcBase64']   => 1,
70
		$GLOBALS['xmlrpcArray']    => 2,
71
		$GLOBALS['xmlrpcStruct']   => 3
72
	);
73
74
	$GLOBALS['xmlrpc_valid_parents'] = array(
75
		'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
76
		'BOOLEAN' => array('VALUE'),
77
		'I4' => array('VALUE'),
78
		'INT' => array('VALUE'),
79
		'STRING' => array('VALUE'),
80
		'DOUBLE' => array('VALUE'),
81
		'DATETIME.ISO8601' => array('VALUE'),
82
		'BASE64' => array('VALUE'),
83
		'MEMBER' => array('STRUCT'),
84
		'NAME' => array('MEMBER'),
85
		'DATA' => array('ARRAY'),
86
		'ARRAY' => array('VALUE'),
87
		'STRUCT' => array('VALUE'),
88
		'PARAM' => array('PARAMS'),
89
		'METHODNAME' => array('METHODCALL'),
90
		'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
91
		'FAULT' => array('METHODRESPONSE'),
92
		'NIL' => array('VALUE'), // only used when extension activated
93
		'EX:NIL' => array('VALUE') // only used when extension activated
94
	);
95
96
	// define extra types for supporting NULL (useful for json or <NIL/>)
97
	$GLOBALS['xmlrpcNull']='null';
98
	$GLOBALS['xmlrpcTypes']['null']=1;
99
100
	// Not in use anymore since 2.0. Shall we remove it?
101
	/// @deprecated
102
	$GLOBALS['xmlEntities']=array(
103
		'amp'  => '&',
104
		'quot' => '"',
105
		'lt'   => '<',
106
		'gt'   => '>',
107
		'apos' => "'"
108
	);
109
110
	// tables used for transcoding different charsets into us-ascii xml
111
112
	$GLOBALS['xml_iso88591_Entities']=array();
113
	$GLOBALS['xml_iso88591_Entities']['in'] = array();
114
	$GLOBALS['xml_iso88591_Entities']['out'] = array();
115 View Code Duplication
	for ($i = 0; $i < 32; $i++)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
	{
117
		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
118
		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
119
	}
120 View Code Duplication
	for ($i = 160; $i < 256; $i++)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
	{
122
		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
123
		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
124
	}
125
126
	/// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
127
	/// These will NOT be present in true ISO-8859-1, but will save the unwary
128
	/// windows user from sending junk (though no luck when reciving them...)
129
  /*
130
	$GLOBALS['xml_cp1252_Entities']=array();
131
	for ($i = 128; $i < 160; $i++)
132
	{
133
		$GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
134
	}
135
	$GLOBALS['xml_cp1252_Entities']['out'] = array(
136
		'&#x20AC;', '?',        '&#x201A;', '&#x0192;',
137
		'&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
138
		'&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
139
		'&#x0152;', '?',        '&#x017D;', '?',
140
		'?',        '&#x2018;', '&#x2019;', '&#x201C;',
141
		'&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
142
		'&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
143
		'&#x0153;', '?',        '&#x017E;', '&#x0178;'
144
	);
145
  */
146
147
	$GLOBALS['xmlrpcerr'] = array(
148
	'unknown_method'=>1,
149
	'invalid_return'=>2,
150
	'incorrect_params'=>3,
151
	'introspect_unknown'=>4,
152
	'http_error'=>5,
153
	'no_data'=>6,
154
	'no_ssl'=>7,
155
	'curl_fail'=>8,
156
	'invalid_request'=>15,
157
	'no_curl'=>16,
158
	'server_error'=>17,
159
	'multicall_error'=>18,
160
	'multicall_notstruct'=>9,
161
	'multicall_nomethod'=>10,
162
	'multicall_notstring'=>11,
163
	'multicall_recursion'=>12,
164
	'multicall_noparams'=>13,
165
	'multicall_notarray'=>14,
166
167
	'cannot_decompress'=>103,
168
	'decompress_fail'=>104,
169
	'dechunk_fail'=>105,
170
	'server_cannot_decompress'=>106,
171
	'server_decompress_fail'=>107
172
	);
173
174
	$GLOBALS['xmlrpcstr'] = array(
175
	'unknown_method'=>'Unknown method',
176
	'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
177
	'incorrect_params'=>'Incorrect parameters passed to method',
178
	'introspect_unknown'=>"Can't introspect: method unknown",
179
	'http_error'=>"Didn't receive 200 OK from remote server.",
180
	'no_data'=>'No data received from server.',
181
	'no_ssl'=>'No SSL support compiled in.',
182
	'curl_fail'=>'CURL error',
183
	'invalid_request'=>'Invalid request payload',
184
	'no_curl'=>'No CURL support compiled in.',
185
	'server_error'=>'Internal server error',
186
	'multicall_error'=>'Received from server invalid multicall response',
187
	'multicall_notstruct'=>'system.multicall expected struct',
188
	'multicall_nomethod'=>'missing methodName',
189
	'multicall_notstring'=>'methodName is not a string',
190
	'multicall_recursion'=>'recursive system.multicall forbidden',
191
	'multicall_noparams'=>'missing params',
192
	'multicall_notarray'=>'params is not an array',
193
194
	'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
195
	'decompress_fail'=>'Received from server invalid compressed HTTP',
196
	'dechunk_fail'=>'Received from server invalid chunked HTTP',
197
	'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
198
	'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
199
	);
200
201
	// The charset encoding used by the server for received messages and
202
	// by the client for received responses when received charset cannot be determined
203
	// or is not supported
204
	$GLOBALS['xmlrpc_defencoding']='UTF-8';
205
206
	// The encoding used internally by PHP.
207
	// String values received as xml will be converted to this, and php strings will be converted to xml
208
	// as if having been coded with this
209
	$GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
210
211
	$GLOBALS['xmlrpcName']='XML-RPC for PHP';
212
	$GLOBALS['xmlrpcVersion']='3.0.0.beta';
213
214
	// let user errors start at 800
215
	$GLOBALS['xmlrpcerruser']=800;
216
	// let XML parse errors start at 100
217
	$GLOBALS['xmlrpcerrxml']=100;
218
219
	// formulate backslashes for escaping regexp
220
	// Not in use anymore since 2.0. Shall we remove it?
221
	/// @deprecated
222
	$GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
223
224
	// set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
225
	$GLOBALS['xmlrpc_null_extension']=false;
226
227
	// set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
228
	$GLOBALS['xmlrpc_null_apache_encoding']=false;
229
230
	// used to store state during parsing
231
	// quick explanation of components:
232
	//   ac - used to accumulate values
233
	//   isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
234
	//   isf_reason - used for storing xmlrpcresp fault string
235
	//   lv - used to indicate "looking for a value": implements
236
	//        the logic to allow values with no types to be strings
237
	//   params - used to store parameters in method calls
238
	//   method - used to store method name
239
	//   stack - array with genealogy of xml elements names:
240
	//           used to validate nesting of xmlrpc elements
241
	$GLOBALS['_xh']=null;
242
243
	/**
244
	* Convert a string to the correct XML representation in a target charset
245
	* To help correct communication of non-ascii chars inside strings, regardless
246
	* of the charset used when sending requests, parsing them, sending responses
247
	* and parsing responses, an option is to convert all non-ascii chars present in the message
248
	* into their equivalent 'charset entity'. Charset entities enumerated this way
249
	* are independent of the charset encoding used to transmit them, and all XML
250
	* parsers are bound to understand them.
251
	* Note that in the std case we are not sending a charset encoding mime type
252
	* along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
253
	*
254
	* @todo do a bit of basic benchmarking (strtr vs. str_replace)
255
	* @todo	make usage of iconv() or recode_string() or mb_string() where available
256
	*/
257
	function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
258
	{
259
		if ($src_encoding == '')
260
		{
261
			// lame, but we know no better...
262
			$src_encoding = $GLOBALS['xmlrpc_internalencoding'];
263
		}
264
265
		switch(strtoupper($src_encoding.'_'.$dest_encoding))
266
		{
267
			case 'ISO-8859-1_':
268
			case 'ISO-8859-1_US-ASCII':
269
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
270
				$escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
271
				break;
272
			case 'ISO-8859-1_UTF-8':
273
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
274
				$escaped_data = utf8_encode($escaped_data);
275
				break;
276
			case 'ISO-8859-1_ISO-8859-1':
277
			case 'US-ASCII_US-ASCII':
278
			case 'US-ASCII_UTF-8':
279
			case 'US-ASCII_':
280
			case 'US-ASCII_ISO-8859-1':
281
			case 'UTF-8_UTF-8':
282
			//case 'CP1252_CP1252':
283
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
284
				break;
285
			case 'UTF-8_':
286
			case 'UTF-8_US-ASCII':
287
			case 'UTF-8_ISO-8859-1':
288
	// NB: this will choke on invalid UTF-8, going most likely beyond EOF
289
	$escaped_data = '';
290
	// be kind to users creating string xmlrpcvals out of different php types
291
	$data = (string) $data;
292
	$ns = strlen ($data);
293
	for ($nn = 0; $nn < $ns; $nn++)
294
	{
295
		$ch = $data[$nn];
296
		$ii = ord($ch);
297
		//1 7 0bbbbbbb (127)
298
		if ($ii < 128)
299
		{
300
			/// @todo shall we replace this with a (supposedly) faster str_replace?
301
			switch($ii){
302
				case 34:
303
					$escaped_data .= '&quot;';
304
					break;
305
				case 38:
306
					$escaped_data .= '&amp;';
307
					break;
308
				case 39:
309
					$escaped_data .= '&apos;';
310
					break;
311
				case 60:
312
					$escaped_data .= '&lt;';
313
					break;
314
				case 62:
315
					$escaped_data .= '&gt;';
316
					break;
317
				default:
318
					$escaped_data .= $ch;
319
			} // switch
320
		}
321
		//2 11 110bbbbb 10bbbbbb (2047)
322
		else if ($ii>>5 == 6)
323
		{
324
			$b1 = ($ii & 31);
325
			$ii = ord($data[$nn+1]);
326
			$b2 = ($ii & 63);
327
			$ii = ($b1 * 64) + $b2;
328
			$ent = sprintf ('&#%d;', $ii);
329
			$escaped_data .= $ent;
330
			$nn += 1;
331
		}
332
		//3 16 1110bbbb 10bbbbbb 10bbbbbb
333
		else if ($ii>>4 == 14)
334
		{
335
			$b1 = ($ii & 15);
336
			$ii = ord($data[$nn+1]);
337
			$b2 = ($ii & 63);
338
			$ii = ord($data[$nn+2]);
339
			$b3 = ($ii & 63);
340
			$ii = ((($b1 * 64) + $b2) * 64) + $b3;
341
			$ent = sprintf ('&#%d;', $ii);
342
			$escaped_data .= $ent;
343
			$nn += 2;
344
		}
345
		//4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
346
		else if ($ii>>3 == 30)
347
		{
348
			$b1 = ($ii & 7);
349
			$ii = ord($data[$nn+1]);
350
			$b2 = ($ii & 63);
351
			$ii = ord($data[$nn+2]);
352
			$b3 = ($ii & 63);
353
			$ii = ord($data[$nn+3]);
354
			$b4 = ($ii & 63);
355
			$ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
356
			$ent = sprintf ('&#%d;', $ii);
357
			$escaped_data .= $ent;
358
			$nn += 3;
359
		}
360
	}
361
				break;
362
/*
363
			case 'CP1252_':
364
			case 'CP1252_US-ASCII':
365
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
366
				$escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
367
				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
368
				break;
369
			case 'CP1252_UTF-8':
370
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
371
				/// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
372
				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
373
				$escaped_data = utf8_encode($escaped_data);
374
				break;
375
			case 'CP1252_ISO-8859-1':
376
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
377
				// we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities...
378
				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
379
				break;
380
*/
381
			default:
382
				$escaped_data = '';
383
				error_log("Converting from $src_encoding to $dest_encoding: not supported...");
384
		}
385
		return $escaped_data;
386
	}
387
388
	/// xml parser handler function for opening element tags
389
	function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

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

Loading history...
390
	{
391
		// if invalid xmlrpc already detected, skip all processing
392
		if ($GLOBALS['_xh']['isf'] < 2)
393
		{
394
			// check for correct element nesting
395
			// top level element can only be of 2 types
396
			/// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
397
			///       there is only a single top level element in xml anyway
398
			if (count($GLOBALS['_xh']['stack']) == 0)
399
			{
400
				if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
401
					$name != 'VALUE' && !$accept_single_vals))
402
				{
403
					$GLOBALS['_xh']['isf'] = 2;
404
					$GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
405
					return;
406
				}
407
				else
408
				{
409
					$GLOBALS['_xh']['rt'] = strtolower($name);
410
					$GLOBALS['_xh']['rt'] = strtolower($name);
411
				}
412
			}
413
			else
414
			{
415
				// not top level element: see if parent is OK
416
				$parent = end($GLOBALS['_xh']['stack']);
417
				if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
418
				{
419
					$GLOBALS['_xh']['isf'] = 2;
420
					$GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
421
					return;
422
				}
423
			}
424
425
			switch($name)
426
			{
427
				// optimize for speed switch cases: most common cases first
428 View Code Duplication
				case 'VALUE':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
					/// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
430
					$GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
431
					$GLOBALS['_xh']['ac']='';
432
					$GLOBALS['_xh']['lv']=1;
433
					$GLOBALS['_xh']['php_class']=null;
434
					break;
435
				case 'I4':
436
				case 'INT':
437
				case 'STRING':
438
				case 'BOOLEAN':
439
				case 'DOUBLE':
440
				case 'DATETIME.ISO8601':
441 View Code Duplication
				case 'BASE64':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
442
					if ($GLOBALS['_xh']['vt']!='value')
443
					{
444
						//two data elements inside a value: an error occurred!
445
						$GLOBALS['_xh']['isf'] = 2;
446
						$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
447
						return;
448
					}
449
					$GLOBALS['_xh']['ac']=''; // reset the accumulator
450
					break;
451
				case 'STRUCT':
452
				case 'ARRAY':
453
					if ($GLOBALS['_xh']['vt']!='value')
454
					{
455
						//two data elements inside a value: an error occurred!
456
						$GLOBALS['_xh']['isf'] = 2;
457
						$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
458
						return;
459
					}
460
					// create an empty array to hold child values, and push it onto appropriate stack
461
					$cur_val = array();
462
					$cur_val['values'] = array();
463
					$cur_val['type'] = $name;
464
					// check for out-of-band information to rebuild php objs
465
					// and in case it is found, save it
466
					if (@isset($attrs['PHP_CLASS']))
467
					{
468
						$cur_val['php_class'] = $attrs['PHP_CLASS'];
469
					}
470
					$GLOBALS['_xh']['valuestack'][] = $cur_val;
471
					$GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
472
					break;
473
				case 'DATA':
474
					if ($GLOBALS['_xh']['vt']!='data')
475
					{
476
						//two data elements inside a value: an error occurred!
477
						$GLOBALS['_xh']['isf'] = 2;
478
						$GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
479
						return;
480
					}
481
				case 'METHODCALL':
482
				case 'METHODRESPONSE':
483
				case 'PARAMS':
484
					// valid elements that add little to processing
485
					break;
486
				case 'METHODNAME':
487
				case 'NAME':
488
					/// @todo we could check for 2 NAME elements inside a MEMBER element
489
					$GLOBALS['_xh']['ac']='';
490
					break;
491
				case 'FAULT':
492
					$GLOBALS['_xh']['isf']=1;
493
					break;
494 View Code Duplication
				case 'MEMBER':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
495
					$GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
496
					//$GLOBALS['_xh']['ac']='';
497
					// Drop trough intentionally
498
				case 'PARAM':
499
					// clear value type, so we can check later if no value has been passed for this param/member
500
					$GLOBALS['_xh']['vt']=null;
501
					break;
502
				case 'NIL':
503 View Code Duplication
				case 'EX:NIL':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
504
					if ($GLOBALS['xmlrpc_null_extension'])
505
					{
506
						if ($GLOBALS['_xh']['vt']!='value')
507
						{
508
							//two data elements inside a value: an error occurred!
509
							$GLOBALS['_xh']['isf'] = 2;
510
							$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
511
							return;
512
						}
513
						$GLOBALS['_xh']['ac']=''; // reset the accumulator
514
						break;
515
					}
516
					// we do not support the <NIL/> extension, so
517
					// drop through intentionally
518
				default:
519
					/// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
520
					$GLOBALS['_xh']['isf'] = 2;
521
					$GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
522
					break;
523
			}
524
525
			// Save current element name to stack, to validate nesting
526
			$GLOBALS['_xh']['stack'][] = $name;
527
528
			/// @todo optimization creep: move this inside the big switch() above
529
			if($name!='VALUE')
530
			{
531
				$GLOBALS['_xh']['lv']=0;
532
			}
533
		}
534
	}
535
536
	/// Used in decoding xml chunks that might represent single xmlrpc values
537
	function xmlrpc_se_any($parser, $name, $attrs)
538
	{
539
		xmlrpc_se($parser, $name, $attrs, true);
540
	}
541
542
	/// xml parser handler function for close element tags
543
	function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

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

Loading history...
544
	{
545
		if ($GLOBALS['_xh']['isf'] < 2)
546
		{
547
			// push this element name from stack
548
			// NB: if XML validates, correct opening/closing is guaranteed and
549
			// we do not have to check for $name == $curr_elem.
550
			// we also checked for proper nesting at start of elements...
551
			$curr_elem = array_pop($GLOBALS['_xh']['stack']);
0 ignored issues
show
Unused Code introduced by
$curr_elem is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
552
553
			switch($name)
554
			{
555
				case 'VALUE':
556
					// This if() detects if no scalar was inside <VALUE></VALUE>
557
					if ($GLOBALS['_xh']['vt']=='value')
558
					{
559
						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
560
						$GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
561
					}
562
563
					if ($rebuild_xmlrpcvals)
564
					{
565
						// build the xmlrpc val out of the data received, and substitute it
566
						$temp = new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
567
						// in case we got info about underlying php class, save it
568
						// in the object we're rebuilding
569
						if (isset($GLOBALS['_xh']['php_class']))
570
							$temp->_php_class = $GLOBALS['_xh']['php_class'];
571
						// check if we are inside an array or struct:
572
						// if value just built is inside an array, let's move it into array on the stack
573
						$vscount = count($GLOBALS['_xh']['valuestack']);
574
						if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
575
						{
576
							$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
577
						}
578
						else
579
						{
580
							$GLOBALS['_xh']['value'] = $temp;
581
						}
582
					}
583 View Code Duplication
					else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
584
					{
585
						/// @todo this needs to treat correctly php-serialized objects,
586
						/// since std deserializing is done by php_xmlrpc_decode,
587
						/// which we will not be calling...
588
						if (isset($GLOBALS['_xh']['php_class']))
589
						{
590
						}
591
592
						// check if we are inside an array or struct:
593
						// if value just built is inside an array, let's move it into array on the stack
594
						$vscount = count($GLOBALS['_xh']['valuestack']);
595
						if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
596
						{
597
							$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
598
						}
599
					}
600
					break;
601
				case 'BOOLEAN':
602
				case 'I4':
603
				case 'INT':
604
				case 'STRING':
605
				case 'DOUBLE':
606
				case 'DATETIME.ISO8601':
607
				case 'BASE64':
608
					$GLOBALS['_xh']['vt']=strtolower($name);
609
					/// @todo: optimization creep - remove the if/elseif cycle below
610
					/// since the case() in which we are already did that
611
					if ($name=='STRING')
612
					{
613
						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
614
					}
615
					elseif ($name=='DATETIME.ISO8601')
616
					{
617
						if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
618
						{
619
							error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
620
						}
621
						$GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
622
						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
623
					}
624
					elseif ($name=='BASE64')
625
					{
626
						/// @todo check for failure of base64 decoding / catch warnings
627
						$GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
628
					}
629
					elseif ($name=='BOOLEAN')
630
					{
631
						// special case here: we translate boolean 1 or 0 into PHP
632
						// constants true or false.
633
						// Strings 'true' and 'false' are accepted, even though the
634
						// spec never mentions them (see eg. Blogger api docs)
635
						// NB: this simple checks helps a lot sanitizing input, ie no
636
						// security problems around here
637
						if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
638
						{
639
							$GLOBALS['_xh']['value']=true;
640
						}
641
						else
642
						{
643
							// log if receiveing something strange, even though we set the value to false anyway
644
							if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0)
645
								error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
646
							$GLOBALS['_xh']['value']=false;
647
						}
648
					}
649 View Code Duplication
					elseif ($name=='DOUBLE')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
650
					{
651
						// we have a DOUBLE
652
						// we must check that only 0123456789-.<space> are characters here
653
						// NOTE: regexp could be much stricter than this...
654
						if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac']))
655
						{
656
							/// @todo: find a better way of throwing an error than this!
657
							error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
658
							$GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
659
						}
660
						else
661
						{
662
							// it's ok, add it on
663
							$GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
664
						}
665
					}
666 View Code Duplication
					else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
667
					{
668
						// we have an I4/INT
669
						// we must check that only 0123456789-<space> are characters here
670
						if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
671
						{
672
							/// @todo find a better way of throwing an error than this!
673
							error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
674
							$GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
675
						}
676
						else
677
						{
678
							// it's ok, add it on
679
							$GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
680
						}
681
					}
682
					//$GLOBALS['_xh']['ac']=''; // is this necessary?
683
					$GLOBALS['_xh']['lv']=3; // indicate we've found a value
684
					break;
685 View Code Duplication
				case 'NAME':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
686
					$GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
687
					break;
688
				case 'MEMBER':
689
					//$GLOBALS['_xh']['ac']=''; // is this necessary?
690
					// add to array in the stack the last element built,
691
					// unless no VALUE was found
692 View Code Duplication
					if ($GLOBALS['_xh']['vt'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
693
					{
694
						$vscount = count($GLOBALS['_xh']['valuestack']);
695
						$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
696
					} else
697
						error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
698
					break;
699
				case 'DATA':
700
					//$GLOBALS['_xh']['ac']=''; // is this necessary?
701
					$GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
702
					break;
703
				case 'STRUCT':
704
				case 'ARRAY':
705
					// fetch out of stack array of values, and promote it to current value
706
					$curr_val = array_pop($GLOBALS['_xh']['valuestack']);
707
					$GLOBALS['_xh']['value'] = $curr_val['values'];
708
					$GLOBALS['_xh']['vt']=strtolower($name);
709
					if (isset($curr_val['php_class']))
710
					{
711
						$GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
712
					}
713
					break;
714
				case 'PARAM':
715
					// add to array of params the current value,
716
					// unless no VALUE was found
717
					if ($GLOBALS['_xh']['vt'])
718
					{
719
						$GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
720
						$GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
721
					}
722
					else
723
						error_log('XML-RPC: missing VALUE inside PARAM in received xml');
724
					break;
725
				case 'METHODNAME':
726
					$GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
727
					break;
728
				case 'NIL':
729 View Code Duplication
				case 'EX:NIL':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
730
					if ($GLOBALS['xmlrpc_null_extension'])
731
					{
732
						$GLOBALS['_xh']['vt']='null';
733
						$GLOBALS['_xh']['value']=null;
734
						$GLOBALS['_xh']['lv']=3;
735
						break;
736
					}
737
					// drop through intentionally if nil extension not enabled
738
				case 'PARAMS':
739
				case 'FAULT':
740
				case 'METHODCALL':
741
				case 'METHORESPONSE':
742
					break;
743
				default:
744
					// End of INVALID ELEMENT!
745
					// shall we add an assert here for unreachable code???
746
					break;
747
			}
748
		}
749
	}
750
751
	/// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
752
	function xmlrpc_ee_fast($parser, $name)
753
	{
754
		xmlrpc_ee($parser, $name, false);
755
	}
756
757
	/// xml parser handler function for character data
758
	function xmlrpc_cd($parser, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

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

Loading history...
759
	{
760
		// skip processing if xml fault already detected
761
		if ($GLOBALS['_xh']['isf'] < 2)
762
		{
763
			// "lookforvalue==3" means that we've found an entire value
764
			// and should discard any further character data
765
			if($GLOBALS['_xh']['lv']!=3)
766
			{
767
				// G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
768
				//if($GLOBALS['_xh']['lv']==1)
769
				//{
770
					// if we've found text and we're just in a <value> then
771
					// say we've found a value
772
					//$GLOBALS['_xh']['lv']=2;
773
				//}
774
				// we always initialize the accumulator before starting parsing, anyway...
775
				//if(!@isset($GLOBALS['_xh']['ac']))
776
				//{
777
				//	$GLOBALS['_xh']['ac'] = '';
778
				//}
779
				$GLOBALS['_xh']['ac'].=$data;
780
			}
781
		}
782
	}
783
784
	/// xml parser handler function for 'other stuff', ie. not char data or
785
	/// element start/end tag. In fact it only gets called on unknown entities...
786
	function xmlrpc_dh($parser, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

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

Loading history...
787
	{
788
		// skip processing if xml fault already detected
789
		if ($GLOBALS['_xh']['isf'] < 2)
790
		{
791
			if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
792
			{
793
				// G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
794
				//if($GLOBALS['_xh']['lv']==1)
795
				//{
796
				//	$GLOBALS['_xh']['lv']=2;
797
				//}
798
				$GLOBALS['_xh']['ac'].=$data;
799
			}
800
		}
801
		return true;
802
	}
803
804
	class xmlrpc_client
805
	{
806
		var $path;
807
		var $server;
808
		var $port=0;
809
		var $method='http';
810
		var $errno;
811
		var $errstr;
812
		var $debug=0;
813
		var $username='';
814
		var $password='';
815
		var $authtype=1;
816
		var $cert='';
817
		var $certpass='';
818
		var $cacert='';
819
		var $cacertdir='';
820
		var $key='';
821
		var $keypass='';
822
		var $verifypeer=true;
823
		var $verifyhost=1;
824
		var $no_multicall=false;
825
		var $proxy='';
826
		var $proxyport=0;
827
		var $proxy_user='';
828
		var $proxy_pass='';
829
		var $proxy_authtype=1;
830
		var $cookies=array();
831
		var $extracurlopts=array();
832
833
		/**
834
		* List of http compression methods accepted by the client for responses.
835
		* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
836
		*
837
		* NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
838
		* in those cases it will be up to CURL to decide the compression methods
839
		* it supports. You might check for the presence of 'zlib' in the output of
840
		* curl_version() to determine wheter compression is supported or not
841
		*/
842
		var $accepted_compression = array();
843
		/**
844
		* Name of compression scheme to be used for sending requests.
845
		* Either null, gzip or deflate
846
		*/
847
		var $request_compression = '';
848
		/**
849
		* CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
850
		* http://curl.haxx.se/docs/faq.html#7.3)
851
		*/
852
		var $xmlrpc_curl_handle = null;
853
		/// Wheter to use persistent connections for http 1.1 and https
854
		var $keepalive = false;
855
		/// Charset encodings that can be decoded without problems by the client
856
		var $accepted_charset_encodings = array();
857
		/// Charset encoding to be used in serializing request. NULL = use ASCII
858
		var $request_charset_encoding = '';
859
		/**
860
		* Decides the content of xmlrpcresp objects returned by calls to send()
861
		* valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
862
		*/
863
		var $return_type = 'xmlrpcvals';
864
		/**
865
		* Sent to servers in http headers
866
		*/
867
		var $user_agent;
868
869
		/**
870
		* @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
871
		* @param string $server the server name / ip address
872
		* @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
0 ignored issues
show
Documentation introduced by
Should the type for parameter $port not be string|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
873
		* @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
874
		*/
875
		function xmlrpc_client($path, $server='', $port='', $method='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
876
		{
877
			// allow user to specify all params in $path
878
			if($server == '' and $port == '' and $method == '')
879
			{
880
				$parts = parse_url($path);
881
				$server = $parts['host'];
882
				$path = isset($parts['path']) ? $parts['path'] : '';
883
				if(isset($parts['query']))
884
				{
885
					$path .= '?'.$parts['query'];
886
				}
887
				if(isset($parts['fragment']))
888
				{
889
					$path .= '#'.$parts['fragment'];
890
				}
891
				if(isset($parts['port']))
892
				{
893
					$port = $parts['port'];
894
				}
895
				if(isset($parts['scheme']))
896
				{
897
					$method = $parts['scheme'];
898
				}
899
				if(isset($parts['user']))
900
				{
901
					$this->username = $parts['user'];
902
				}
903
				if(isset($parts['pass']))
904
				{
905
					$this->password = $parts['pass'];
906
				}
907
			}
908
			if($path == '' || $path[0] != '/')
909
			{
910
				$this->path='/'.$path;
911
			}
912
			else
913
			{
914
				$this->path=$path;
915
			}
916
			$this->server=$server;
917
			if($port != '')
918
			{
919
				$this->port=$port;
920
			}
921
			if($method != '')
922
			{
923
				$this->method=$method;
924
			}
925
926
			// if ZLIB is enabled, let the client by default accept compressed responses
927
			if(function_exists('gzinflate') || (
928
				function_exists('curl_init') && (($info = curl_version()) &&
929
				((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
930
			))
931
			{
932
				$this->accepted_compression = array('gzip', 'deflate');
933
			}
934
935
			// keepalives: enabled by default
936
			$this->keepalive = true;
937
938
			// by default the xml parser can support these 3 charset encodings
939
			$this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
940
941
			// initialize user_agent string
942
			$this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
943
		}
944
945
		/**
946
		* Enables/disables the echoing to screen of the xmlrpc responses received
947
		* @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
0 ignored issues
show
Bug introduced by
There is no parameter named $debug. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
948
		* @access public
949
		*/
950
		function setDebug($in)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
951
		{
952
			$this->debug=$in;
953
		}
954
955
		/**
956
		* Add some http BASIC AUTH credentials, used by the client to authenticate
957
		* @param string $u username
958
		* @param string $p password
959
		* @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
960
		* @access public
961
		*/
962
		function setCredentials($u, $p, $t=1)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
963
		{
964
			$this->username=$u;
965
			$this->password=$p;
966
			$this->authtype=$t;
967
		}
968
969
		/**
970
		* Add a client-side https certificate
971
		* @param string $cert
972
		* @param string $certpass
973
		* @access public
974
		*/
975
		function setCertificate($cert, $certpass)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
976
		{
977
			$this->cert = $cert;
978
			$this->certpass = $certpass;
979
		}
980
981
		/**
982
		* Add a CA certificate to verify server with (see man page about
983
		* CURLOPT_CAINFO for more details
984
		* @param string $cacert certificate file name (or dir holding certificates)
985
		* @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
986
		* @access public
987
		*/
988
		function setCaCertificate($cacert, $is_dir=false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
989
		{
990
			if ($is_dir)
991
			{
992
				$this->cacertdir = $cacert;
993
			}
994
			else
995
			{
996
				$this->cacert = $cacert;
997
			}
998
		}
999
1000
		/**
1001
		* Set attributes for SSL communication: private SSL key
1002
		* NB: does not work in older php/curl installs
1003
		* Thanks to Daniel Convissor
1004
		* @param string $key The name of a file containing a private SSL key
1005
		* @param string $keypass The secret password needed to use the private SSL key
1006
		* @access public
1007
		*/
1008
		function setKey($key, $keypass)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1009
		{
1010
			$this->key = $key;
1011
			$this->keypass = $keypass;
1012
		}
1013
1014
		/**
1015
		* Set attributes for SSL communication: verify server certificate
1016
		* @param bool $i enable/disable verification of peer certificate
1017
		* @access public
1018
		*/
1019
		function setSSLVerifyPeer($i)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1020
		{
1021
			$this->verifypeer = $i;
1022
		}
1023
1024
		/**
1025
		* Set attributes for SSL communication: verify match of server cert w. hostname
1026
		* @param int $i
1027
		* @access public
1028
		*/
1029
		function setSSLVerifyHost($i)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1030
		{
1031
			$this->verifyhost = $i;
1032
		}
1033
1034
		/**
1035
		* Set proxy info
1036
		* @param string $proxyhost
1037
		* @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1038
		* @param string $proxyusername Leave blank if proxy has public access
1039
		* @param string $proxypassword Leave blank if proxy has public access
1040
		* @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1041
		* @access public
1042
		*/
1043
		function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1044
		{
1045
			$this->proxy = $proxyhost;
1046
			$this->proxyport = $proxyport;
0 ignored issues
show
Documentation Bug introduced by
The property $proxyport was declared of type integer, but $proxyport is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1047
			$this->proxy_user = $proxyusername;
1048
			$this->proxy_pass = $proxypassword;
1049
			$this->proxy_authtype = $proxyauthtype;
1050
		}
1051
1052
		/**
1053
		* Enables/disables reception of compressed xmlrpc responses.
1054
		* Note that enabling reception of compressed responses merely adds some standard
1055
		* http headers to xmlrpc requests. It is up to the xmlrpc server to return
1056
		* compressed responses when receiving such requests.
1057
		* @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1058
		* @access public
1059
		*/
1060
		function setAcceptedCompression($compmethod)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1061
		{
1062
			if ($compmethod == 'any')
1063
				$this->accepted_compression = array('gzip', 'deflate');
1064
			else
1065
				$this->accepted_compression = array($compmethod);
1066
		}
1067
1068
		/**
1069
		* Enables/disables http compression of xmlrpc request.
1070
		* Take care when sending compressed requests: servers might not support them
1071
		* (and automatic fallback to uncompressed requests is not yet implemented)
1072
		* @param string $compmethod either 'gzip', 'deflate' or ''
1073
		* @access public
1074
		*/
1075
		function setRequestCompression($compmethod)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1076
		{
1077
			$this->request_compression = $compmethod;
1078
		}
1079
1080
		/**
1081
		* Adds a cookie to list of cookies that will be sent to server.
1082
		* NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1083
		* do not do it unless you know what you are doing
1084
		* @param string $name
1085
		* @param string $value
1086
		* @param string $path
1087
		* @param string $domain
1088
		* @param int $port
0 ignored issues
show
Documentation introduced by
Should the type for parameter $port not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1089
		* @access public
1090
		*
1091
		* @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1092
		*/
1093
		function setCookie($name, $value='', $path='', $domain='', $port=null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1094
		{
1095
			$this->cookies[$name]['value'] = urlencode($value);
1096
			if ($path || $domain || $port)
0 ignored issues
show
Bug Best Practice introduced by
The expression $port of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1097
			{
1098
				$this->cookies[$name]['path'] = $path;
1099
				$this->cookies[$name]['domain'] = $domain;
1100
				$this->cookies[$name]['port'] = $port;
1101
				$this->cookies[$name]['version'] = 1;
1102
			}
1103
			else
1104
			{
1105
				$this->cookies[$name]['version'] = 0;
1106
			}
1107
		}
1108
1109
		/**
1110
		* Directly set cURL options, for extra flexibility
1111
		* It allows eg. to bind client to a specific IP interface / address
1112
		* @param $options array
1113
		*/
1114
		function SetCurlOptions( $options )
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1115
		{
1116
			$this->extracurlopts = $options;
1117
		}
1118
1119
		/**
1120
		* Set user-agent string that will be used by this client instance
1121
		* in http headers sent to the server
1122
		*/
1123
		function SetUserAgent( $agentstring )
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1124
		{
1125
			$this->user_agent = $agentstring;
1126
		}
1127
1128
		/**
1129
		* Send an xmlrpc request
1130
		* @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1131
		* @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1132
		* @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1133
		* @return xmlrpcresp
1134
		* @access public
1135
		*/
1136
		function& send($msg, $timeout=0, $method='')
1137
		{
1138
			// if user deos not specify http protocol, use native method of this client
1139
			// (i.e. method set during call to constructor)
1140
			if($method == '')
1141
			{
1142
				$method = $this->method;
1143
			}
1144
1145
			if(is_array($msg))
1146
			{
1147
				// $msg is an array of xmlrpcmsg's
1148
				$r = $this->multicall($msg, $timeout, $method);
1149
				return $r;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $r; (array) is incompatible with the return type documented by xmlrpc_client::send of type xmlrpcresp.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1150
			}
1151
			elseif(is_string($msg))
1152
			{
1153
				$n = new xmlrpcmsg('');
1154
				$n->payload = $msg;
1155
				$msg = $n;
1156
			}
1157
1158
			// where msg is an xmlrpcmsg
1159
			$msg->debug=$this->debug;
1160
1161
			if($method == 'https')
1162
			{
1163
				$r =& $this->sendPayloadHTTPS(
1164
					$msg,
1165
					$this->server,
1166
					$this->port,
1167
					$timeout,
1168
					$this->username,
1169
					$this->password,
1170
					$this->authtype,
1171
					$this->cert,
1172
					$this->certpass,
1173
					$this->cacert,
1174
					$this->cacertdir,
1175
					$this->proxy,
1176
					$this->proxyport,
1177
					$this->proxy_user,
1178
					$this->proxy_pass,
1179
					$this->proxy_authtype,
1180
					$this->keepalive,
1181
					$this->key,
1182
					$this->keypass
1183
				);
1184
			}
1185
			elseif($method == 'http11')
1186
			{
1187
				$r =& $this->sendPayloadCURL(
1188
					$msg,
1189
					$this->server,
1190
					$this->port,
1191
					$timeout,
1192
					$this->username,
1193
					$this->password,
1194
					$this->authtype,
1195
					null,
1196
					null,
1197
					null,
1198
					null,
1199
					$this->proxy,
1200
					$this->proxyport,
1201
					$this->proxy_user,
1202
					$this->proxy_pass,
1203
					$this->proxy_authtype,
1204
					'http',
1205
					$this->keepalive
1206
				);
1207
			}
1208
			else
1209
			{
1210
				$r =& $this->sendPayloadHTTP10(
1211
					$msg,
1212
					$this->server,
1213
					$this->port,
1214
					$timeout,
1215
					$this->username,
1216
					$this->password,
1217
					$this->authtype,
1218
					$this->proxy,
1219
					$this->proxyport,
1220
					$this->proxy_user,
1221
					$this->proxy_pass,
1222
					$this->proxy_authtype
1223
				);
1224
			}
1225
1226
			return $r;
1227
		}
1228
1229
		/**
1230
		* @access private
1231
		*/
1232
		function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1233
			$username='', $password='', $authtype=1, $proxyhost='',
1234
			$proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1235
		{
1236
			if($port==0)
1237
			{
1238
				$port=80;
1239
			}
1240
1241
			// Only create the payload if it was not created previously
1242
			if(empty($msg->payload))
1243
			{
1244
				$msg->createPayload($this->request_charset_encoding);
1245
			}
1246
1247
			$payload = $msg->payload;
1248
			// Deflate request body and set appropriate request headers
1249 View Code Duplication
			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1250
			{
1251
				if($this->request_compression == 'gzip')
1252
				{
1253
					$a = @gzencode($payload);
1254
					if($a)
1255
					{
1256
						$payload = $a;
1257
						$encoding_hdr = "Content-Encoding: gzip\r\n";
1258
					}
1259
				}
1260
				else
1261
				{
1262
					$a = @gzcompress($payload);
1263
					if($a)
1264
					{
1265
						$payload = $a;
1266
						$encoding_hdr = "Content-Encoding: deflate\r\n";
1267
					}
1268
				}
1269
			}
1270
			else
1271
			{
1272
				$encoding_hdr = '';
1273
			}
1274
1275
			// thanks to Grant Rauscher <[email protected]> for this
1276
			$credentials='';
1277
			if($username!='')
1278
			{
1279
				$credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1280
				if ($authtype != 1)
1281
				{
1282
					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1283
				}
1284
			}
1285
1286
			$accepted_encoding = '';
1287
			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1288
			{
1289
				$accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1290
			}
1291
1292
			$proxy_credentials = '';
1293
			if($proxyhost)
1294
			{
1295
				if($proxyport == 0)
1296
				{
1297
					$proxyport = 8080;
1298
				}
1299
				$connectserver = $proxyhost;
1300
				$connectport = $proxyport;
1301
				$uri = 'http://'.$server.':'.$port.$this->path;
1302
				if($proxyusername != '')
1303
				{
1304
					if ($proxyauthtype != 1)
1305
					{
1306
						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1307
					}
1308
					$proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1309
				}
1310
			}
1311
			else
1312
			{
1313
				$connectserver = $server;
1314
				$connectport = $port;
1315
				$uri = $this->path;
1316
			}
1317
1318
			// Cookie generation, as per rfc2965 (version 1 cookies) or
1319
			// netscape's rules (version 0 cookies)
1320
			$cookieheader='';
1321
			if (count($this->cookies))
1322
			{
1323
				$version = '';
1324
				foreach ($this->cookies as $name => $cookie)
1325
				{
1326
					if ($cookie['version'])
1327
					{
1328
						$version = ' $Version="' . $cookie['version'] . '";';
1329
						$cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1330
						if ($cookie['path'])
1331
							$cookieheader .= ' $Path="' . $cookie['path'] . '";';
1332
						if ($cookie['domain'])
1333
							$cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1334
						if ($cookie['port'])
1335
							$cookieheader .= ' $Port="' . $cookie['port'] . '";';
1336
					}
1337
					else
1338
					{
1339
						$cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1340
					}
1341
				}
1342
				$cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1343
			}
1344
1345
			$op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1346
				'User-Agent: ' . $this->user_agent . "\r\n" .
1347
				'Host: '. $server . ':' . $port . "\r\n" .
1348
				$credentials .
1349
				$proxy_credentials .
1350
				$accepted_encoding .
1351
				$encoding_hdr .
0 ignored issues
show
Bug introduced by
The variable $encoding_hdr does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1352
				'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1353
				$cookieheader .
1354
				'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1355
				strlen($payload) . "\r\n\r\n" .
1356
				$payload;
1357
1358
			if($this->debug > 1)
1359
			{
1360
				print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1361
				// let the client see this now in case http times out...
1362
				flush();
1363
			}
1364
1365
			if($timeout>0)
1366
			{
1367
				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1368
			}
1369
			else
1370
			{
1371
				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1372
			}
1373
			if($fp)
1374
			{
1375
				if($timeout>0 && function_exists('stream_set_timeout'))
1376
				{
1377
					stream_set_timeout($fp, $timeout);
1378
				}
1379
			}
1380
			else
1381
			{
1382
				$this->errstr='Connect error: '.$this->errstr;
1383
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1384
				return $r;
1385
			}
1386
1387
			if(!fputs($fp, $op, strlen($op)))
1388
			{
1389
				fclose($fp);
1390
				$this->errstr='Write error';
1391
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1392
				return $r;
1393
			}
1394
			else
1395
			{
1396
				// reset errno and errstr on succesful socket connection
1397
				$this->errstr = '';
1398
			}
1399
			// G. Giunta 2005/10/24: close socket before parsing.
1400
			// should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1401
			$ipd='';
1402
			do
1403
			{
1404
				// shall we check for $data === FALSE?
1405
				// as per the manual, it signals an error
1406
				$ipd.=fread($fp, 32768);
1407
			} while(!feof($fp));
1408
			fclose($fp);
1409
			$r =& $msg->parseResponse($ipd, false, $this->return_type);
1410
			return $r;
1411
1412
		}
1413
1414
		/**
1415
		* @access private
1416
		*/
1417
		function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1418
			$password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1419
			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1420
			$keepalive=false, $key='', $keypass='')
1421
		{
1422
			$r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1423
				$password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1424
				$proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1425
			return $r;
1426
		}
1427
1428
		/**
1429
		* Contributed by Justin Miller <[email protected]>
1430
		* Requires curl to be built into PHP
1431
		* NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1432
		* @access private
1433
		*/
1434
		function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1435
			$password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1436
			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1437
			$keepalive=false, $key='', $keypass='')
1438
		{
1439
			if(!function_exists('curl_init'))
1440
			{
1441
				$this->errstr='CURL unavailable on this install';
1442
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1443
				return $r;
1444
			}
1445
			if($method == 'https')
1446
			{
1447
				if(($info = curl_version()) &&
1448
					((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1449
				{
1450
					$this->errstr='SSL unavailable on this install';
1451
					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1452
					return $r;
1453
				}
1454
			}
1455
1456
			if($port == 0)
1457
			{
1458
				if($method == 'http')
1459
				{
1460
					$port = 80;
1461
				}
1462
				else
1463
				{
1464
					$port = 443;
1465
				}
1466
			}
1467
1468
			// Only create the payload if it was not created previously
1469
			if(empty($msg->payload))
1470
			{
1471
				$msg->createPayload($this->request_charset_encoding);
1472
			}
1473
1474
			// Deflate request body and set appropriate request headers
1475
			$payload = $msg->payload;
1476 View Code Duplication
			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1477
			{
1478
				if($this->request_compression == 'gzip')
1479
				{
1480
					$a = @gzencode($payload);
1481
					if($a)
1482
					{
1483
						$payload = $a;
1484
						$encoding_hdr = 'Content-Encoding: gzip';
1485
					}
1486
				}
1487
				else
1488
				{
1489
					$a = @gzcompress($payload);
1490
					if($a)
1491
					{
1492
						$payload = $a;
1493
						$encoding_hdr = 'Content-Encoding: deflate';
1494
					}
1495
				}
1496
			}
1497
			else
1498
			{
1499
				$encoding_hdr = '';
1500
			}
1501
1502
			if($this->debug > 1)
1503
			{
1504
				print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1505
				// let the client see this now in case http times out...
1506
				flush();
1507
			}
1508
1509
			if(!$keepalive || !$this->xmlrpc_curl_handle)
1510
			{
1511
				$curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1512
				if($keepalive)
1513
				{
1514
					$this->xmlrpc_curl_handle = $curl;
1515
				}
1516
			}
1517
			else
1518
			{
1519
				$curl = $this->xmlrpc_curl_handle;
1520
			}
1521
1522
			// results into variable
1523
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1524
1525
			if($this->debug)
1526
			{
1527
				curl_setopt($curl, CURLOPT_VERBOSE, 1);
1528
			}
1529
			curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1530
			// required for XMLRPC: post the data
1531
			curl_setopt($curl, CURLOPT_POST, 1);
1532
			// the data
1533
			curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1534
1535
			// return the header too
1536
			curl_setopt($curl, CURLOPT_HEADER, 1);
1537
1538
			// will only work with PHP >= 5.0
1539
			// NB: if we set an empty string, CURL will add http header indicating
1540
			// ALL methods it is supporting. This is possibly a better option than
1541
			// letting the user tell what curl can / cannot do...
1542
			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1543
			{
1544
				//curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1545
				// empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1546
				if (count($this->accepted_compression) == 1)
1547
				{
1548
					curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1549
				}
1550
				else
1551
					curl_setopt($curl, CURLOPT_ENCODING, '');
1552
			}
1553
			// extra headers
1554
			$headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1555
			// if no keepalive is wanted, let the server know it in advance
1556
			if(!$keepalive)
1557
			{
1558
				$headers[] = 'Connection: close';
1559
			}
1560
			// request compression header
1561
			if($encoding_hdr)
1562
			{
1563
				$headers[] = $encoding_hdr;
0 ignored issues
show
Bug introduced by
The variable $encoding_hdr does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1564
			}
1565
1566
			curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1567
			// timeout is borked
1568
			if($timeout)
1569
			{
1570
				curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1571
			}
1572
1573 View Code Duplication
			if($username && $password)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1574
			{
1575
				curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1576
				if (defined('CURLOPT_HTTPAUTH'))
1577
				{
1578
					curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1579
				}
1580
				else if ($authtype != 1)
1581
				{
1582
					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1583
				}
1584
			}
1585
1586
			if($method == 'https')
1587
			{
1588
				// set cert file
1589
				if($cert)
1590
				{
1591
					curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1592
				}
1593
				// set cert password
1594
				if($certpass)
1595
				{
1596
					curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1597
				}
1598
				// whether to verify remote host's cert
1599
				curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1600
				// set ca certificates file/dir
1601
				if($cacert)
1602
				{
1603
					curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1604
				}
1605
				if($cacertdir)
1606
				{
1607
					curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1608
				}
1609
				// set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1610
				if($key)
1611
				{
1612
					curl_setopt($curl, CURLOPT_SSLKEY, $key);
1613
				}
1614
				// set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1615
				if($keypass)
1616
				{
1617
					curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1618
				}
1619
				// whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
1620
				curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1621
			}
1622
1623
			// proxy info
1624
			if($proxyhost)
1625
			{
1626
				if($proxyport == 0)
1627
				{
1628
					$proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1629
				}
1630
				curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1631
				//curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1632 View Code Duplication
				if($proxyusername)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1633
				{
1634
					curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1635
					if (defined('CURLOPT_PROXYAUTH'))
1636
					{
1637
						curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1638
					}
1639
					else if ($proxyauthtype != 1)
1640
					{
1641
						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1642
					}
1643
				}
1644
			}
1645
1646
			// NB: should we build cookie http headers by hand rather than let CURL do it?
1647
			// the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1648
			// set to client obj the the user...
1649
			if (count($this->cookies))
1650
			{
1651
				$cookieheader = '';
1652
				foreach ($this->cookies as $name => $cookie)
1653
				{
1654
					$cookieheader .= $name . '=' . $cookie['value'] . '; ';
1655
				}
1656
				curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1657
			}
1658
1659
			foreach ($this->extracurlopts as $opt => $val)
1660
			{
1661
				curl_setopt($curl, $opt, $val);
1662
			}
1663
1664
			$result = curl_exec($curl);
1665
1666
			if ($this->debug > 1)
1667
			{
1668
				print "<PRE>\n---CURL INFO---\n";
1669
				foreach(curl_getinfo($curl) as $name => $val)
1670
					 print $name . ': ' . htmlentities($val). "\n";
1671
				print "---END---\n</PRE>";
1672
			}
1673
1674
			if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1675
			{
1676
				$this->errstr='no response';
1677
				$resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1678
				curl_close($curl);
1679
				if($keepalive)
1680
				{
1681
					$this->xmlrpc_curl_handle = null;
1682
				}
1683
			}
1684
			else
1685
			{
1686
				if(!$keepalive)
1687
				{
1688
					curl_close($curl);
1689
				}
1690
				$resp =& $msg->parseResponse($result, true, $this->return_type);
1691
			}
1692
			return $resp;
1693
		}
1694
1695
		/**
1696
		* Send an array of request messages and return an array of responses.
1697
		* Unless $this->no_multicall has been set to true, it will try first
1698
		* to use one single xmlrpc call to server method system.multicall, and
1699
		* revert to sending many successive calls in case of failure.
1700
		* This failure is also stored in $this->no_multicall for subsequent calls.
1701
		* Unfortunately, there is no server error code universally used to denote
1702
		* the fact that multicall is unsupported, so there is no way to reliably
1703
		* distinguish between that and a temporary failure.
1704
		* If you are sure that server supports multicall and do not want to
1705
		* fallback to using many single calls, set the fourth parameter to FALSE.
1706
		*
1707
		* NB: trying to shoehorn extra functionality into existing syntax has resulted
1708
		* in pretty much convoluted code...
1709
		*
1710
		* @param array $msgs an array of xmlrpcmsg objects
1711
		* @param integer $timeout connection timeout (in seconds)
1712
		* @param string $method the http protocol variant to be used
1713
		* @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1714
		* @return array
1715
		* @access public
1716
		*/
1717
		function multicall($msgs, $timeout=0, $method='', $fallback=true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1718
		{
1719
			if ($method == '')
1720
			{
1721
				$method = $this->method;
1722
			}
1723
			if(!$this->no_multicall)
1724
			{
1725
				$results = $this->_try_multicall($msgs, $timeout, $method);
1726
				if(is_array($results))
1727
				{
1728
					// System.multicall succeeded
1729
					return $results;
1730
				}
1731
				else
1732
				{
1733
					// either system.multicall is unsupported by server,
1734
					// or call failed for some other reason.
1735
					if ($fallback)
1736
					{
1737
						// Don't try it next time...
1738
						$this->no_multicall = true;
1739
					}
1740
					else
1741
					{
1742
						if (is_a($results, 'xmlrpcresp'))
1743
						{
1744
							$result = $results;
1745
						}
1746
						else
1747
						{
1748
							$result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1749
						}
1750
					}
1751
				}
1752
			}
1753
			else
1754
			{
1755
				// override fallback, in case careless user tries to do two
1756
				// opposite things at the same time
1757
				$fallback = true;
1758
			}
1759
1760
			$results = array();
1761
			if ($fallback)
1762
			{
1763
				// system.multicall is (probably) unsupported by server:
1764
				// emulate multicall via multiple requests
1765
				foreach($msgs as $msg)
1766
				{
1767
					$results[] =& $this->send($msg, $timeout, $method);
1768
				}
1769
			}
1770
			else
1771
			{
1772
				// user does NOT want to fallback on many single calls:
1773
				// since we should always return an array of responses,
1774
				// return an array with the same error repeated n times
1775
				foreach($msgs as $msg)
1776
				{
1777
					$results[] = $result;
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1778
				}
1779
			}
1780
			return $results;
1781
		}
1782
1783
		/**
1784
		* Attempt to boxcar $msgs via system.multicall.
1785
		* Returns either an array of xmlrpcreponses, an xmlrpc error response
1786
		* or false (when received response does not respect valid multicall syntax)
1787
		* @access private
1788
		*/
1789
		function _try_multicall($msgs, $timeout, $method)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1790
		{
1791
			// Construct multicall message
1792
			$calls = array();
1793
			foreach($msgs as $msg)
1794
			{
1795
				$call['methodName'] = new xmlrpcval($msg->method(),'string');
0 ignored issues
show
Coding Style Comprehensibility introduced by
$call was never initialized. Although not strictly required by PHP, it is generally a good practice to add $call = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1796
				$numParams = $msg->getNumParams();
1797
				$params = array();
1798
				for($i = 0; $i < $numParams; $i++)
1799
				{
1800
					$params[$i] = $msg->getParam($i);
1801
				}
1802
				$call['params'] = new xmlrpcval($params, 'array');
0 ignored issues
show
Documentation introduced by
$params is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1803
				$calls[] = new xmlrpcval($call, 'struct');
0 ignored issues
show
Documentation introduced by
$call is of type array<string,object<xmlr...":"object<xmlrpcval>"}>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1804
			}
1805
			$multicall = new xmlrpcmsg('system.multicall');
1806
			$multicall->addParam(new xmlrpcval($calls, 'array'));
0 ignored issues
show
Documentation introduced by
$calls is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1807
1808
			// Attempt RPC call
1809
			$result =& $this->send($multicall, $timeout, $method);
1810
1811
			if($result->faultCode() != 0)
1812
			{
1813
				// call to system.multicall failed
1814
				return $result;
1815
			}
1816
1817
			// Unpack responses.
1818
			$rets = $result->value();
1819
1820
			if ($this->return_type == 'xml')
1821
			{
1822
					return $rets;
1823
			}
1824
			else if ($this->return_type == 'phpvals')
1825
			{
1826
				///@todo test this code branch...
1827
				$rets = $result->value();
1828
				if(!is_array($rets))
1829
				{
1830
					return false;		// bad return type from system.multicall
1831
				}
1832
				$numRets = count($rets);
1833
				if($numRets != count($msgs))
1834
				{
1835
					return false;		// wrong number of return values.
1836
				}
1837
1838
				$response = array();
1839
				for($i = 0; $i < $numRets; $i++)
1840
				{
1841
					$val = $rets[$i];
1842
					if (!is_array($val)) {
1843
						return false;
1844
					}
1845
					switch(count($val))
1846
					{
1847
						case 1:
1848
							if(!isset($val[0]))
1849
							{
1850
								return false;		// Bad value
1851
							}
1852
							// Normal return value
1853
							$response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1854
							break;
1855
						case 2:
1856
							///	@todo remove usage of @: it is apparently quite slow
1857
							$code = @$val['faultCode'];
1858
							if(!is_int($code))
1859
							{
1860
								return false;
1861
							}
1862
							$str = @$val['faultString'];
1863
							if(!is_string($str))
1864
							{
1865
								return false;
1866
							}
1867
							$response[$i] = new xmlrpcresp(0, $code, $str);
1868
							break;
1869
						default:
1870
							return false;
1871
					}
1872
				}
1873
				return $response;
1874
			}
1875
			else // return type == 'xmlrpcvals'
1876
			{
1877
				$rets = $result->value();
1878
				if($rets->kindOf() != 'array')
0 ignored issues
show
Bug introduced by
The method kindOf cannot be called on $rets (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1879
				{
1880
					return false;		// bad return type from system.multicall
1881
				}
1882
				$numRets = $rets->arraysize();
0 ignored issues
show
Bug introduced by
The method arraysize cannot be called on $rets (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1883
				if($numRets != count($msgs))
1884
				{
1885
					return false;		// wrong number of return values.
1886
				}
1887
1888
				$response = array();
1889
				for($i = 0; $i < $numRets; $i++)
1890
				{
1891
					$val = $rets->arraymem($i);
0 ignored issues
show
Bug introduced by
The method arraymem cannot be called on $rets (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1892
					switch($val->kindOf())
1893
					{
1894
						case 'array':
1895
							if($val->arraysize() != 1)
1896
							{
1897
								return false;		// Bad value
1898
							}
1899
							// Normal return value
1900
							$response[$i] = new xmlrpcresp($val->arraymem(0));
1901
							break;
1902
						case 'struct':
1903
							$code = $val->structmem('faultCode');
1904
							if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1905
							{
1906
								return false;
1907
							}
1908
							$str = $val->structmem('faultString');
1909
							if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1910
							{
1911
								return false;
1912
							}
1913
							$response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1914
							break;
1915
						default:
1916
							return false;
1917
					}
1918
				}
1919
				return $response;
1920
			}
1921
		}
1922
	} // end class xmlrpc_client
1923
1924
	class xmlrpcresp
1925
	{
1926
		var $val = 0;
1927
		var $valtyp;
1928
		var $errno = 0;
1929
		var $errstr = '';
1930
		var $payload;
1931
		var $hdrs = array();
1932
		var $_cookies = array();
1933
		var $content_type = 'text/xml';
1934
		var $raw_data = '';
1935
1936
		/**
1937
		* @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1938
		* @param integer $fcode set it to anything but 0 to create an error response
1939
		* @param string $fstr the error string, in case of an error response
1940
		* @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1941
		*
1942
		* @todo add check that $val / $fcode / $fstr is of correct type???
1943
		* NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1944
		* php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1945
		*/
1946
		function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1947
		{
1948
			if($fcode != 0)
1949
			{
1950
				// error response
1951
				$this->errno = $fcode;
1952
				$this->errstr = $fstr;
1953
				//$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1954
			}
1955
			else
1956
			{
1957
				// successful response
1958
				$this->val = $val;
1959
				if ($valtyp == '')
1960
				{
1961
					// user did not declare type of response value: try to guess it
1962
					if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1963
					{
1964
						$this->valtyp = 'xmlrpcvals';
1965
					}
1966
					else if (is_string($this->val))
1967
					{
1968
						$this->valtyp = 'xml';
1969
1970
					}
1971
					else
1972
					{
1973
						$this->valtyp = 'phpvals';
1974
					}
1975
				}
1976
				else
1977
				{
1978
					// user declares type of resp value: believe him
1979
					$this->valtyp = $valtyp;
1980
				}
1981
			}
1982
		}
1983
1984
		/**
1985
		* Returns the error code of the response.
1986
		* @return integer the error code of this response (0 for not-error responses)
1987
		* @access public
1988
		*/
1989
		function faultCode()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1990
		{
1991
			return $this->errno;
1992
		}
1993
1994
		/**
1995
		* Returns the error code of the response.
1996
		* @return string the error string of this response ('' for not-error responses)
1997
		* @access public
1998
		*/
1999
		function faultString()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2000
		{
2001
			return $this->errstr;
2002
		}
2003
2004
		/**
2005
		* Returns the value received by the server.
2006
		* @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
2007
		* @access public
2008
		*/
2009
		function value()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2010
		{
2011
			return $this->val;
2012
		}
2013
2014
		/**
2015
		* Returns an array with the cookies received from the server.
2016
		* Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2017
		* with attributes being e.g. 'expires', 'path', domain'.
2018
		* NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2019
		* are still present in the array. It is up to the user-defined code to decide
2020
		* how to use the received cookies, and wheter they have to be sent back with the next
2021
		* request to the server (using xmlrpc_client::setCookie) or not
2022
		* @return array array of cookies received from the server
2023
		* @access public
2024
		*/
2025
		function cookies()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2026
		{
2027
			return $this->_cookies;
2028
		}
2029
2030
		/**
2031
		* Returns xml representation of the response. XML prologue not included
2032
		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2033
		* @return string the xml representation of the response
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2034
		* @access public
2035
		*/
2036
		function serialize($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2037
		{
2038
			if ($charset_encoding != '')
2039
				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2040
			else
2041
				$this->content_type = 'text/xml';
2042
			$result = "<methodResponse>\n";
2043
			if($this->errno)
2044
			{
2045
				// G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2046
				// by xml-encoding non ascii chars
2047
				$result .= "<fault>\n" .
2048
"<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2049
"</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2050
xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2051
"</struct>\n</value>\n</fault>";
2052
			}
2053
			else
2054
			{
2055
				if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2056
				{
2057
					if (is_string($this->val) && $this->valtyp == 'xml')
2058
					{
2059
						$result .= "<params>\n<param>\n" .
2060
							$this->val .
2061
							"</param>\n</params>";
2062
					}
2063
					else
2064
					{
2065
						/// @todo try to build something serializable?
2066
						die('cannot serialize xmlrpcresp objects whose content is native php values');
2067
					}
2068
				}
2069
				else
2070
				{
2071
					$result .= "<params>\n<param>\n" .
2072
						$this->val->serialize($charset_encoding) .
2073
						"</param>\n</params>";
2074
				}
2075
			}
2076
			$result .= "\n</methodResponse>";
2077
			$this->payload = $result;
2078
			return $result;
2079
		}
2080
	}
2081
2082
	class xmlrpcmsg
2083
	{
2084
		var $payload;
2085
		var $methodname;
2086
		var $params=array();
2087
		var $debug=0;
2088
		var $content_type = 'text/xml';
2089
2090
		/**
2091
		* @param string $meth the name of the method to invoke
2092
		* @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pars not be integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2093
		*/
2094
		function xmlrpcmsg($meth, $pars=0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2095
		{
2096
			$this->methodname=$meth;
2097
			if(is_array($pars) && count($pars)>0)
2098
			{
2099
				for($i=0; $i<count($pars); $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...
2100
				{
2101
					$this->addParam($pars[$i]);
2102
				}
2103
			}
2104
		}
2105
2106
		/**
2107
		* @access private
2108
		*/
2109
		function xml_header($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2110
		{
2111
			if ($charset_encoding != '')
2112
			{
2113
				return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2114
			}
2115
			else
2116
			{
2117
				return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2118
			}
2119
		}
2120
2121
		/**
2122
		* @access private
2123
		*/
2124
		function xml_footer()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2125
		{
2126
			return '</methodCall>';
2127
		}
2128
2129
		/**
2130
		* @access private
2131
		*/
2132
		function kindOf()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2133
		{
2134
			return 'msg';
2135
		}
2136
2137
		/**
2138
		* @access private
2139
		*/
2140
		function createPayload($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2141
		{
2142
			if ($charset_encoding != '')
2143
				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2144
			else
2145
				$this->content_type = 'text/xml';
2146
			$this->payload=$this->xml_header($charset_encoding);
2147
			$this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2148
			$this->payload.="<params>\n";
2149
			for($i=0; $i<count($this->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...
2150
			{
2151
				$p=$this->params[$i];
2152
				$this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2153
				"</param>\n";
2154
			}
2155
			$this->payload.="</params>\n";
2156
			$this->payload.=$this->xml_footer();
2157
		}
2158
2159
		/**
2160
		* Gets/sets the xmlrpc method to be invoked
2161
		* @param string $meth the method to be set (leave empty not to set it)
2162
		* @return string the method that will be invoked
2163
		* @access public
2164
		*/
2165
		function method($meth='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2166
		{
2167
			if($meth!='')
2168
			{
2169
				$this->methodname=$meth;
2170
			}
2171
			return $this->methodname;
2172
		}
2173
2174
		/**
2175
		* Returns xml representation of the message. XML prologue included
2176
		* @return string the xml representation of the message, xml prologue included
2177
		* @access public
2178
		*/
2179
		function serialize($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2180
		{
2181
			$this->createPayload($charset_encoding);
2182
			return $this->payload;
2183
		}
2184
2185
		/**
2186
		* Add a parameter to the list of parameters to be used upon method invocation
2187
		* @param xmlrpcval $par
2188
		* @return boolean false on failure
2189
		* @access public
2190
		*/
2191
		function addParam($par)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2192
		{
2193
			// add check: do not add to self params which are not xmlrpcvals
2194
			if(is_object($par) && is_a($par, 'xmlrpcval'))
2195
			{
2196
				$this->params[]=$par;
2197
				return true;
2198
			}
2199
			else
2200
			{
2201
				return false;
2202
			}
2203
		}
2204
2205
		/**
2206
		* Returns the nth parameter in the message. The index zero-based.
2207
		* @param integer $i the index of the parameter to fetch (zero based)
2208
		* @return xmlrpcval the i-th parameter
2209
		* @access public
2210
		*/
2211
		function getParam($i) { return $this->params[$i]; }
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2212
2213
		/**
2214
		* Returns the number of parameters in the messge.
2215
		* @return integer the number of parameters currently set
2216
		* @access public
2217
		*/
2218
		function getNumParams() { return count($this->params); }
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2219
2220
		/**
2221
		* Given an open file handle, read all data available and parse it as axmlrpc response.
2222
		* NB: the file handle is not closed by this function.
2223
		* NNB: might have trouble in rare cases to work on network streams, as we
2224
		*      check for a read of 0 bytes instead of feof($fp).
2225
		*      But since checking for feof(null) returns false, we would risk an
2226
		*      infinite loop in that case, because we cannot trust the caller
2227
		*      to give us a valid pointer to an open file...
2228
		* @access public
2229
		* @return xmlrpcresp
2230
		* @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2231
		*/
2232
		function &parseResponseFile($fp)
2233
		{
2234
			$ipd='';
2235
			while($data=fread($fp, 32768))
2236
			{
2237
				$ipd.=$data;
2238
			}
2239
			//fclose($fp);
2240
			$r =& $this->parseResponse($ipd);
2241
			return $r;
2242
		}
2243
2244
		/**
2245
		* Parses HTTP headers and separates them from data.
2246
		* @access private
2247
		*/
2248
		function &parseResponseHeaders(&$data, $headers_processed=false)
2249
		{
2250
				// Support "web-proxy-tunelling" connections for https through proxies
2251
				if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2252
				{
2253
					// Look for CR/LF or simple LF as line separator,
2254
					// (even though it is not valid http)
2255
					$pos = strpos($data,"\r\n\r\n");
2256 View Code Duplication
					if($pos || is_int($pos))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2257
					{
2258
						$bd = $pos+4;
2259
					}
2260
					else
2261
					{
2262
						$pos = strpos($data,"\n\n");
2263
						if($pos || is_int($pos))
2264
						{
2265
							$bd = $pos+2;
2266
						}
2267
						else
2268
						{
2269
							// No separation between response headers and body: fault?
2270
							$bd = 0;
2271
						}
2272
					}
2273 View Code Duplication
					if ($bd)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2274
					{
2275
						// this filters out all http headers from proxy.
2276
						// maybe we could take them into account, too?
2277
						$data = substr($data, $bd);
2278
					}
2279
					else
2280
					{
2281
						error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2282
						$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2283
						return $r;
2284
					}
2285
				}
2286
2287
				// Strip HTTP 1.1 100 Continue header if present
2288
				while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2289
				{
2290
					$pos = strpos($data, 'HTTP', 12);
2291
					// server sent a Continue header without any (valid) content following...
2292
					// give the client a chance to know it
2293
					if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2294
					{
2295
						break;
2296
					}
2297
					$data = substr($data, $pos);
2298
				}
2299
				if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2300
				{
2301
					$errstr= substr($data, 0, strpos($data, "\n")-1);
2302
					error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2303
					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2304
					return $r;
2305
				}
2306
2307
				$GLOBALS['_xh']['headers'] = array();
2308
				$GLOBALS['_xh']['cookies'] = array();
2309
2310
				// be tolerant to usage of \n instead of \r\n to separate headers and data
2311
				// (even though it is not valid http)
2312
				$pos = strpos($data,"\r\n\r\n");
2313 View Code Duplication
				if($pos || is_int($pos))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2314
				{
2315
					$bd = $pos+4;
2316
				}
2317
				else
2318
				{
2319
					$pos = strpos($data,"\n\n");
2320
					if($pos || is_int($pos))
2321
					{
2322
						$bd = $pos+2;
2323
					}
2324
					else
2325
					{
2326
						// No separation between response headers and body: fault?
2327
						// we could take some action here instead of going on...
2328
						$bd = 0;
2329
					}
2330
				}
2331
				// be tolerant to line endings, and extra empty lines
2332
				$ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2333
				while(list(,$line) = @each($ar))
2334
				{
2335
					// take care of multi-line headers and cookies
2336
					$arr = explode(':',$line,2);
2337
					if(count($arr) > 1)
2338
					{
2339
						$header_name = strtolower(trim($arr[0]));
2340
						/// @todo some other headers (the ones that allow a CSV list of values)
2341
						/// do allow many values to be passed using multiple header lines.
2342
						/// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2343
						/// instead of replacing it for those...
2344
						if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2345
						{
2346
							if ($header_name == 'set-cookie2')
2347
							{
2348
								// version 2 cookies:
2349
								// there could be many cookies on one line, comma separated
2350
								$cookies = explode(',', $arr[1]);
2351
							}
2352
							else
2353
							{
2354
								$cookies = array($arr[1]);
2355
							}
2356
							foreach ($cookies as $cookie)
2357
							{
2358
								// glue together all received cookies, using a comma to separate them
2359
								// (same as php does with getallheaders())
2360
								if (isset($GLOBALS['_xh']['headers'][$header_name]))
2361
									$GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2362
								else
2363
									$GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2364
								// parse cookie attributes, in case user wants to correctly honour them
2365
								// feature creep: only allow rfc-compliant cookie attributes?
2366
								// @todo support for server sending multiple time cookie with same name, but using different PATHs
2367
								$cookie = explode(';', $cookie);
2368
								foreach ($cookie as $pos => $val)
2369
								{
2370
									$val = explode('=', $val, 2);
2371
									$tag = trim($val[0]);
2372
									$val = trim(@$val[1]);
2373
									/// @todo with version 1 cookies, we should strip leading and trailing " chars
2374
									if ($pos == 0)
2375
									{
2376
										$cookiename = $tag;
2377
										$GLOBALS['_xh']['cookies'][$tag] = array();
2378
										$GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2379
									}
2380
									else
2381
									{
2382
										if ($tag != 'value')
2383
										{
2384
										  $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
0 ignored issues
show
Bug introduced by
The variable $cookiename does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2385
										}
2386
									}
2387
								}
2388
							}
2389
						}
2390
						else
2391
						{
2392
							$GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2393
						}
2394
					}
2395
					elseif(isset($header_name))
2396
					{
2397
						///	@todo version1 cookies might span multiple lines, thus breaking the parsing above
2398
						$GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2399
					}
2400
				}
2401
2402
				$data = substr($data, $bd);
2403
2404
				if($this->debug && count($GLOBALS['_xh']['headers']))
2405
				{
2406
					print '<PRE>';
2407
					foreach($GLOBALS['_xh']['headers'] as $header => $value)
2408
					{
2409
						print htmlentities("HEADER: $header: $value\n");
2410
					}
2411
					foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2412
					{
2413
						print htmlentities("COOKIE: $header={$value['value']}\n");
2414
					}
2415
					print "</PRE>\n";
2416
				}
2417
2418
				// if CURL was used for the call, http headers have been processed,
2419
				// and dechunking + reinflating have been carried out
2420
				if(!$headers_processed)
2421
				{
2422
					// Decode chunked encoding sent by http 1.1 servers
2423
					if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2424
					{
2425 View Code Duplication
						if(!$data = decode_chunked($data))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2426
						{
2427
							error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2428
							$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2429
							return $r;
2430
						}
2431
					}
2432
2433
					// Decode gzip-compressed stuff
2434
					// code shamelessly inspired from nusoap library by Dietrich Ayala
2435
					if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2436
					{
2437
						$GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2438
						if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2439
						{
2440
							// if decoding works, use it. else assume data wasn't gzencoded
2441
							if(function_exists('gzinflate'))
2442
							{
2443
								if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2444
								{
2445
									$data = $degzdata;
2446 View Code Duplication
									if($this->debug)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2447
									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2448
								}
2449
								elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2450
								{
2451
									$data = $degzdata;
2452 View Code Duplication
									if($this->debug)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2453
									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2454
								}
2455
								else
2456
								{
2457
									error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2458
									$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2459
									return $r;
2460
								}
2461
							}
2462
							else
2463
							{
2464
								error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2465
								$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2466
								return $r;
2467
							}
2468
						}
2469
					}
2470
				} // end of 'if needed, de-chunk, re-inflate response'
2471
2472
				// real stupid hack to avoid PHP complaining about returning NULL by ref
2473
				$r = null;
2474
				$r =& $r;
2475
				return $r;
2476
		}
2477
2478
		/**
2479
		* Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2480
		* @param string $data the xmlrpc response, eventually including http headers
2481
		* @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2482
		* @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2483
		* @return xmlrpcresp
2484
		* @access public
2485
		*/
2486
		function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2487
		{
2488
			if($this->debug)
2489
			{
2490
				//by maHo, replaced htmlspecialchars with htmlentities
2491
				print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2492
			}
2493
2494
			if($data == '')
2495
			{
2496
				error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2497
				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2498
				return $r;
2499
			}
2500
2501
			$GLOBALS['_xh']=array();
2502
2503
			$raw_data = $data;
2504
			// parse the HTTP headers of the response, if present, and separate them from data
2505
			if(substr($data, 0, 4) == 'HTTP')
2506
			{
2507
				$r =& $this->parseResponseHeaders($data, $headers_processed);
2508
				if ($r)
2509
				{
2510
					// failed processing of HTTP response headers
2511
					// save into response obj the full payload received, for debugging
2512
					$r->raw_data = $data;
2513
					return $r;
2514
				}
2515
			}
2516
			else
2517
			{
2518
				$GLOBALS['_xh']['headers'] = array();
2519
				$GLOBALS['_xh']['cookies'] = array();
2520
			}
2521
2522
			if($this->debug)
2523
			{
2524
				$start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2525
				if ($start)
2526
				{
2527
					$start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2528
					$end = strpos($data, '-->', $start);
2529
					$comments = substr($data, $start, $end-$start);
2530
					print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2531
				}
2532
			}
2533
2534
			// be tolerant of extra whitespace in response body
2535
			$data = trim($data);
2536
2537
			/// @todo return an error msg if $data=='' ?
2538
2539
			// be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2540
			// idea from Luca Mariano <[email protected]> originally in PEARified version of the lib
2541
			$pos = strrpos($data, '</methodResponse>');
2542
			if($pos !== false)
2543
			{
2544
				$data = substr($data, 0, $pos+17);
2545
			}
2546
2547
			// if user wants back raw xml, give it to him
2548
			if ($return_type == 'xml')
2549
			{
2550
				$r = new xmlrpcresp($data, 0, '', 'xml');
2551
				$r->hdrs = $GLOBALS['_xh']['headers'];
2552
				$r->_cookies = $GLOBALS['_xh']['cookies'];
2553
				$r->raw_data = $raw_data;
2554
				return $r;
2555
			}
2556
2557
			// try to 'guestimate' the character encoding of the received response
2558
			$resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2559
2560
			$GLOBALS['_xh']['ac']='';
2561
			//$GLOBALS['_xh']['qt']=''; //unused...
2562
			$GLOBALS['_xh']['stack'] = array();
2563
			$GLOBALS['_xh']['valuestack'] = array();
2564
			$GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2565
			$GLOBALS['_xh']['isf_reason']='';
2566
			$GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2567
2568
			// if response charset encoding is not known / supported, try to use
2569
			// the default encoding and parse the xml anyway, but log a warning...
2570 View Code Duplication
			if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2571
			// the following code might be better for mb_string enabled installs, but
2572
			// makes the lib about 200% slower...
2573
			//if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2574
			{
2575
				error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received response: '.$resp_encoding);
2576
				$resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2577
			}
2578
			$parser = xml_parser_create($resp_encoding);
2579
			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2580
			// G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2581
			// the xml parser to give us back data in the expected charset.
2582
			// What if internal encoding is not in one of the 3 allowed?
2583
			// we use the broadest one, ie. utf8
2584
			// This allows to send data which is native in various charset,
2585
			// by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2586 View Code Duplication
			if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2587
			{
2588
				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2589
			}
2590
			else
2591
			{
2592
				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2593
			}
2594
2595
			if ($return_type == 'phpvals')
2596
			{
2597
				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2598
			}
2599
			else
2600
			{
2601
				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2602
			}
2603
2604
			xml_set_character_data_handler($parser, 'xmlrpc_cd');
2605
			xml_set_default_handler($parser, 'xmlrpc_dh');
2606
2607
			// first error check: xml not well formed
2608
			if(!xml_parse($parser, $data, count($data)))
2609
			{
2610
				// thanks to Peter Kocks <[email protected]>
2611
				if((xml_get_current_line_number($parser)) == 1)
2612
				{
2613
					$errstr = 'XML error at line 1, check URL';
2614
				}
2615
				else
2616
				{
2617
					$errstr = sprintf('XML error: %s at line %d, column %d',
2618
						xml_error_string(xml_get_error_code($parser)),
2619
						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2620
				}
2621
				error_log($errstr);
2622
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2623
				xml_parser_free($parser);
2624
				if($this->debug)
2625
				{
2626
					print $errstr;
2627
				}
2628
				$r->hdrs = $GLOBALS['_xh']['headers'];
2629
				$r->_cookies = $GLOBALS['_xh']['cookies'];
2630
				$r->raw_data = $raw_data;
2631
				return $r;
2632
			}
2633
			xml_parser_free($parser);
2634
			// second error check: xml well formed but not xml-rpc compliant
2635
			if ($GLOBALS['_xh']['isf'] > 1)
2636
			{
2637
				if ($this->debug)
2638
				{
2639
					/// @todo echo something for user?
2640
				}
2641
2642
				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2643
				$GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2644
			}
2645
			// third error check: parsing of the response has somehow gone boink.
2646
			// NB: shall we omit this check, since we trust the parsing code?
2647
			elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2648
			{
2649
				// something odd has happened
2650
				// and it's time to generate a client side error
2651
				// indicating something odd went on
2652
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2653
					$GLOBALS['xmlrpcstr']['invalid_return']);
2654
			}
2655
			else
2656
			{
2657
				if ($this->debug)
2658
				{
2659
					print "<PRE>---PARSED---\n";
2660
					// somehow htmlentities chokes on var_export, and some full html string...
2661
					//print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2662
					print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2663
					print "\n---END---</PRE>";
2664
				}
2665
2666
				// note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2667
				$v =& $GLOBALS['_xh']['value'];
2668
2669
				if($GLOBALS['_xh']['isf'])
2670
				{
2671
					/// @todo we should test here if server sent an int and a string,
2672
					/// and/or coerce them into such...
2673
					if ($return_type == 'xmlrpcvals')
2674
					{
2675
						$errno_v = $v->structmem('faultCode');
2676
						$errstr_v = $v->structmem('faultString');
2677
						$errno = $errno_v->scalarval();
2678
						$errstr = $errstr_v->scalarval();
2679
					}
2680
					else
2681
					{
2682
						$errno = $v['faultCode'];
2683
						$errstr = $v['faultString'];
2684
					}
2685
2686
					if($errno == 0)
2687
					{
2688
						// FAULT returned, errno needs to reflect that
2689
						$errno = -1;
2690
					}
2691
2692
					$r = new xmlrpcresp(0, $errno, $errstr);
2693
				}
2694
				else
2695
				{
2696
					$r=new xmlrpcresp($v, 0, '', $return_type);
2697
				}
2698
			}
2699
2700
			$r->hdrs = $GLOBALS['_xh']['headers'];
2701
			$r->_cookies = $GLOBALS['_xh']['cookies'];
2702
			$r->raw_data = $raw_data;
2703
			return $r;
2704
		}
2705
	}
2706
2707
	class xmlrpcval
2708
	{
2709
		var $me=array();
2710
		var $mytype=0;
2711
		var $_php_class=null;
2712
2713
		/**
2714
		* @param mixed $val
2715
		* @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2716
		*/
2717
		function xmlrpcval($val=-1, $type='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2718
		{
2719
			/// @todo: optimization creep - do not call addXX, do it all inline.
2720
			/// downside: booleans will not be coerced anymore
2721
			if($val!==-1 || $type!='')
2722
			{
2723
				// optimization creep: inlined all work done by constructor
2724
				switch($type)
2725
				{
2726
					case '':
2727
						$this->mytype=1;
2728
						$this->me['string']=$val;
2729
						break;
2730
					case 'i4':
2731
					case 'int':
2732
					case 'double':
2733
					case 'string':
2734
					case 'boolean':
2735
					case 'dateTime.iso8601':
2736
					case 'base64':
2737
					case 'null':
2738
						$this->mytype=1;
2739
						$this->me[$type]=$val;
2740
						break;
2741
					case 'array':
2742
						$this->mytype=2;
2743
						$this->me['array']=$val;
2744
						break;
2745
					case 'struct':
2746
						$this->mytype=3;
2747
						$this->me['struct']=$val;
2748
						break;
2749
					default:
2750
						error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2751
				}
2752
				/*if($type=='')
2753
				{
2754
					$type='string';
2755
				}
2756
				if($GLOBALS['xmlrpcTypes'][$type]==1)
2757
				{
2758
					$this->addScalar($val,$type);
2759
				}
2760
				elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2761
				{
2762
					$this->addArray($val);
2763
				}
2764
				elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2765
				{
2766
					$this->addStruct($val);
2767
				}*/
2768
			}
2769
		}
2770
2771
		/**
2772
		* Add a single php value to an (unitialized) xmlrpcval
2773
		* @param mixed $val
2774
		* @param string $type
2775
		* @return int 1 or 0 on failure
2776
		*/
2777
		function addScalar($val, $type='string')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2778
		{
2779
			$typeof=@$GLOBALS['xmlrpcTypes'][$type];
2780
			if($typeof!=1)
2781
			{
2782
				error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2783
				return 0;
2784
			}
2785
2786
			// coerce booleans into correct values
2787
			// NB: we should either do it for datetimes, integers and doubles, too,
2788
			// or just plain remove this check, implemented on booleans only...
2789
			if($type==$GLOBALS['xmlrpcBoolean'])
2790
			{
2791
				if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2792
				{
2793
					$val=true;
2794
				}
2795
				else
2796
				{
2797
					$val=false;
2798
				}
2799
			}
2800
2801
			switch($this->mytype)
2802
			{
2803
				case 1:
2804
					error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2805
					return 0;
2806
				case 3:
2807
					error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2808
					return 0;
2809
				case 2:
2810
					// we're adding a scalar value to an array here
2811
					//$ar=$this->me['array'];
2812
					//$ar[]=new xmlrpcval($val, $type);
2813
					//$this->me['array']=$ar;
2814
					// Faster (?) avoid all the costly array-copy-by-val done here...
2815
					$this->me['array'][]=new xmlrpcval($val, $type);
2816
					return 1;
2817
				default:
2818
					// a scalar, so set the value and remember we're scalar
2819
					$this->me[$type]=$val;
2820
					$this->mytype=$typeof;
2821
					return 1;
2822
			}
2823
		}
2824
2825
		/**
2826
		* Add an array of xmlrpcval objects to an xmlrpcval
2827
		* @param array $vals
2828
		* @return int 1 or 0 on failure
2829
		* @access public
2830
		*
2831
		* @todo add some checking for $vals to be an array of xmlrpcvals?
2832
		*/
2833 View Code Duplication
		function addArray($vals)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2834
		{
2835
			if($this->mytype==0)
2836
			{
2837
				$this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2838
				$this->me['array']=$vals;
2839
				return 1;
2840
			}
2841
			elseif($this->mytype==2)
2842
			{
2843
				// we're adding to an array here
2844
				$this->me['array'] = array_merge($this->me['array'], $vals);
2845
				return 1;
2846
			}
2847
			else
2848
			{
2849
				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2850
				return 0;
2851
			}
2852
		}
2853
2854
		/**
2855
		* Add an array of named xmlrpcval objects to an xmlrpcval
2856
		* @param array $vals
2857
		* @return int 1 or 0 on failure
2858
		* @access public
2859
		*
2860
		* @todo add some checking for $vals to be an array?
2861
		*/
2862 View Code Duplication
		function addStruct($vals)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2863
		{
2864
			if($this->mytype==0)
2865
			{
2866
				$this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2867
				$this->me['struct']=$vals;
2868
				return 1;
2869
			}
2870
			elseif($this->mytype==3)
2871
			{
2872
				// we're adding to a struct here
2873
				$this->me['struct'] = array_merge($this->me['struct'], $vals);
2874
				return 1;
2875
			}
2876
			else
2877
			{
2878
				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2879
				return 0;
2880
			}
2881
		}
2882
2883
		// poor man's version of print_r ???
2884
		// DEPRECATED!
2885
		function dump($ar)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2886
		{
2887
			foreach($ar as $key => $val)
2888
			{
2889
				echo "$key => $val<br />";
2890
				if($key == 'array')
2891
				{
2892
					while(list($key2, $val2) = each($val))
2893
					{
2894
						echo "-- $key2 => $val2<br />";
2895
					}
2896
				}
2897
			}
2898
		}
2899
2900
		/**
2901
		* Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2902
		* @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2903
		* @access public
2904
		*/
2905
		function kindOf()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2906
		{
2907
			switch($this->mytype)
2908
			{
2909
				case 3:
2910
					return 'struct';
2911
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2912
				case 2:
2913
					return 'array';
2914
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2915
				case 1:
2916
					return 'scalar';
2917
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2918
				default:
2919
					return 'undef';
2920
			}
2921
		}
2922
2923
		/**
2924
		* @access private
2925
		*/
2926
		function serializedata($typ, $val, $charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2927
		{
2928
			$rs='';
2929
			switch(@$GLOBALS['xmlrpcTypes'][$typ])
2930
			{
2931
				case 1:
2932
					switch($typ)
2933
					{
2934
						case $GLOBALS['xmlrpcBase64']:
2935
							$rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2936
							break;
2937
						case $GLOBALS['xmlrpcBoolean']:
2938
							$rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2939
							break;
2940
						case $GLOBALS['xmlrpcString']:
2941
							// G. Giunta 2005/2/13: do NOT use htmlentities, since
2942
							// it will produce named html entities, which are invalid xml
2943
							$rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2944
							break;
2945
						case $GLOBALS['xmlrpcInt']:
2946
						case $GLOBALS['xmlrpcI4']:
2947
							$rs.="<${typ}>".(int)$val."</${typ}>";
2948
							break;
2949
						case $GLOBALS['xmlrpcDouble']:
2950
							// avoid using standard conversion of float to string because it is locale-dependent,
2951
							// and also because the xmlrpc spec forbids exponential notation.
2952
							// sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
2953
							// The code below tries its best at keeping max precision while avoiding exp notation,
2954
							// but there is of course no limit in the number of decimal places to be used...
2955
							$rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
2956
							break;
2957
						case $GLOBALS['xmlrpcDateTime']:
2958
							if (is_string($val))
2959
							{
2960
								$rs.="<${typ}>${val}</${typ}>";
2961
							}
2962
							else if(is_a($val, 'DateTime'))
2963
							{
2964
								$rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
2965
							}
2966
							else if(is_int($val))
2967
							{
2968
								$rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
2969
							}
2970
							else
2971
							{
2972
								// not really a good idea here: but what shall we output anyway? left for backward compat...
2973
								$rs.="<${typ}>${val}</${typ}>";
2974
							}
2975
							break;
2976
						case $GLOBALS['xmlrpcNull']:
2977
							if ($GLOBALS['xmlrpc_null_apache_encoding'])
2978
							{
2979
								$rs.="<ex:nil/>";
2980
							}
2981
							else
2982
							{
2983
								$rs.="<nil/>";
2984
							}
2985
							break;
2986
						default:
2987
							// no standard type value should arrive here, but provide a possibility
2988
							// for xmlrpcvals of unknown type...
2989
							$rs.="<${typ}>${val}</${typ}>";
2990
					}
2991
					break;
2992
				case 3:
2993
					// struct
2994
					if ($this->_php_class)
2995
					{
2996
						$rs.='<struct php_class="' . $this->_php_class . "\">\n";
2997
					}
2998
					else
2999
					{
3000
						$rs.="<struct>\n";
3001
					}
3002
					foreach($val as $key2 => $val2)
3003
					{
3004
						$rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3005
						//$rs.=$this->serializeval($val2);
3006
						$rs.=$val2->serialize($charset_encoding);
3007
						$rs.="</member>\n";
3008
					}
3009
					$rs.='</struct>';
3010
					break;
3011
				case 2:
3012
					// array
3013
					$rs.="<array>\n<data>\n";
3014
					for($i=0; $i<count($val); $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...
3015
					{
3016
						//$rs.=$this->serializeval($val[$i]);
3017
						$rs.=$val[$i]->serialize($charset_encoding);
3018
					}
3019
					$rs.="</data>\n</array>";
3020
					break;
3021
				default:
3022
					break;
3023
			}
3024
			return $rs;
3025
		}
3026
3027
		/**
3028
		* Returns xml representation of the value. XML prologue not included
3029
		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3030
		* @return string
3031
		* @access public
3032
		*/
3033
		function serialize($charset_encoding='')
3034
		{
3035
			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3036
			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3037
			//{
3038
				reset($this->me);
3039
				list($typ, $val) = each($this->me);
3040
				return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3041
			//}
3042
		}
3043
3044
		// DEPRECATED
3045
		function serializeval($o)
3046
		{
3047
			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3048
			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3049
			//{
3050
				$ar=$o->me;
3051
				reset($ar);
3052
				list($typ, $val) = each($ar);
3053
				return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3054
			//}
3055
		}
3056
3057
		/**
3058
		* Checks wheter a struct member with a given name is present.
3059
		* Works only on xmlrpcvals of type struct.
3060
		* @param string $m the name of the struct member to be looked up
3061
		* @return boolean
3062
		* @access public
3063
		*/
3064
		function structmemexists($m)
3065
		{
3066
			return array_key_exists($m, $this->me['struct']);
3067
		}
3068
3069
		/**
3070
		* Returns the value of a given struct member (an xmlrpcval object in itself).
3071
		* Will raise a php warning if struct member of given name does not exist
3072
		* @param string $m the name of the struct member to be looked up
3073
		* @return xmlrpcval
3074
		* @access public
3075
		*/
3076
		function structmem($m)
3077
		{
3078
			return $this->me['struct'][$m];
3079
		}
3080
3081
		/**
3082
		* Reset internal pointer for xmlrpcvals of type struct.
3083
		* @access public
3084
		*/
3085
		function structreset()
3086
		{
3087
			reset($this->me['struct']);
3088
		}
3089
3090
		/**
3091
		* Return next member element for xmlrpcvals of type struct.
3092
		* @return xmlrpcval
0 ignored issues
show
Documentation introduced by
Should the return type not be array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3093
		* @access public
3094
		*/
3095
		function structeach()
3096
		{
3097
			return each($this->me['struct']);
3098
		}
3099
3100
		// DEPRECATED! this code looks like it is very fragile and has not been fixed
3101
		// for a long long time. Shall we remove it for 2.0?
3102
		function getval()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
3103
		{
3104
			// UNSTABLE
3105
			reset($this->me);
3106
			list($a,$b)=each($this->me);
0 ignored issues
show
Unused Code introduced by
The assignment to $a is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
3107
			// contributed by I Sofer, 2001-03-24
3108
			// add support for nested arrays to scalarval
3109
			// i've created a new method here, so as to
3110
			// preserve back compatibility
3111
3112
			if(is_array($b))
3113
			{
3114
				@reset($b);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3115
				while(list($id,$cont) = @each($b))
3116
				{
3117
					$b[$id] = $cont->scalarval();
3118
				}
3119
			}
3120
3121
			// add support for structures directly encoding php objects
3122
			if(is_object($b))
3123
			{
3124
				$t = get_object_vars($b);
3125
				@reset($t);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3126
				while(list($id,$cont) = @each($t))
3127
				{
3128
					$t[$id] = $cont->scalarval();
3129
				}
3130
				@reset($t);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3131
				while(list($id,$cont) = @each($t))
3132
				{
3133
					@$b->$id = $cont;
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3134
				}
3135
			}
3136
			// end contrib
3137
			return $b;
3138
		}
3139
3140
		/**
3141
		* Returns the value of a scalar xmlrpcval
3142
		* @return mixed
3143
		* @access public
3144
		*/
3145
		function scalarval()
3146
		{
3147
			reset($this->me);
3148
			list(,$b)=each($this->me);
3149
			return $b;
3150
		}
3151
3152
		/**
3153
		* Returns the type of the xmlrpcval.
3154
		* For integers, 'int' is always returned in place of 'i4'
3155
		* @return string
3156
		* @access public
3157
		*/
3158
		function scalartyp()
3159
		{
3160
			reset($this->me);
3161
			list($a,)=each($this->me);
3162
			if($a==$GLOBALS['xmlrpcI4'])
3163
			{
3164
				$a=$GLOBALS['xmlrpcInt'];
3165
			}
3166
			return $a;
3167
		}
3168
3169
		/**
3170
		* Returns the m-th member of an xmlrpcval of struct type
3171
		* @param integer $m the index of the value to be retrieved (zero based)
3172
		* @return xmlrpcval
3173
		* @access public
3174
		*/
3175
		function arraymem($m)
3176
		{
3177
			return $this->me['array'][$m];
3178
		}
3179
3180
		/**
3181
		* Returns the number of members in an xmlrpcval of array type
3182
		* @return integer
3183
		* @access public
3184
		*/
3185
		function arraysize()
3186
		{
3187
			return count($this->me['array']);
3188
		}
3189
3190
		/**
3191
		* Returns the number of members in an xmlrpcval of struct type
3192
		* @return integer
3193
		* @access public
3194
		*/
3195
		function structsize()
3196
		{
3197
			return count($this->me['struct']);
3198
		}
3199
	}
3200
3201
3202
	// date helpers
3203
3204
	/**
3205
	* Given a timestamp, return the corresponding ISO8601 encoded string.
3206
	*
3207
	* Really, timezones ought to be supported
3208
	* but the XML-RPC spec says:
3209
	*
3210
	* "Don't assume a timezone. It should be specified by the server in its
3211
	* documentation what assumptions it makes about timezones."
3212
	*
3213
	* These routines always assume localtime unless
3214
	* $utc is set to 1, in which case UTC is assumed
3215
	* and an adjustment for locale is made when encoding
3216
	*
3217
	* @param int $timet (timestamp)
3218
	* @param int $utc (0 or 1)
3219
	* @return string
3220
	*/
3221
	function iso8601_encode($timet, $utc=0)
3222
	{
3223
		if(!$utc)
3224
		{
3225
			$t=strftime("%Y%m%dT%H:%M:%S", $timet);
3226
		}
3227
		else
3228
		{
3229
			if(function_exists('gmstrftime'))
3230
			{
3231
				// gmstrftime doesn't exist in some versions
3232
				// of PHP
3233
				$t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3234
			}
3235
			else
3236
			{
3237
				$t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3238
			}
3239
		}
3240
		return $t;
3241
	}
3242
3243
	/**
3244
	* Given an ISO8601 date string, return a timet in the localtime, or UTC
3245
	* @param string $idate
3246
	* @param int $utc either 0 or 1
3247
	* @return int (datetime)
3248
	*/
3249
	function iso8601_decode($idate, $utc=0)
3250
	{
3251
		$t=0;
3252
		if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3253
		{
3254
			if($utc)
3255
			{
3256
				$t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3257
			}
3258
			else
3259
			{
3260
				$t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3261
			}
3262
		}
3263
		return $t;
3264
	}
3265
3266
	/**
3267
	* Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3268
	*
3269
	* Works with xmlrpc message objects as input, too.
3270
	*
3271
	* Given proper options parameter, can rebuild generic php object instances
3272
	* (provided those have been encoded to xmlrpc format using a corresponding
3273
	* option in php_xmlrpc_encode())
3274
	* PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3275
	* This means that the remote communication end can decide which php code will
3276
	* get executed on your server, leaving the door possibly open to 'php-injection'
3277
	* style of attacks (provided you have some classes defined on your server that
3278
	* might wreak havoc if instances are built outside an appropriate context).
3279
	* Make sure you trust the remote server/client before eanbling this!
3280
	*
3281
	* @author Dan Libby ([email protected])
3282
	*
3283
	* @param xmlrpcval $xmlrpc_val
3284
	* @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects (standard is
3285
	* @return mixed
3286
	*/
3287
	function php_xmlrpc_decode($xmlrpc_val, $options=array())
3288
	{
3289
		switch($xmlrpc_val->kindOf())
3290
		{
3291
			case 'scalar':
3292
				if (in_array('extension_api', $options))
3293
				{
3294
					reset($xmlrpc_val->me);
3295
					list($typ,$val) = each($xmlrpc_val->me);
3296
					switch ($typ)
3297
					{
3298
						case 'dateTime.iso8601':
3299
							$xmlrpc_val->scalar = $val;
0 ignored issues
show
Bug introduced by
The property scalar does not seem to exist in xmlrpcval.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3300
							$xmlrpc_val->xmlrpc_type = 'datetime';
0 ignored issues
show
Bug introduced by
The property xmlrpc_type does not seem to exist in xmlrpcval.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3301
							$xmlrpc_val->timestamp = iso8601_decode($val);
0 ignored issues
show
Bug introduced by
The property timestamp does not seem to exist in xmlrpcval.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3302
							return $xmlrpc_val;
3303
						case 'base64':
3304
							$xmlrpc_val->scalar = $val;
3305
							$xmlrpc_val->type = $typ;
0 ignored issues
show
Bug introduced by
The property type does not seem to exist in xmlrpcval.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3306
							return $xmlrpc_val;
3307
						default:
3308
							return $xmlrpc_val->scalarval();
3309
					}
3310
				}
3311
				if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3312
				{
3313
					// we return a Datetime object instead of a string
3314
					// since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3315
					// we cater to all 3 cases here
3316
					$out = $xmlrpc_val->scalarval();
3317
					if (is_string($out))
3318
					{
3319
						$out = strtotime($out);
3320
					}
3321
					if (is_int($out))
3322
					{
3323
						$result = new Datetime();
3324
						$result->setTimestamp($out);
3325
						return $result;
3326
					}
3327
					elseif (is_a($out, 'Datetime'))
3328
					{
3329
						return $out;
3330
					}
3331
				}
3332
				return $xmlrpc_val->scalarval();
3333
			case 'array':
3334
				$size = $xmlrpc_val->arraysize();
3335
				$arr = array();
3336
				for($i = 0; $i < $size; $i++)
3337
				{
3338
					$arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3339
				}
3340
				return $arr;
3341
			case 'struct':
3342
				$xmlrpc_val->structreset();
3343
				// If user said so, try to rebuild php objects for specific struct vals.
3344
				/// @todo should we raise a warning for class not found?
3345
				// shall we check for proper subclass of xmlrpcval instead of
3346
				// presence of _php_class to detect what we can do?
3347
				if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3348
					&& class_exists($xmlrpc_val->_php_class))
3349
				{
3350
					$obj = @new $xmlrpc_val->_php_class;
3351
					while(list($key,$value)=$xmlrpc_val->structeach())
3352
					{
3353
						$obj->$key = php_xmlrpc_decode($value, $options);
3354
					}
3355
					return $obj;
3356
				}
3357
				else
3358
				{
3359
					$arr = array();
3360
					while(list($key,$value)=$xmlrpc_val->structeach())
3361
					{
3362
						$arr[$key] = php_xmlrpc_decode($value, $options);
3363
					}
3364
					return $arr;
3365
				}
3366
			case 'msg':
3367
				$paramcount = $xmlrpc_val->getNumParams();
0 ignored issues
show
Bug introduced by
The method getNumParams() does not seem to exist on object<xmlrpcval>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3368
				$arr = array();
3369
				for($i = 0; $i < $paramcount; $i++)
3370
				{
3371
					$arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
0 ignored issues
show
Bug introduced by
The method getParam() does not seem to exist on object<xmlrpcval>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3372
				}
3373
				return $arr;
3374
			}
3375
	}
3376
3377
	// This constant left here only for historical reasons...
3378
	// it was used to decide if we have to define xmlrpc_encode on our own, but
3379
	// we do not do it anymore
3380
	if(function_exists('xmlrpc_decode'))
3381
	{
3382
		define('XMLRPC_EPI_ENABLED','1');
3383
	}
3384
	else
3385
	{
3386
		define('XMLRPC_EPI_ENABLED','0');
3387
	}
3388
3389
	/**
3390
	* Takes native php types and encodes them into xmlrpc PHP object format.
3391
	* It will not re-encode xmlrpcval objects.
3392
	*
3393
	* Feature creep -- could support more types via optional type argument
3394
	* (string => datetime support has been added, ??? => base64 not yet)
3395
	*
3396
	* If given a proper options parameter, php object instances will be encoded
3397
	* into 'special' xmlrpc values, that can later be decoded into php objects
3398
	* by calling php_xmlrpc_decode() with a corresponding option
3399
	*
3400
	* @author Dan Libby ([email protected])
3401
	*
3402
	* @param mixed $php_val the value to be converted into an xmlrpcval object
3403
	* @param array $options	can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3404
	* @return xmlrpcval
3405
	*/
3406
	function php_xmlrpc_encode($php_val, $options=array())
3407
	{
3408
		$type = gettype($php_val);
3409
		switch($type)
3410
		{
3411
			case 'string':
3412
				if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3413
					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3414
				else
3415
					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3416
				break;
3417
			case 'integer':
3418
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3419
				break;
3420
			case 'double':
3421
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3422
				break;
3423
				// <G_Giunta_2001-02-29>
3424
				// Add support for encoding/decoding of booleans, since they are supported in PHP
3425
			case 'boolean':
3426
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3427
				break;
3428
				// </G_Giunta_2001-02-29>
3429
			case 'array':
3430
				// PHP arrays can be encoded to either xmlrpc structs or arrays,
3431
				// depending on wheter they are hashes or plain 0..n integer indexed
3432
				// A shorter one-liner would be
3433
				// $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3434
				// but execution time skyrockets!
3435
				$j = 0;
3436
				$arr = array();
3437
				$ko = false;
3438
				foreach($php_val as $key => $val)
3439
				{
3440
					$arr[$key] = php_xmlrpc_encode($val, $options);
3441
					if(!$ko && $key !== $j)
3442
					{
3443
						$ko = true;
3444
					}
3445
					$j++;
3446
				}
3447
				if($ko)
3448
				{
3449
					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
0 ignored issues
show
Documentation introduced by
$arr is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3450
				}
3451
				else
3452
				{
3453
					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
0 ignored issues
show
Documentation introduced by
$arr is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3454
				}
3455
				break;
3456
			case 'object':
3457
				if(is_a($php_val, 'xmlrpcval'))
3458
				{
3459
					$xmlrpc_val = $php_val;
3460
				}
3461
				else if(is_a($php_val, 'DateTime'))
3462
				{
3463
					$xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3464
				}
3465
				else
3466
				{
3467
					$arr = array();
3468
					reset($php_val);
3469
					while(list($k,$v) = each($php_val))
3470
					{
3471
						$arr[$k] = php_xmlrpc_encode($v, $options);
3472
					}
3473
					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
0 ignored issues
show
Documentation introduced by
$arr is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3474
					if (in_array('encode_php_objs', $options))
3475
					{
3476
						// let's save original class name into xmlrpcval:
3477
						// might be useful later on...
3478
						$xmlrpc_val->_php_class = get_class($php_val);
3479
					}
3480
				}
3481
				break;
3482
			case 'NULL':
3483
				if (in_array('extension_api', $options))
3484
				{
3485
					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3486
				}
3487
				else if (in_array('null_extension', $options))
3488
				{
3489
					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3490
				}
3491
				else
3492
				{
3493
					$xmlrpc_val = new xmlrpcval();
3494
				}
3495
				break;
3496
			case 'resource':
3497
				if (in_array('extension_api', $options))
3498
				{
3499
					$xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
0 ignored issues
show
Unused Code introduced by
$xmlrpc_val is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3500
				}
3501
				else
3502
				{
3503
					$xmlrpc_val = new xmlrpcval();
0 ignored issues
show
Unused Code introduced by
$xmlrpc_val is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3504
				}
3505
			// catch "user function", "unknown type"
3506
			default:
3507
				// giancarlo pinerolo <[email protected]>
3508
				// it has to return
3509
				// an empty object in case, not a boolean.
3510
				$xmlrpc_val = new xmlrpcval();
3511
				break;
3512
			}
3513
			return $xmlrpc_val;
3514
	}
3515
3516
	/**
3517
	* Convert the xml representation of a method response, method request or single
3518
	* xmlrpc value into the appropriate object (a.k.a. deserialize)
3519
	* @param string $xml_val
3520
	* @param array $options
3521
	* @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3522
	*/
3523
	function php_xmlrpc_decode_xml($xml_val, $options=array())
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

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

Loading history...
3524
	{
3525
		$GLOBALS['_xh'] = array();
3526
		$GLOBALS['_xh']['ac'] = '';
3527
		$GLOBALS['_xh']['stack'] = array();
3528
		$GLOBALS['_xh']['valuestack'] = array();
3529
		$GLOBALS['_xh']['params'] = array();
3530
		$GLOBALS['_xh']['pt'] = array();
3531
		$GLOBALS['_xh']['isf'] = 0;
3532
		$GLOBALS['_xh']['isf_reason'] = '';
3533
		$GLOBALS['_xh']['method'] = false;
3534
		$GLOBALS['_xh']['rt'] = '';
3535
		/// @todo 'guestimate' encoding
3536
		$parser = xml_parser_create();
3537
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3538
		// What if internal encoding is not in one of the 3 allowed?
3539
		// we use the broadest one, ie. utf8!
3540 View Code Duplication
		if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3541
		{
3542
			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3543
		}
3544
		else
3545
		{
3546
			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3547
		}
3548
		xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3549
		xml_set_character_data_handler($parser, 'xmlrpc_cd');
3550
		xml_set_default_handler($parser, 'xmlrpc_dh');
3551
		if(!xml_parse($parser, $xml_val, 1))
3552
		{
3553
			$errstr = sprintf('XML error: %s at line %d, column %d',
3554
						xml_error_string(xml_get_error_code($parser)),
3555
						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3556
			error_log($errstr);
3557
			xml_parser_free($parser);
3558
			return false;
3559
		}
3560
		xml_parser_free($parser);
3561
		if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3562
		{
3563
			error_log($GLOBALS['_xh']['isf_reason']);
3564
			return false;
3565
		}
3566
		switch ($GLOBALS['_xh']['rt'])
3567
		{
3568
			case 'methodresponse':
3569
				$v =& $GLOBALS['_xh']['value'];
3570
				if ($GLOBALS['_xh']['isf'] == 1)
3571
				{
3572
					$vc = $v->structmem('faultCode');
3573
					$vs = $v->structmem('faultString');
3574
					$r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3575
				}
3576
				else
3577
				{
3578
					$r = new xmlrpcresp($v);
3579
				}
3580
				return $r;
3581
			case 'methodcall':
3582
				$m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3583 View Code Duplication
				for($i=0; $i < count($GLOBALS['_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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3584
				{
3585
					$m->addParam($GLOBALS['_xh']['params'][$i]);
3586
				}
3587
				return $m;
3588
			case 'value':
3589
				return $GLOBALS['_xh']['value'];
3590
			default:
3591
				return false;
3592
		}
3593
	}
3594
3595
	/**
3596
	* decode a string that is encoded w/ "chunked" transfer encoding
3597
	* as defined in rfc2068 par. 19.4.6
3598
	* code shamelessly stolen from nusoap library by Dietrich Ayala
3599
	*
3600
	* @param string $buffer the string to be decoded
3601
	* @return string
3602
	*/
3603
	function decode_chunked($buffer)
3604
	{
3605
		// length := 0
3606
		$length = 0;
3607
		$new = '';
3608
3609
		// read chunk-size, chunk-extension (if any) and crlf
3610
		// get the position of the linebreak
3611
		$chunkend = strpos($buffer,"\r\n") + 2;
3612
		$temp = substr($buffer,0,$chunkend);
3613
		$chunk_size = hexdec( trim($temp) );
3614
		$chunkstart = $chunkend;
3615
		while($chunk_size > 0)
3616
		{
3617
			$chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3618
3619
			// just in case we got a broken connection
3620
			if($chunkend == false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $chunkend of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
3621
			{
3622
				$chunk = substr($buffer,$chunkstart);
3623
				// append chunk-data to entity-body
3624
				$new .= $chunk;
3625
				$length += strlen($chunk);
3626
				break;
3627
			}
3628
3629
			// read chunk-data and crlf
3630
			$chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3631
			// append chunk-data to entity-body
3632
			$new .= $chunk;
3633
			// length := length + chunk-size
3634
			$length += strlen($chunk);
3635
			// read chunk-size and crlf
3636
			$chunkstart = $chunkend + 2;
3637
3638
			$chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3639
			if($chunkend == false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $chunkend of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
3640
			{
3641
				break; //just in case we got a broken connection
3642
			}
3643
			$temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3644
			$chunk_size = hexdec( trim($temp) );
3645
			$chunkstart = $chunkend;
3646
		}
3647
		return $new;
3648
	}
3649
3650
	/**
3651
	* xml charset encoding guessing helper function.
3652
	* Tries to determine the charset encoding of an XML chunk received over HTTP.
3653
	* NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3654
	* we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3655
	* which will be most probably using UTF-8 anyway...
3656
	*
3657
	* @param string $httpheaders the http Content-type header
0 ignored issues
show
Documentation introduced by
There is no parameter named $httpheaders. Did you maybe mean $httpheader?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
3658
	* @param string $xmlchunk xml content buffer
3659
	* @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $encoding_prefs not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3660
	*
3661
	* @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3662
	*/
3663
	function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
3664
	{
3665
		// discussion: see http://www.yale.edu/pclt/encoding/
3666
		// 1 - test if encoding is specified in HTTP HEADERS
3667
3668
		//Details:
3669
		// LWS:           (\13\10)?( |\t)+
3670
		// token:         (any char but excluded stuff)+
3671
		// quoted string: " (any char but double quotes and cointrol chars)* "
3672
		// header:        Content-type = ...; charset=value(; ...)*
3673
		//   where value is of type token, no LWS allowed between 'charset' and value
3674
		// Note: we do not check for invalid chars in VALUE:
3675
		//   this had better be done using pure ereg as below
3676
		// Note 2: we might be removing whitespace/tabs that ought to be left in if
3677
		//   the received charset is a quoted string. But nobody uses such charset names...
3678
3679
		/// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3680
		$matches = array();
3681
		if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3682
		{
3683
			return strtoupper(trim($matches[1], " \t\""));
3684
		}
3685
3686
		// 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3687
		//     (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3688
		//     NOTE: actually, according to the spec, even if we find the BOM and determine
3689
		//     an encoding, we should check if there is an encoding specified
3690
		//     in the xml declaration, and verify if they match.
3691
		/// @todo implement check as described above?
3692
		/// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3693
		if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3694
		{
3695
			return 'UCS-4';
3696
		}
3697
		elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3698
		{
3699
			return 'UTF-16';
3700
		}
3701
		elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3702
		{
3703
			return 'UTF-8';
3704
		}
3705
3706
		// 3 - test if encoding is specified in the xml declaration
3707
		// Details:
3708
		// SPACE:         (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3709
		// EQ:            SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3710
		if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3711
			'\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3712
			$xmlchunk, $matches))
3713
		{
3714
			return strtoupper(substr($matches[2], 1, -1));
3715
		}
3716
3717
		// 4 - if mbstring is available, let it do the guesswork
3718
		// NB: we favour finding an encoding that is compatible with what we can process
3719
		if(extension_loaded('mbstring'))
3720
		{
3721
			if($encoding_prefs)
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoding_prefs of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3722
			{
3723
				$enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3724
			}
3725
			else
3726
			{
3727
				$enc = mb_detect_encoding($xmlchunk);
3728
			}
3729
			// NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3730
			// IANA also likes better US-ASCII, so go with it
3731
			if($enc == 'ASCII')
3732
			{
3733
				$enc = 'US-'.$enc;
3734
			}
3735
			return $enc;
3736
		}
3737
		else
3738
		{
3739
			// no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3740
			// Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3741
			// this should be the standard. And we should be getting text/xml as request and response.
3742
			// BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3743
			return $GLOBALS['xmlrpc_defencoding'];
3744
		}
3745
	}
3746
3747
	/**
3748
	* Checks if a given charset encoding is present in a list of encodings or
3749
	* if it is a valid subset of any encoding in the list
3750
	* @param string $encoding charset to be tested
3751
	* @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3752
	*/
3753
	function is_valid_charset($encoding, $validlist)
3754
	{
3755
		$charset_supersets = array(
3756
			'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3757
				'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3758
				'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3759
				'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3760
				'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3761
		);
3762
		if (is_string($validlist))
3763
			$validlist = explode(',', $validlist);
3764
		if (@in_array(strtoupper($encoding), $validlist))
3765
			return true;
3766
		else
3767
		{
3768
			if (array_key_exists($encoding, $charset_supersets))
3769
				foreach ($validlist as $allowed)
0 ignored issues
show
Bug introduced by
The expression $validlist of type object|integer|double|null|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
3770
					if (in_array($allowed, $charset_supersets[$encoding]))
3771
						return true;
3772
				return false;
3773
		}
3774
	}
3775
3776
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...