Completed
Push — php51 ( 13671e...f099e1 )
by Gaetano
05:44
created

xmlrpc_client::setSSLVerifyPeer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
// by Edd Dumbill (C) 1999-2002
3
// <[email protected]>
4
5
// Copyright (c) 1999,2000,2002 Edd Dumbill.
6
// All rights reserved.
7
//
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions
10
// are met:
11
//
12
//    * Redistributions of source code must retain the above copyright
13
//      notice, this list of conditions and the following disclaimer.
14
//
15
//    * Redistributions in binary form must reproduce the above
16
//      copyright notice, this list of conditions and the following
17
//      disclaimer in the documentation and/or other materials provided
18
//      with the distribution.
19
//
20
//    * Neither the name of the "XML-RPC for PHP" nor the names of its
21
//      contributors may be used to endorse or promote products derived
22
//      from this software without specific prior written permission.
23
//
24
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28
// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35
// OF THE POSSIBILITY OF SUCH DAMAGE.
36
37
	if(!function_exists('xml_parser_create'))
38
	{
39
		// For PHP 4 onward, XML functionality is always compiled-in on windows:
40
		// no more need to dl-open it. It might have been compiled out on *nix...
41
		if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
42
		{
43
			dl('xml.so');
44
		}
45
	}
46
47
	// G. Giunta 2005/01/29: declare global these variables,
48
	// so that xmlrpc.inc will work even if included from within a function
49
	// Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
50
	$GLOBALS['xmlrpcI4']='i4';
51
	$GLOBALS['xmlrpcInt']='int';
52
	$GLOBALS['xmlrpcBoolean']='boolean';
53
	$GLOBALS['xmlrpcDouble']='double';
54
	$GLOBALS['xmlrpcString']='string';
55
	$GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
56
	$GLOBALS['xmlrpcBase64']='base64';
57
	$GLOBALS['xmlrpcArray']='array';
58
	$GLOBALS['xmlrpcStruct']='struct';
59
	$GLOBALS['xmlrpcValue']='undefined';
60
61
	$GLOBALS['xmlrpcTypes']=array(
62
		$GLOBALS['xmlrpcI4']       => 1,
63
		$GLOBALS['xmlrpcInt']      => 1,
64
		$GLOBALS['xmlrpcBoolean']  => 1,
65
		$GLOBALS['xmlrpcString']   => 1,
66
		$GLOBALS['xmlrpcDouble']   => 1,
67
		$GLOBALS['xmlrpcDateTime'] => 1,
68
		$GLOBALS['xmlrpcBase64']   => 1,
69
		$GLOBALS['xmlrpcArray']    => 2,
70
		$GLOBALS['xmlrpcStruct']   => 3
71
	);
72
73
	$GLOBALS['xmlrpc_valid_parents'] = array(
74
		'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
75
		'BOOLEAN' => array('VALUE'),
76
		'I4' => array('VALUE'),
77
		'INT' => array('VALUE'),
78
		'STRING' => array('VALUE'),
79
		'DOUBLE' => array('VALUE'),
80
		'DATETIME.ISO8601' => array('VALUE'),
81
		'BASE64' => array('VALUE'),
82
		'MEMBER' => array('STRUCT'),
83
		'NAME' => array('MEMBER'),
84
		'DATA' => array('ARRAY'),
85
		'ARRAY' => array('VALUE'),
86
		'STRUCT' => array('VALUE'),
87
		'PARAM' => array('PARAMS'),
88
		'METHODNAME' => array('METHODCALL'),
89
		'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
90
		'FAULT' => array('METHODRESPONSE'),
91
		'NIL' => array('VALUE'), // only used when extension activated
92
		'EX:NIL' => array('VALUE') // only used when extension activated
93
	);
94
95
	// define extra types for supporting NULL (useful for json or <NIL/>)
96
	$GLOBALS['xmlrpcNull']='null';
97
	$GLOBALS['xmlrpcTypes']['null']=1;
98
99
	// Not in use anymore since 2.0. Shall we remove it?
100
	/// @deprecated
101
	$GLOBALS['xmlEntities']=array(
102
		'amp'  => '&',
103
		'quot' => '"',
104
		'lt'   => '<',
105
		'gt'   => '>',
106
		'apos' => "'"
107
	);
108
109
	// tables used for transcoding different charsets into us-ascii xml
110
111
	$GLOBALS['xml_iso88591_Entities']=array();
112
	$GLOBALS['xml_iso88591_Entities']['in'] = array();
113
	$GLOBALS['xml_iso88591_Entities']['out'] = array();
114 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...
115
	{
116
		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
117
		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
118
	}
119 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...
120
	{
121
		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
122
		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
123
	}
124
125
	/// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
126
	/// These will NOT be present in true ISO-8859-1, but will save the unwary
127
	/// windows user from sending junk (though no luck when reciving them...)
128
  /*
129
	$GLOBALS['xml_cp1252_Entities']=array();
130
	for ($i = 128; $i < 160; $i++)
131
	{
132
		$GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
133
	}
134
	$GLOBALS['xml_cp1252_Entities']['out'] = array(
135
		'&#x20AC;', '?',        '&#x201A;', '&#x0192;',
136
		'&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
137
		'&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
138
		'&#x0152;', '?',        '&#x017D;', '?',
139
		'?',        '&#x2018;', '&#x2019;', '&#x201C;',
140
		'&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
141
		'&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
142
		'&#x0153;', '?',        '&#x017E;', '&#x0178;'
143
	);
144
  */
145
146
	$GLOBALS['xmlrpcerr'] = array(
147
	'unknown_method'=>1,
148
	'invalid_return'=>2,
149
	'incorrect_params'=>3,
150
	'introspect_unknown'=>4,
151
	'http_error'=>5,
152
	'no_data'=>6,
153
	'no_ssl'=>7,
154
	'curl_fail'=>8,
155
	'invalid_request'=>15,
156
	'no_curl'=>16,
157
	'server_error'=>17,
158
	'multicall_error'=>18,
159
	'multicall_notstruct'=>9,
160
	'multicall_nomethod'=>10,
161
	'multicall_notstring'=>11,
162
	'multicall_recursion'=>12,
163
	'multicall_noparams'=>13,
164
	'multicall_notarray'=>14,
165
166
	'cannot_decompress'=>103,
167
	'decompress_fail'=>104,
168
	'dechunk_fail'=>105,
169
	'server_cannot_decompress'=>106,
170
	'server_decompress_fail'=>107
171
	);
172
173
	$GLOBALS['xmlrpcstr'] = array(
174
	'unknown_method'=>'Unknown method',
175
	'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
176
	'incorrect_params'=>'Incorrect parameters passed to method',
177
	'introspect_unknown'=>"Can't introspect: method unknown",
178
	'http_error'=>"Didn't receive 200 OK from remote server.",
179
	'no_data'=>'No data received from server.',
180
	'no_ssl'=>'No SSL support compiled in.',
181
	'curl_fail'=>'CURL error',
182
	'invalid_request'=>'Invalid request payload',
183
	'no_curl'=>'No CURL support compiled in.',
184
	'server_error'=>'Internal server error',
185
	'multicall_error'=>'Received from server invalid multicall response',
186
	'multicall_notstruct'=>'system.multicall expected struct',
187
	'multicall_nomethod'=>'missing methodName',
188
	'multicall_notstring'=>'methodName is not a string',
189
	'multicall_recursion'=>'recursive system.multicall forbidden',
190
	'multicall_noparams'=>'missing params',
191
	'multicall_notarray'=>'params is not an array',
192
193
	'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
194
	'decompress_fail'=>'Received from server invalid compressed HTTP',
195
	'dechunk_fail'=>'Received from server invalid chunked HTTP',
196
	'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
197
	'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
198
	);
199
200
	// The charset encoding used by the server for received messages and
201
	// by the client for received responses when received charset cannot be determined
202
	// or is not supported
203
	$GLOBALS['xmlrpc_defencoding']='UTF-8';
204
205
	// The encoding used internally by PHP.
206
	// String values received as xml will be converted to this, and php strings will be converted to xml
207
	// as if having been coded with this
208
	$GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
209
210
	$GLOBALS['xmlrpcName']='XML-RPC for PHP';
211
	$GLOBALS['xmlrpcVersion']='3.0.1';
212
213
	// let user errors start at 800
214
	$GLOBALS['xmlrpcerruser']=800;
215
	// let XML parse errors start at 100
216
	$GLOBALS['xmlrpcerrxml']=100;
217
218
	// formulate backslashes for escaping regexp
219
	// Not in use anymore since 2.0. Shall we remove it?
220
	/// @deprecated
221
	$GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
222
223
	// set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
224
	$GLOBALS['xmlrpc_null_extension']=false;
225
226
	// set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
227
	$GLOBALS['xmlrpc_null_apache_encoding']=false;
228
	$GLOBALS['xmlrpc_null_apache_encoding_ns']='http://ws.apache.org/xmlrpc/namespaces/extensions';
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='')
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 $sslversion=0; // corresponds to CURL_SSLVERSION_DEFAULT
825
		var $no_multicall=false;
826
		var $proxy='';
827
		var $proxyport=0;
828
		var $proxy_user='';
829
		var $proxy_pass='';
830
		var $proxy_authtype=1;
831
		var $cookies=array();
832
		var $extracurlopts=array();
833
834
		/**
835
		* List of http compression methods accepted by the client for responses.
836
		* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
837
		*
838
		* NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
839
		* in those cases it will be up to CURL to decide the compression methods
840
		* it supports. You might check for the presence of 'zlib' in the output of
841
		* curl_version() to determine wheter compression is supported or not
842
		*/
843
		var $accepted_compression = array();
844
		/**
845
		* Name of compression scheme to be used for sending requests.
846
		* Either null, gzip or deflate
847
		*/
848
		var $request_compression = '';
849
		/**
850
		* CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
851
		* http://curl.haxx.se/docs/faq.html#7.3)
852
		*/
853
		var $xmlrpc_curl_handle = null;
854
		/// Whether to use persistent connections for http 1.1 and https
855
		var $keepalive = false;
856
		/// Charset encodings that can be decoded without problems by the client
857
		var $accepted_charset_encodings = array();
858
		/// Charset encoding to be used in serializing request. NULL = use ASCII
859
		var $request_charset_encoding = '';
860
		/**
861
		* Decides the content of xmlrpcresp objects returned by calls to send()
862
		* valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
863
		*/
864
		var $return_type = 'xmlrpcvals';
865
		/**
866
		* Sent to servers in http headers
867
		*/
868
		var $user_agent;
869
870
		/**
871
		* @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
872
		* @param string $server the server name / ip address
873
		* @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
874
		* @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
875
		*/
876
		function __construct($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...
877
		{
878
			// allow user to specify all params in $path
879
			if($server == '' and $port == '' and $method == '')
880
			{
881
				$parts = parse_url($path);
882
				$server = $parts['host'];
883
				$path = isset($parts['path']) ? $parts['path'] : '';
884
				if(isset($parts['query']))
885
				{
886
					$path .= '?'.$parts['query'];
887
				}
888
				if(isset($parts['fragment']))
889
				{
890
					$path .= '#'.$parts['fragment'];
891
				}
892
				if(isset($parts['port']))
893
				{
894
					$port = $parts['port'];
895
				}
896
				if(isset($parts['scheme']))
897
				{
898
					$method = $parts['scheme'];
899
				}
900
				if(isset($parts['user']))
901
				{
902
					$this->username = $parts['user'];
903
				}
904
				if(isset($parts['pass']))
905
				{
906
					$this->password = $parts['pass'];
907
				}
908
			}
909
			if($path == '' || $path[0] != '/')
910
			{
911
				$this->path='/'.$path;
912
			}
913
			else
914
			{
915
				$this->path=$path;
916
			}
917
			$this->server=$server;
918
			if($port != '')
919
			{
920
				$this->port=$port;
921
			}
922
			if($method != '')
923
			{
924
				$this->method=$method;
925
			}
926
927
			// if ZLIB is enabled, let the client by default accept compressed responses
928
			if(function_exists('gzinflate') || (
929
				function_exists('curl_init') && (($info = curl_version()) &&
930
				((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
931
			))
932
			{
933
				$this->accepted_compression = array('gzip', 'deflate');
934
			}
935
936
			// keepalives: enabled by default
937
			$this->keepalive = true;
938
939
			// by default the xml parser can support these 3 charset encodings
940
			$this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
941
942
			// initialize user_agent string
943
			$this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
944
		}
945
946
		/**
947
		* @deprecated
948
		*/
949
		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...
950
		{
951
			self::__construct($path, $server, $port, $method);
952
		}
953
954
		/**
955
		* Enables/disables the echoing to screen of the xmlrpc responses received
956
		* @param integer $in values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
957
		* @access public
958
		*/
959
		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...
960
		{
961
			$this->debug=$in;
962
		}
963
964
		/**
965
		* Add some http BASIC AUTH credentials, used by the client to authenticate
966
		* @param string $u username
967
		* @param string $p password
968
		* @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
969
		* @access public
970
		*/
971
		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...
972
		{
973
			$this->username=$u;
974
			$this->password=$p;
975
			$this->authtype=$t;
976
		}
977
978
		/**
979
		* Add a client-side https certificate
980
		* @param string $cert
981
		* @param string $certpass
982
		* @access public
983
		*/
984
		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...
985
		{
986
			$this->cert = $cert;
987
			$this->certpass = $certpass;
988
		}
989
990
		/**
991
		* Add a CA certificate to verify server with (see man page about
992
		* CURLOPT_CAINFO for more details)
993
		* @param string $cacert certificate file name (or dir holding certificates)
994
		* @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
995
		* @access public
996
		*/
997
		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...
998
		{
999
			if ($is_dir)
1000
			{
1001
				$this->cacertdir = $cacert;
1002
			}
1003
			else
1004
			{
1005
				$this->cacert = $cacert;
1006
			}
1007
		}
1008
1009
		/**
1010
		* Set attributes for SSL communication: private SSL key
1011
		* NB: does not work in older php/curl installs
1012
		* Thanks to Daniel Convissor
1013
		* @param string $key The name of a file containing a private SSL key
1014
		* @param string $keypass The secret password needed to use the private SSL key
1015
		* @access public
1016
		*/
1017
		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...
1018
		{
1019
			$this->key = $key;
1020
			$this->keypass = $keypass;
1021
		}
1022
1023
		/**
1024
		* Set attributes for SSL communication: verify server certificate
1025
		* @param bool $i enable/disable verification of peer certificate
1026
		* @access public
1027
		*/
1028
		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...
1029
		{
1030
			$this->verifypeer = $i;
1031
		}
1032
1033
		/**
1034
		* Set attributes for SSL communication: verify match of server cert w. hostname
1035
		* @param int $i
1036
		* @access public
1037
		*/
1038
		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...
1039
		{
1040
			$this->verifyhost = $i;
1041
		}
1042
1043
		/**
1044
		* Set attributes for SSL communication: SSL version to use. Best left at 0 (default value ): let cURL decide
1045
		*
1046
		* @param int $i
1047
		*/
1048
		public function setSSLVersion($i)
1049
		{
1050
			$this->sslversion = $i;
1051
		}
1052
1053
		/**
1054
		* Set proxy info
1055
		* @param string $proxyhost
1056
		* @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1057
		* @param string $proxyusername Leave blank if proxy has public access
1058
		* @param string $proxypassword Leave blank if proxy has public access
1059
		* @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1060
		* @access public
1061
		*/
1062
		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...
1063
		{
1064
			$this->proxy = $proxyhost;
1065
			$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...
1066
			$this->proxy_user = $proxyusername;
1067
			$this->proxy_pass = $proxypassword;
1068
			$this->proxy_authtype = $proxyauthtype;
1069
		}
1070
1071
		/**
1072
		* Enables/disables reception of compressed xmlrpc responses.
1073
		* Note that enabling reception of compressed responses merely adds some standard
1074
		* http headers to xmlrpc requests. It is up to the xmlrpc server to return
1075
		* compressed responses when receiving such requests.
1076
		* @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1077
		* @access public
1078
		*/
1079
		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...
1080
		{
1081
			if ($compmethod == 'any')
1082
				$this->accepted_compression = array('gzip', 'deflate');
1083
			else
1084
				if ($compmethod == false )
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $compmethod of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
1085
					$this->accepted_compression = array();
1086
				else
1087
					$this->accepted_compression = array($compmethod);
1088
		}
1089
1090
		/**
1091
		* Enables/disables http compression of xmlrpc request.
1092
		* Take care when sending compressed requests: servers might not support them
1093
		* (and automatic fallback to uncompressed requests is not yet implemented)
1094
		* @param string $compmethod either 'gzip', 'deflate' or ''
1095
		* @access public
1096
		*/
1097
		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...
1098
		{
1099
			$this->request_compression = $compmethod;
1100
		}
1101
1102
		/**
1103
		* Adds a cookie to list of cookies that will be sent to server.
1104
		* NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1105
		* do not do it unless you know what you are doing
1106
		* @param string $name
1107
		* @param string $value
1108
		* @param string $path
1109
		* @param string $domain
1110
		* @param int $port
1111
		* @access public
1112
		*
1113
		* @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1114
		*/
1115
		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...
1116
		{
1117
			$this->cookies[$name]['value'] = urlencode($value);
1118
			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...
1119
			{
1120
				$this->cookies[$name]['path'] = $path;
1121
				$this->cookies[$name]['domain'] = $domain;
1122
				$this->cookies[$name]['port'] = $port;
1123
				$this->cookies[$name]['version'] = 1;
1124
			}
1125
			else
1126
			{
1127
				$this->cookies[$name]['version'] = 0;
1128
			}
1129
		}
1130
1131
		/**
1132
		* Directly set cURL options, for extra flexibility
1133
		* It allows eg. to bind client to a specific IP interface / address
1134
		* @param array $options
1135
		*/
1136
		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...
1137
		{
1138
			$this->extracurlopts = $options;
1139
		}
1140
1141
		/**
1142
		* Set user-agent string that will be used by this client instance
1143
		* in http headers sent to the server
1144
		*/
1145
		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...
1146
		{
1147
			$this->user_agent = $agentstring;
1148
		}
1149
1150
		/**
1151
		* Send an xmlrpc request
1152
		* @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1153
		* @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1154
		* @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1155
		* @return xmlrpcresp
1156
		* @access public
1157
		*/
1158
		function& send($msg, $timeout=0, $method='')
1159
		{
1160
			// if user deos not specify http protocol, use native method of this client
1161
			// (i.e. method set during call to constructor)
1162
			if($method == '')
1163
			{
1164
				$method = $this->method;
1165
			}
1166
1167
			if(is_array($msg))
1168
			{
1169
				// $msg is an array of xmlrpcmsg's
1170
				$r = $this->multicall($msg, $timeout, $method);
1171
				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...
1172
			}
1173
			elseif(is_string($msg))
1174
			{
1175
				$n = new xmlrpcmsg('');
1176
				$n->payload = $msg;
1177
				$msg = $n;
1178
			}
1179
1180
			// where msg is an xmlrpcmsg
1181
			$msg->debug=$this->debug;
1182
1183
			if($method == 'https')
1184
			{
1185
				$r =& $this->sendPayloadHTTPS(
1186
					$msg,
1187
					$this->server,
1188
					$this->port,
1189
					$timeout,
1190
					$this->username,
1191
					$this->password,
1192
					$this->authtype,
1193
					$this->cert,
1194
					$this->certpass,
1195
					$this->cacert,
1196
					$this->cacertdir,
1197
					$this->proxy,
1198
					$this->proxyport,
1199
					$this->proxy_user,
1200
					$this->proxy_pass,
1201
					$this->proxy_authtype,
1202
					$this->keepalive,
1203
					$this->key,
1204
					$this->keypass,
1205
					$this->sslversion
1206
				);
1207
			}
1208
			elseif($method == 'http11')
1209
			{
1210
				$r =& $this->sendPayloadCURL(
1211
					$msg,
1212
					$this->server,
1213
					$this->port,
1214
					$timeout,
1215
					$this->username,
1216
					$this->password,
1217
					$this->authtype,
1218
					null,
1219
					null,
1220
					null,
1221
					null,
1222
					$this->proxy,
1223
					$this->proxyport,
1224
					$this->proxy_user,
1225
					$this->proxy_pass,
1226
					$this->proxy_authtype,
1227
					'http',
1228
					$this->keepalive
1229
				);
1230
			}
1231
			else
1232
			{
1233
				$r =& $this->sendPayloadHTTP10(
1234
					$msg,
1235
					$this->server,
1236
					$this->port,
1237
					$timeout,
1238
					$this->username,
1239
					$this->password,
1240
					$this->authtype,
1241
					$this->proxy,
1242
					$this->proxyport,
1243
					$this->proxy_user,
1244
					$this->proxy_pass,
1245
					$this->proxy_authtype
1246
				);
1247
			}
1248
1249
			return $r;
1250
		}
1251
1252
		/**
1253
		* @access private
1254
		*/
1255
		function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1256
			$username='', $password='', $authtype=1, $proxyhost='',
1257
			$proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1258
		{
1259
			if($port==0)
1260
			{
1261
				$port=80;
1262
			}
1263
1264
			// Only create the payload if it was not created previously
1265
			if(empty($msg->payload))
1266
			{
1267
				$msg->createPayload($this->request_charset_encoding);
1268
			}
1269
1270
			$payload = $msg->payload;
1271
			// Deflate request body and set appropriate request headers
1272 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...
1273
			{
1274
				if($this->request_compression == 'gzip')
1275
				{
1276
					$a = @gzencode($payload);
1277
					if($a)
1278
					{
1279
						$payload = $a;
1280
						$encoding_hdr = "Content-Encoding: gzip\r\n";
1281
					}
1282
				}
1283
				else
1284
				{
1285
					$a = @gzcompress($payload);
1286
					if($a)
1287
					{
1288
						$payload = $a;
1289
						$encoding_hdr = "Content-Encoding: deflate\r\n";
1290
					}
1291
				}
1292
			}
1293
			else
1294
			{
1295
				$encoding_hdr = '';
1296
			}
1297
1298
			// thanks to Grant Rauscher <[email protected]> for this
1299
			$credentials='';
1300
			if($username!='')
1301
			{
1302
				$credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1303
				if ($authtype != 1)
1304
				{
1305
					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1306
				}
1307
			}
1308
1309
			$accepted_encoding = '';
1310
			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1311
			{
1312
				$accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1313
			}
1314
1315
			$proxy_credentials = '';
1316
			if($proxyhost)
1317
			{
1318
				if($proxyport == 0)
1319
				{
1320
					$proxyport = 8080;
1321
				}
1322
				$connectserver = $proxyhost;
1323
				$connectport = $proxyport;
1324
				$uri = 'http://'.$server.':'.$port.$this->path;
1325
				if($proxyusername != '')
1326
				{
1327
					if ($proxyauthtype != 1)
1328
					{
1329
						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1330
					}
1331
					$proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1332
				}
1333
			}
1334
			else
1335
			{
1336
				$connectserver = $server;
1337
				$connectport = $port;
1338
				$uri = $this->path;
1339
			}
1340
1341
			// Cookie generation, as per rfc2965 (version 1 cookies) or
1342
			// netscape's rules (version 0 cookies)
1343
			$cookieheader='';
1344
			if (count($this->cookies))
1345
			{
1346
				$version = '';
1347
				foreach ($this->cookies as $name => $cookie)
1348
				{
1349
					if ($cookie['version'])
1350
					{
1351
						$version = ' $Version="' . $cookie['version'] . '";';
1352
						$cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1353
						if ($cookie['path'])
1354
							$cookieheader .= ' $Path="' . $cookie['path'] . '";';
1355
						if ($cookie['domain'])
1356
							$cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1357
						if ($cookie['port'])
1358
							$cookieheader .= ' $Port="' . $cookie['port'] . '";';
1359
					}
1360
					else
1361
					{
1362
						$cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1363
					}
1364
				}
1365
				$cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1366
			}
1367
1368
			// omit port if 80
1369
			$port = ($port == 80) ? '' : (':' . $port);
1370
1371
			$op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1372
				'User-Agent: ' . $this->user_agent . "\r\n" .
1373
				'Host: '. $server . $port . "\r\n" .
1374
				$credentials .
1375
				$proxy_credentials .
1376
				$accepted_encoding .
1377
				$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...
1378
				'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1379
				$cookieheader .
1380
				'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1381
				strlen($payload) . "\r\n\r\n" .
1382
				$payload;
1383
1384
			if($this->debug > 1)
1385
			{
1386
				print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1387
				// let the client see this now in case http times out...
1388
				flush();
1389
			}
1390
1391
			if($timeout>0)
1392
			{
1393
				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1394
			}
1395
			else
1396
			{
1397
				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1398
			}
1399
			if($fp)
1400
			{
1401
				if($timeout>0 && function_exists('stream_set_timeout'))
1402
				{
1403
					stream_set_timeout($fp, $timeout);
1404
				}
1405
			}
1406
			else
1407
			{
1408
				$this->errstr='Connect error: '.$this->errstr;
1409
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1410
				return $r;
1411
			}
1412
1413
			if(!fputs($fp, $op, strlen($op)))
1414
			{
1415
				fclose($fp);
1416
				$this->errstr='Write error';
1417
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1418
				return $r;
1419
			}
1420
			else
1421
			{
1422
				// reset errno and errstr on successful socket connection
1423
				$this->errstr = '';
1424
			}
1425
			// G. Giunta 2005/10/24: close socket before parsing.
1426
			// should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1427
			$ipd='';
1428
			do
1429
			{
1430
				// shall we check for $data === FALSE?
1431
				// as per the manual, it signals an error
1432
				$ipd.=fread($fp, 32768);
1433
			} while(!feof($fp));
1434
			fclose($fp);
1435
			$r =& $msg->parseResponse($ipd, false, $this->return_type);
1436
			return $r;
1437
1438
		}
1439
1440
		/**
1441
		* @access private
1442
		*/
1443
		function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1444
			$password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1445
			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1446
			$keepalive=false, $key='', $keypass='', $sslVersion = 0)
1447
		{
1448
			$r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1449
				$password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1450
				$proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass, $sslVersion);
1451
			return $r;
1452
		}
1453
1454
		/**
1455
		* Contributed by Justin Miller <[email protected]>
1456
		* Requires curl to be built into PHP
1457
		* NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1458
		* @access private
1459
		*/
1460
		function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1461
			$password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1462
			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1463
			$keepalive=false, $key='', $keypass='', $sslVersion = 0)
1464
		{
1465
			if(!function_exists('curl_init'))
1466
			{
1467
				$this->errstr='CURL unavailable on this install';
1468
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1469
				return $r;
1470
			}
1471
			if($method == 'https')
1472
			{
1473
				if(($info = curl_version()) &&
1474
					((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1475
				{
1476
					$this->errstr='SSL unavailable on this install';
1477
					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1478
					return $r;
1479
				}
1480
			}
1481
1482
			if($port == 0)
1483
			{
1484
				if($method == 'http')
1485
				{
1486
					$port = 80;
1487
				}
1488
				else
1489
				{
1490
					$port = 443;
1491
				}
1492
			}
1493
1494
			// Only create the payload if it was not created previously
1495
			if(empty($msg->payload))
1496
			{
1497
				$msg->createPayload($this->request_charset_encoding);
1498
			}
1499
1500
			// Deflate request body and set appropriate request headers
1501
			$payload = $msg->payload;
1502 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...
1503
			{
1504
				if($this->request_compression == 'gzip')
1505
				{
1506
					$a = @gzencode($payload);
1507
					if($a)
1508
					{
1509
						$payload = $a;
1510
						$encoding_hdr = 'Content-Encoding: gzip';
1511
					}
1512
				}
1513
				else
1514
				{
1515
					$a = @gzcompress($payload);
1516
					if($a)
1517
					{
1518
						$payload = $a;
1519
						$encoding_hdr = 'Content-Encoding: deflate';
1520
					}
1521
				}
1522
			}
1523
			else
1524
			{
1525
				$encoding_hdr = '';
1526
			}
1527
1528
			if($this->debug > 1)
1529
			{
1530
				print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1531
				// let the client see this now in case http times out...
1532
				flush();
1533
			}
1534
1535
			if(!$keepalive || !$this->xmlrpc_curl_handle)
1536
			{
1537
				$curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1538
				if($keepalive)
1539
				{
1540
					$this->xmlrpc_curl_handle = $curl;
1541
				}
1542
			}
1543
			else
1544
			{
1545
				$curl = $this->xmlrpc_curl_handle;
1546
			}
1547
1548
			// results into variable
1549
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1550
1551
			if($this->debug)
1552
			{
1553
				curl_setopt($curl, CURLOPT_VERBOSE, 1);
1554
			}
1555
			curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1556
			// required for XMLRPC: post the data
1557
			curl_setopt($curl, CURLOPT_POST, 1);
1558
			// the data
1559
			curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1560
1561
			// return the header too
1562
			curl_setopt($curl, CURLOPT_HEADER, 1);
1563
1564
			// NB: if we set an empty string, CURL will add http header indicating
1565
			// ALL methods it is supporting. This is possibly a better option than
1566
			// letting the user tell what curl can / cannot do...
1567
			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1568
			{
1569
				//curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1570
				// empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1571
				if (count($this->accepted_compression) == 1)
1572
				{
1573
					curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1574
				}
1575
				else
1576
					curl_setopt($curl, CURLOPT_ENCODING, '');
1577
			}
1578
			// extra headers
1579
			$headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1580
			// if no keepalive is wanted, let the server know it in advance
1581
			if(!$keepalive)
1582
			{
1583
				$headers[] = 'Connection: close';
1584
			}
1585
			// request compression header
1586
			if($encoding_hdr)
1587
			{
1588
				$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...
1589
			}
1590
1591
			curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1592
			// timeout is borked
1593
			if($timeout)
1594
			{
1595
				curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1596
			}
1597
1598 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...
1599
			{
1600
				curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1601
				if (defined('CURLOPT_HTTPAUTH'))
1602
				{
1603
					curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1604
				}
1605
				else if ($authtype != 1)
1606
				{
1607
					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1608
				}
1609
			}
1610
1611
			if($method == 'https')
1612
			{
1613
				// set cert file
1614
				if($cert)
1615
				{
1616
					curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1617
				}
1618
				// set cert password
1619
				if($certpass)
1620
				{
1621
					curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1622
				}
1623
				// whether to verify remote host's cert
1624
				curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1625
				// set ca certificates file/dir
1626
				if($cacert)
1627
				{
1628
					curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1629
				}
1630
				if($cacertdir)
1631
				{
1632
					curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1633
				}
1634
				// set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1635
				if($key)
1636
				{
1637
					curl_setopt($curl, CURLOPT_SSLKEY, $key);
1638
				}
1639
				// set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1640
				if($keypass)
1641
				{
1642
					curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1643
				}
1644
1645
				// Upgrade transparently to more stringent check for versions of php which do not support otherwise.
1646
				// Doing it in constructor would be cleaner; doing it here saves us a couple of function calls
1647
				if($this->verifyhost == 1 && $info = curl_version() && version_compare($info['version'], '7.28.1') >= 0)
0 ignored issues
show
Bug introduced by
The variable $info 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...
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $info = (curl_version() ...sion'], '7.28.1') >= 0), Probably Intended Meaning: ($info = curl_version())...rsion'], '7.28.1') >= 0
Loading history...
1648
				{
1649
					$this->verifyhost = 2;
1650
				}
1651
				// 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
1652
				curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1653
				// allow usage of different SSL versions
1654
				curl_setopt($curl, CURLOPT_SSLVERSION, $sslVersion);
1655
			}
1656
1657
			// proxy info
1658
			if($proxyhost)
1659
			{
1660
				if($proxyport == 0)
1661
				{
1662
					$proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1663
				}
1664
				curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1665
				//curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1666 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...
1667
				{
1668
					curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1669
					if (defined('CURLOPT_PROXYAUTH'))
1670
					{
1671
						curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1672
					}
1673
					else if ($proxyauthtype != 1)
1674
					{
1675
						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1676
					}
1677
				}
1678
			}
1679
1680
			// NB: should we build cookie http headers by hand rather than let CURL do it?
1681
			// the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1682
			// set to client obj the the user...
1683
			if (count($this->cookies))
1684
			{
1685
				$cookieheader = '';
1686
				foreach ($this->cookies as $name => $cookie)
1687
				{
1688
					$cookieheader .= $name . '=' . $cookie['value'] . '; ';
1689
				}
1690
				curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1691
			}
1692
1693
			foreach ($this->extracurlopts as $opt => $val)
1694
			{
1695
				curl_setopt($curl, $opt, $val);
1696
			}
1697
1698
			$result = curl_exec($curl);
1699
1700
			if ($this->debug > 1)
1701
			{
1702
				print "<PRE>\n---CURL INFO---\n";
1703
				foreach(curl_getinfo($curl) as $name => $val)
1704
				{
1705
					if (is_array($val))
1706
					{
1707
						$val = implode("\n", $val);
1708
					}
1709
					print $name . ': ' . htmlentities($val) . "\n";
1710
				}
1711
1712
				print "---END---\n</PRE>";
1713
			}
1714
1715
			if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1716
			{
1717
				$this->errstr='no response';
1718
				$resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1719
				curl_close($curl);
1720
				if($keepalive)
1721
				{
1722
					$this->xmlrpc_curl_handle = null;
1723
				}
1724
			}
1725
			else
1726
			{
1727
				if(!$keepalive)
1728
				{
1729
					curl_close($curl);
1730
				}
1731
				$resp =& $msg->parseResponse($result, true, $this->return_type);
1732
				// if we got back a 302, we can not reuse the curl handle for later calls
1733
				if($resp->faultCode() == $GLOBALS['xmlrpcerr']['http_error'] && $keepalive)
1734
				{
1735
					curl_close($curl);
1736
					$this->xmlrpc_curl_handle = null;
1737
				}
1738
			}
1739
			return $resp;
1740
		}
1741
1742
		/**
1743
		* Send an array of request messages and return an array of responses.
1744
		* Unless $this->no_multicall has been set to true, it will try first
1745
		* to use one single xmlrpc call to server method system.multicall, and
1746
		* revert to sending many successive calls in case of failure.
1747
		* This failure is also stored in $this->no_multicall for subsequent calls.
1748
		* Unfortunately, there is no server error code universally used to denote
1749
		* the fact that multicall is unsupported, so there is no way to reliably
1750
		* distinguish between that and a temporary failure.
1751
		* If you are sure that server supports multicall and do not want to
1752
		* fallback to using many single calls, set the fourth parameter to FALSE.
1753
		*
1754
		* NB: trying to shoehorn extra functionality into existing syntax has resulted
1755
		* in pretty much convoluted code...
1756
		*
1757
		* @param array $msgs an array of xmlrpcmsg objects
1758
		* @param integer $timeout connection timeout (in seconds)
1759
		* @param string $method the http protocol variant to be used
1760
		* @param boolean fallback When true, upon receiving an error during multicall, multiple single calls will be attempted
1761
		* @return array
1762
		* @access public
1763
		*/
1764
		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...
1765
		{
1766
			if ($method == '')
1767
			{
1768
				$method = $this->method;
1769
			}
1770
			if(!$this->no_multicall)
1771
			{
1772
				$results = $this->_try_multicall($msgs, $timeout, $method);
1773
				if(is_array($results))
1774
				{
1775
					// System.multicall succeeded
1776
					return $results;
1777
				}
1778
				else
1779
				{
1780
					// either system.multicall is unsupported by server,
1781
					// or call failed for some other reason.
1782
					if ($fallback)
1783
					{
1784
						// Don't try it next time...
1785
						$this->no_multicall = true;
1786
					}
1787
					else
1788
					{
1789
						if (is_a($results, 'xmlrpcresp'))
1790
						{
1791
							$result = $results;
1792
						}
1793
						else
1794
						{
1795
							$result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1796
						}
1797
					}
1798
				}
1799
			}
1800
			else
1801
			{
1802
				// override fallback, in case careless user tries to do two
1803
				// opposite things at the same time
1804
				$fallback = true;
1805
			}
1806
1807
			$results = array();
1808
			if ($fallback)
1809
			{
1810
				// system.multicall is (probably) unsupported by server:
1811
				// emulate multicall via multiple requests
1812
				foreach($msgs as $msg)
1813
				{
1814
					$results[] =& $this->send($msg, $timeout, $method);
1815
				}
1816
			}
1817
			else
1818
			{
1819
				// user does NOT want to fallback on many single calls:
1820
				// since we should always return an array of responses,
1821
				// return an array with the same error repeated n times
1822
				foreach($msgs as $msg)
1823
				{
1824
					$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...
1825
				}
1826
			}
1827
			return $results;
1828
		}
1829
1830
		/**
1831
		* Attempt to boxcar $msgs via system.multicall.
1832
		* Returns either an array of xmlrpcreponses, an xmlrpc error response
1833
		* or false (when received response does not respect valid multicall syntax)
1834
		* @access private
1835
		*/
1836
		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...
1837
		{
1838
			// Construct multicall message
1839
			$calls = array();
1840
			foreach($msgs as $msg)
1841
			{
1842
				$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...
1843
				$numParams = $msg->getNumParams();
1844
				$params = array();
1845
				for($i = 0; $i < $numParams; $i++)
1846
				{
1847
					$params[$i] = $msg->getParam($i);
1848
				}
1849
				$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...
1850
				$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...
1851
			}
1852
			$multicall = new xmlrpcmsg('system.multicall');
1853
			$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...
1854
1855
			// Attempt RPC call
1856
			$result =& $this->send($multicall, $timeout, $method);
1857
1858
			if($result->faultCode() != 0)
1859
			{
1860
				// call to system.multicall failed
1861
				return $result;
1862
			}
1863
1864
			// Unpack responses.
1865
			$rets = $result->value();
1866
1867
			if ($this->return_type == 'xml')
1868
			{
1869
					return $rets;
1870
			}
1871
			else if ($this->return_type == 'phpvals')
1872
			{
1873
				///@todo test this code branch...
1874
				$rets = $result->value();
1875
				if(!is_array($rets))
1876
				{
1877
					return false;		// bad return type from system.multicall
1878
				}
1879
				$numRets = count($rets);
1880
				if($numRets != count($msgs))
1881
				{
1882
					return false;		// wrong number of return values.
1883
				}
1884
1885
				$response = array();
1886
				for($i = 0; $i < $numRets; $i++)
1887
				{
1888
					$val = $rets[$i];
1889
					if (!is_array($val)) {
1890
						return false;
1891
					}
1892
					switch(count($val))
1893
					{
1894
						case 1:
1895
							if(!isset($val[0]))
1896
							{
1897
								return false;		// Bad value
1898
							}
1899
							// Normal return value
1900
							$response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1901
							break;
1902
						case 2:
1903
							///	@todo remove usage of @: it is apparently quite slow
1904
							$code = @$val['faultCode'];
1905
							if(!is_int($code))
1906
							{
1907
								return false;
1908
							}
1909
							$str = @$val['faultString'];
1910
							if(!is_string($str))
1911
							{
1912
								return false;
1913
							}
1914
							$response[$i] = new xmlrpcresp(0, $code, $str);
1915
							break;
1916
						default:
1917
							return false;
1918
					}
1919
				}
1920
				return $response;
1921
			}
1922
			else // return type == 'xmlrpcvals'
1923
			{
1924
				$rets = $result->value();
1925
				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...
1926
				{
1927
					return false;		// bad return type from system.multicall
1928
				}
1929
				$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...
1930
				if($numRets != count($msgs))
1931
				{
1932
					return false;		// wrong number of return values.
1933
				}
1934
1935
				$response = array();
1936
				for($i = 0; $i < $numRets; $i++)
1937
				{
1938
					$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...
1939
					switch($val->kindOf())
1940
					{
1941
						case 'array':
1942
							if($val->arraysize() != 1)
1943
							{
1944
								return false;		// Bad value
1945
							}
1946
							// Normal return value
1947
							$response[$i] = new xmlrpcresp($val->arraymem(0));
1948
							break;
1949
						case 'struct':
1950
							$code = $val->structmem('faultCode');
1951
							if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1952
							{
1953
								return false;
1954
							}
1955
							$str = $val->structmem('faultString');
1956
							if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1957
							{
1958
								return false;
1959
							}
1960
							$response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1961
							break;
1962
						default:
1963
							return false;
1964
					}
1965
				}
1966
				return $response;
1967
			}
1968
		}
1969
	} // end class xmlrpc_client
1970
1971
	class xmlrpcresp
1972
	{
1973
		var $val = 0;
1974
		var $valtyp;
1975
		var $errno = 0;
1976
		var $errstr = '';
1977
		var $payload;
1978
		var $hdrs = array();
1979
		var $_cookies = array();
1980
		var $content_type = 'text/xml';
1981
		var $raw_data = '';
1982
1983
		/**
1984
		* @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1985
		* @param integer $fcode set it to anything but 0 to create an error response
1986
		* @param string $fstr the error string, in case of an error response
1987
		* @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1988
		*
1989
		* @todo add check that $val / $fcode / $fstr is of correct type???
1990
		* NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1991
		* php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1992
		*/
1993
		function __construct($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...
1994
		{
1995
			if($fcode != 0)
1996
			{
1997
				// error response
1998
				$this->errno = $fcode;
1999
				$this->errstr = $fstr;
2000
				//$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
2001
			}
2002
			else
2003
			{
2004
				// successful response
2005
				$this->val = $val;
2006
				if ($valtyp == '')
2007
				{
2008
					// user did not declare type of response value: try to guess it
2009
					if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
2010
					{
2011
						$this->valtyp = 'xmlrpcvals';
2012
					}
2013
					else if (is_string($this->val))
2014
					{
2015
						$this->valtyp = 'xml';
2016
2017
					}
2018
					else
2019
					{
2020
						$this->valtyp = 'phpvals';
2021
					}
2022
				}
2023
				else
2024
				{
2025
					// user declares type of resp value: believe him
2026
					$this->valtyp = $valtyp;
2027
				}
2028
			}
2029
		}
2030
2031
		/**
2032
		* @deprecated
2033
		*/
2034
		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...
2035
		{
2036
			self::__construct($val, $fcode, $fstr, $valtyp);
2037
		}
2038
2039
		/**
2040
		* Returns the error code of the response.
2041
		* @return integer the error code of this response (0 for not-error responses)
2042
		* @access public
2043
		*/
2044
		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...
2045
		{
2046
			return $this->errno;
2047
		}
2048
2049
		/**
2050
		* Returns the error code of the response.
2051
		* @return string the error string of this response ('' for not-error responses)
2052
		* @access public
2053
		*/
2054
		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...
2055
		{
2056
			return $this->errstr;
2057
		}
2058
2059
		/**
2060
		* Returns the value received by the server.
2061
		* @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
2062
		* @access public
2063
		*/
2064
		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...
2065
		{
2066
			return $this->val;
2067
		}
2068
2069
		/**
2070
		* Returns an array with the cookies received from the server.
2071
		* Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2072
		* with attributes being e.g. 'expires', 'path', domain'.
2073
		* NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2074
		* are still present in the array. It is up to the user-defined code to decide
2075
		* how to use the received cookies, and whether they have to be sent back with the next
2076
		* request to the server (using xmlrpc_client::setCookie) or not
2077
		* @return array array of cookies received from the server
2078
		* @access public
2079
		*/
2080
		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...
2081
		{
2082
			return $this->_cookies;
2083
		}
2084
2085
		/**
2086
		* Returns xml representation of the response. XML prologue not included
2087
		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2088
		* @return string the xml representation of the response
2089
		* @access public
2090
		*/
2091
		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...
2092
		{
2093
			if ($charset_encoding != '')
2094
				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2095
			else
2096
				$this->content_type = 'text/xml';
2097
			if ($GLOBALS['xmlrpc_null_apache_encoding'])
2098
			{
2099
				$result = "<methodResponse xmlns:ex=\"".$GLOBALS['xmlrpc_null_apache_encoding_ns']."\">\n";
2100
			}
2101
			else
2102
			{
2103
			$result = "<methodResponse>\n";
2104
			}
2105
			if($this->errno)
2106
			{
2107
				// G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2108
				// by xml-encoding non ascii chars
2109
				$result .= "<fault>\n" .
2110
"<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2111
"</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2112
xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2113
"</struct>\n</value>\n</fault>";
2114
			}
2115
			else
2116
			{
2117
				if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2118
				{
2119
					if (is_string($this->val) && $this->valtyp == 'xml')
2120
					{
2121
						$result .= "<params>\n<param>\n" .
2122
							$this->val .
2123
							"</param>\n</params>";
2124
					}
2125
					else
2126
					{
2127
						/// @todo try to build something serializable?
2128
						die('cannot serialize xmlrpcresp objects whose content is native php values');
2129
					}
2130
				}
2131
				else
2132
				{
2133
					$result .= "<params>\n<param>\n" .
2134
						$this->val->serialize($charset_encoding) .
2135
						"</param>\n</params>";
2136
				}
2137
			}
2138
			$result .= "\n</methodResponse>";
2139
			$this->payload = $result;
2140
			return $result;
2141
		}
2142
	}
2143
2144
	class xmlrpcmsg
2145
	{
2146
		var $payload;
2147
		var $methodname;
2148
		var $params=array();
2149
		var $debug=0;
2150
		var $content_type = 'text/xml';
2151
2152
		/**
2153
		* @param string $meth the name of the method to invoke
2154
		* @param array $pars array of parameters to be passed to the method (xmlrpcval objects)
2155
		*/
2156
		function __construct($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...
2157
		{
2158
			$this->methodname=$meth;
2159
			if(is_array($pars) && count($pars)>0)
2160
			{
2161
				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...
2162
				{
2163
					$this->addParam($pars[$i]);
2164
				}
2165
			}
2166
		}
2167
2168
				/**
2169
		* @deprecated
2170
		*/
2171
		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...
2172
		{
2173
			self::__construct($meth, $pars);
2174
		}
2175
2176
		/**
2177
		* @access private
2178
		*/
2179
		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...
2180
		{
2181
			if ($charset_encoding != '')
2182
			{
2183
				return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2184
			}
2185
			else
2186
			{
2187
				return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2188
			}
2189
		}
2190
2191
		/**
2192
		* @access private
2193
		*/
2194
		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...
2195
		{
2196
			return '</methodCall>';
2197
		}
2198
2199
		/**
2200
		* @access private
2201
		*/
2202
		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...
2203
		{
2204
			return 'msg';
2205
		}
2206
2207
		/**
2208
		* @access private
2209
		*/
2210
		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...
2211
		{
2212
			if ($charset_encoding != '')
2213
				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2214
			else
2215
				$this->content_type = 'text/xml';
2216
			$this->payload=$this->xml_header($charset_encoding);
2217
			$this->payload.='<methodName>' . xmlrpc_encode_entitites($this->methodname, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</methodName>\n";
2218
			$this->payload.="<params>\n";
2219
			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...
2220
			{
2221
				$p=$this->params[$i];
2222
				$this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2223
				"</param>\n";
2224
			}
2225
			$this->payload.="</params>\n";
2226
			$this->payload.=$this->xml_footer();
2227
		}
2228
2229
		/**
2230
		* Gets/sets the xmlrpc method to be invoked
2231
		* @param string $meth the method to be set (leave empty not to set it)
2232
		* @return string the method that will be invoked
2233
		* @access public
2234
		*/
2235
		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...
2236
		{
2237
			if($meth!='')
2238
			{
2239
				$this->methodname=$meth;
2240
			}
2241
			return $this->methodname;
2242
		}
2243
2244
		/**
2245
		* Returns xml representation of the message. XML prologue included
2246
		* @param string $charset_encoding
2247
		* @return string the xml representation of the message, xml prologue included
2248
		* @access public
2249
		*/
2250
		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...
2251
		{
2252
			$this->createPayload($charset_encoding);
2253
			return $this->payload;
2254
		}
2255
2256
		/**
2257
		* Add a parameter to the list of parameters to be used upon method invocation
2258
		* @param xmlrpcval $par
2259
		* @return boolean false on failure
2260
		* @access public
2261
		*/
2262
		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...
2263
		{
2264
			// add check: do not add to self params which are not xmlrpcvals
2265
			if(is_object($par) && is_a($par, 'xmlrpcval'))
2266
			{
2267
				$this->params[]=$par;
2268
				return true;
2269
			}
2270
			else
2271
			{
2272
				return false;
2273
			}
2274
		}
2275
2276
		/**
2277
		* Returns the nth parameter in the message. The index zero-based.
2278
		* @param integer $i the index of the parameter to fetch (zero based)
2279
		* @return xmlrpcval the i-th parameter
2280
		* @access public
2281
		*/
2282
		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...
2283
2284
		/**
2285
		* Returns the number of parameters in the messge.
2286
		* @return integer the number of parameters currently set
2287
		* @access public
2288
		*/
2289
		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...
2290
2291
		/**
2292
		* Given an open file handle, read all data available and parse it as axmlrpc response.
2293
		* NB: the file handle is not closed by this function.
2294
		* NNB: might have trouble in rare cases to work on network streams, as we
2295
		*      check for a read of 0 bytes instead of feof($fp).
2296
		*      But since checking for feof(null) returns false, we would risk an
2297
		*      infinite loop in that case, because we cannot trust the caller
2298
		*      to give us a valid pointer to an open file...
2299
		* @access public
2300
		* @param resource $fp stream pointer
2301
		* @return xmlrpcresp
2302
		* @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2303
		*/
2304
		function &parseResponseFile($fp)
2305
		{
2306
			$ipd='';
2307
			while($data=fread($fp, 32768))
2308
			{
2309
				$ipd.=$data;
2310
			}
2311
			//fclose($fp);
2312
			$r =& $this->parseResponse($ipd);
2313
			return $r;
2314
		}
2315
2316
		/**
2317
		* Parses HTTP headers and separates them from data.
2318
		* @access private
2319
		*/
2320
		function &parseResponseHeaders(&$data, $headers_processed=false)
2321
		{
2322
				// Support "web-proxy-tunelling" connections for https through proxies
2323
				if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2324
				{
2325
					// Look for CR/LF or simple LF as line separator,
2326
					// (even though it is not valid http)
2327
					$pos = strpos($data,"\r\n\r\n");
2328 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...
2329
					{
2330
						$bd = $pos+4;
2331
					}
2332
					else
2333
					{
2334
						$pos = strpos($data,"\n\n");
2335
						if($pos || is_int($pos))
2336
						{
2337
							$bd = $pos+2;
2338
						}
2339
						else
2340
						{
2341
							// No separation between response headers and body: fault?
2342
							$bd = 0;
2343
						}
2344
					}
2345 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...
2346
					{
2347
						// this filters out all http headers from proxy.
2348
						// maybe we could take them into account, too?
2349
						$data = substr($data, $bd);
2350
					}
2351
					else
2352
					{
2353
						error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2354
						$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2355
						return $r;
2356
					}
2357
				}
2358
2359
				// Strip HTTP 1.1 100 Continue header if present
2360
				while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2361
				{
2362
					$pos = strpos($data, 'HTTP', 12);
2363
					// server sent a Continue header without any (valid) content following...
2364
					// give the client a chance to know it
2365
					if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2366
					{
2367
						break;
2368
					}
2369
					$data = substr($data, $pos);
2370
				}
2371
				if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2372
				{
2373
					$errstr= substr($data, 0, strpos($data, "\n")-1);
2374
					error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2375
					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2376
					return $r;
2377
				}
2378
2379
				$GLOBALS['_xh']['headers'] = array();
2380
				$GLOBALS['_xh']['cookies'] = array();
2381
2382
				// be tolerant to usage of \n instead of \r\n to separate headers and data
2383
				// (even though it is not valid http)
2384
				$pos = strpos($data,"\r\n\r\n");
2385 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...
2386
				{
2387
					$bd = $pos+4;
2388
				}
2389
				else
2390
				{
2391
					$pos = strpos($data,"\n\n");
2392
					if($pos || is_int($pos))
2393
					{
2394
						$bd = $pos+2;
2395
					}
2396
					else
2397
					{
2398
						// No separation between response headers and body: fault?
2399
						// we could take some action here instead of going on...
2400
						$bd = 0;
2401
					}
2402
				}
2403
				// be tolerant to line endings, and extra empty lines
2404
				$ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2405
				while(list(,$line) = @each($ar))
2406
				{
2407
					// take care of multi-line headers and cookies
2408
					$arr = explode(':',$line,2);
2409
					if(count($arr) > 1)
2410
					{
2411
						$header_name = strtolower(trim($arr[0]));
2412
						/// @todo some other headers (the ones that allow a CSV list of values)
2413
						/// do allow many values to be passed using multiple header lines.
2414
						/// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2415
						/// instead of replacing it for those...
2416
						if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2417
						{
2418
							if ($header_name == 'set-cookie2')
2419
							{
2420
								// version 2 cookies:
2421
								// there could be many cookies on one line, comma separated
2422
								$cookies = explode(',', $arr[1]);
2423
							}
2424
							else
2425
							{
2426
								$cookies = array($arr[1]);
2427
							}
2428
							foreach ($cookies as $cookie)
2429
							{
2430
								// glue together all received cookies, using a comma to separate them
2431
								// (same as php does with getallheaders())
2432
								if (isset($GLOBALS['_xh']['headers'][$header_name]))
2433
									$GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2434
								else
2435
									$GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2436
								// parse cookie attributes, in case user wants to correctly honour them
2437
								// feature creep: only allow rfc-compliant cookie attributes?
2438
								// @todo support for server sending multiple time cookie with same name, but using different PATHs
2439
								$cookie = explode(';', $cookie);
2440
								foreach ($cookie as $pos => $val)
2441
								{
2442
									$val = explode('=', $val, 2);
2443
									$tag = trim($val[0]);
2444
									$val = trim(@$val[1]);
2445
									/// @todo with version 1 cookies, we should strip leading and trailing " chars
2446
									if ($pos == 0)
2447
									{
2448
										$cookiename = $tag;
2449
										$GLOBALS['_xh']['cookies'][$tag] = array();
2450
										$GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2451
									}
2452
									else
2453
									{
2454
										if ($tag != 'value')
2455
										{
2456
											$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...
2457
										}
2458
									}
2459
								}
2460
							}
2461
						}
2462
						else
2463
						{
2464
							$GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2465
						}
2466
					}
2467
					elseif(isset($header_name))
2468
					{
2469
						///	@todo version1 cookies might span multiple lines, thus breaking the parsing above
2470
						$GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2471
					}
2472
				}
2473
2474
				$data = substr($data, $bd);
2475
2476
				if($this->debug && count($GLOBALS['_xh']['headers']))
2477
				{
2478
					print '<PRE>';
2479
					foreach($GLOBALS['_xh']['headers'] as $header => $value)
2480
					{
2481
						print htmlentities("HEADER: $header: $value\n");
2482
					}
2483
					foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2484
					{
2485
						print htmlentities("COOKIE: $header={$value['value']}\n");
2486
					}
2487
					print "</PRE>\n";
2488
				}
2489
2490
				// if CURL was used for the call, http headers have been processed,
2491
				// and dechunking + reinflating have been carried out
2492
				if(!$headers_processed)
2493
				{
2494
					// Decode chunked encoding sent by http 1.1 servers
2495
					if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2496
					{
2497 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...
2498
						{
2499
							error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2500
							$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2501
							return $r;
2502
						}
2503
					}
2504
2505
					// Decode gzip-compressed stuff
2506
					// code shamelessly inspired from nusoap library by Dietrich Ayala
2507
					if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2508
					{
2509
						$GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2510
						if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2511
						{
2512
							// if decoding works, use it. else assume data wasn't gzencoded
2513
							if(function_exists('gzinflate'))
2514
							{
2515
								if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2516
								{
2517
									$data = $degzdata;
2518 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...
2519
									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2520
								}
2521
								elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2522
								{
2523
									$data = $degzdata;
2524 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...
2525
									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2526
								}
2527
								else
2528
								{
2529
									error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2530
									$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2531
									return $r;
2532
								}
2533
							}
2534
							else
2535
							{
2536
								error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2537
								$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2538
								return $r;
2539
							}
2540
						}
2541
					}
2542
				} // end of 'if needed, de-chunk, re-inflate response'
2543
2544
				// real stupid hack to avoid PHP complaining about returning NULL by ref
2545
				$r = null;
2546
				$r =& $r;
2547
				return $r;
2548
		}
2549
2550
		/**
2551
		* Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2552
		* @param string $data the xmlrpc response, eventually including http headers
2553
		* @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2554
		* @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2555
		* @return xmlrpcresp
2556
		* @access public
2557
		*/
2558
		function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2559
		{
2560
			if($this->debug)
2561
			{
2562
				//by maHo, replaced htmlspecialchars with htmlentities
2563
				print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2564
			}
2565
2566
			if($data == '')
2567
			{
2568
				error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2569
				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2570
				return $r;
2571
			}
2572
2573
			$GLOBALS['_xh']=array();
2574
2575
			$raw_data = $data;
2576
			// parse the HTTP headers of the response, if present, and separate them from data
2577
			if(substr($data, 0, 4) == 'HTTP')
2578
			{
2579
				$r =& $this->parseResponseHeaders($data, $headers_processed);
2580
				if ($r)
2581
				{
2582
					// failed processing of HTTP response headers
2583
					// save into response obj the full payload received, for debugging
2584
					$r->raw_data = $data;
2585
					return $r;
2586
				}
2587
			}
2588
			else
2589
			{
2590
				$GLOBALS['_xh']['headers'] = array();
2591
				$GLOBALS['_xh']['cookies'] = array();
2592
			}
2593
2594
			if($this->debug)
2595
			{
2596
				$start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2597
				if ($start)
2598
				{
2599
					$start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2600
					$end = strpos($data, '-->', $start);
2601
					$comments = substr($data, $start, $end-$start);
2602
					print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2603
				}
2604
			}
2605
2606
			// be tolerant of extra whitespace in response body
2607
			$data = trim($data);
2608
2609
			/// @todo return an error msg if $data=='' ?
2610
2611
			// be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2612
			// idea from Luca Mariano <[email protected]> originally in PEARified version of the lib
2613
			$pos = strrpos($data, '</methodResponse>');
2614
			if($pos !== false)
2615
			{
2616
				$data = substr($data, 0, $pos+17);
2617
			}
2618
2619
			// if user wants back raw xml, give it to him
2620
			if ($return_type == 'xml')
2621
			{
2622
				$r = new xmlrpcresp($data, 0, '', 'xml');
2623
				$r->hdrs = $GLOBALS['_xh']['headers'];
2624
				$r->_cookies = $GLOBALS['_xh']['cookies'];
2625
				$r->raw_data = $raw_data;
2626
				return $r;
2627
			}
2628
2629
			// try to 'guestimate' the character encoding of the received response
2630
			$resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2631
2632
			$GLOBALS['_xh']['ac']='';
2633
			//$GLOBALS['_xh']['qt']=''; //unused...
2634
			$GLOBALS['_xh']['stack'] = array();
2635
			$GLOBALS['_xh']['valuestack'] = array();
2636
			$GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2637
			$GLOBALS['_xh']['isf_reason']='';
2638
			$GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2639
2640
			// Since parsing will fail if charset is not specified in the xml prologue,
2641
			// the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that...
2642
			// The following code might be better for mb_string enabled installs, but
2643
			// makes the lib about 200% slower...
2644
			//if (!is_valid_charset($resp_encoding, array('UTF-8')))
2645 View Code Duplication
			if (!in_array($resp_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($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...
2646
				if ($resp_encoding == 'ISO-8859-1') {
2647
					$data = utf8_encode($data);
2648
				} else {
2649
					if (extension_loaded('mbstring')) {
2650
						$data = mb_convert_encoding($data, 'UTF-8', $resp_encoding);
2651
					} else {
2652
						error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $resp_encoding);
2653
					}
2654
				}
2655
			}
2656
2657
			$parser = xml_parser_create();
2658
			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2659
			// G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2660
			// the xml parser to give us back data in the expected charset.
2661
			// What if internal encoding is not in one of the 3 allowed?
2662
			// we use the broadest one, ie. utf8
2663
			// This allows to send data which is native in various charset,
2664
			// by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2665 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...
2666
			{
2667
				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2668
			}
2669
			else
2670
			{
2671
				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2672
			}
2673
2674
			if ($return_type == 'phpvals')
2675
			{
2676
				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2677
			}
2678
			else
2679
			{
2680
				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2681
			}
2682
2683
			xml_set_character_data_handler($parser, 'xmlrpc_cd');
2684
			xml_set_default_handler($parser, 'xmlrpc_dh');
2685
2686
			// first error check: xml not well formed
2687
			if(!xml_parse($parser, $data, count($data)))
2688
			{
2689
				// thanks to Peter Kocks <[email protected]>
2690
				if((xml_get_current_line_number($parser)) == 1)
2691
				{
2692
					$errstr = 'XML error at line 1, check URL';
2693
				}
2694
				else
2695
				{
2696
					$errstr = sprintf('XML error: %s at line %d, column %d',
2697
						xml_error_string(xml_get_error_code($parser)),
2698
						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2699
				}
2700
				error_log($errstr);
2701
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2702
				xml_parser_free($parser);
2703
				if($this->debug)
2704
				{
2705
					print $errstr;
2706
				}
2707
				$r->hdrs = $GLOBALS['_xh']['headers'];
2708
				$r->_cookies = $GLOBALS['_xh']['cookies'];
2709
				$r->raw_data = $raw_data;
2710
				return $r;
2711
			}
2712
			xml_parser_free($parser);
2713
			// second error check: xml well formed but not xml-rpc compliant
2714
			if ($GLOBALS['_xh']['isf'] > 1)
2715
			{
2716
				if ($this->debug)
2717
				{
2718
					/// @todo echo something for user?
2719
				}
2720
2721
				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2722
				$GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2723
			}
2724
			// third error check: parsing of the response has somehow gone boink.
2725
			// NB: shall we omit this check, since we trust the parsing code?
2726
			elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2727
			{
2728
				// something odd has happened
2729
				// and it's time to generate a client side error
2730
				// indicating something odd went on
2731
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2732
					$GLOBALS['xmlrpcstr']['invalid_return']);
2733
			}
2734
			else
2735
			{
2736
				if ($this->debug)
2737
				{
2738
					print "<PRE>---PARSED---\n";
2739
					// somehow htmlentities chokes on var_export, and some full html string...
2740
					//print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2741
					print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2742
					print "\n---END---</PRE>";
2743
				}
2744
2745
				// note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2746
				$v =& $GLOBALS['_xh']['value'];
2747
2748
				if($GLOBALS['_xh']['isf'])
2749
				{
2750
					/// @todo we should test here if server sent an int and a string,
2751
					/// and/or coerce them into such...
2752
					if ($return_type == 'xmlrpcvals')
2753
					{
2754
						$errno_v = $v->structmem('faultCode');
2755
						$errstr_v = $v->structmem('faultString');
2756
						$errno = $errno_v->scalarval();
2757
						$errstr = $errstr_v->scalarval();
2758
					}
2759
					else
2760
					{
2761
						$errno = $v['faultCode'];
2762
						$errstr = $v['faultString'];
2763
					}
2764
2765
					if($errno == 0)
2766
					{
2767
						// FAULT returned, errno needs to reflect that
2768
						$errno = -1;
2769
					}
2770
2771
					$r = new xmlrpcresp(0, $errno, $errstr);
2772
				}
2773
				else
2774
				{
2775
					$r=new xmlrpcresp($v, 0, '', $return_type);
2776
				}
2777
			}
2778
2779
			$r->hdrs = $GLOBALS['_xh']['headers'];
2780
			$r->_cookies = $GLOBALS['_xh']['cookies'];
2781
			$r->raw_data = $raw_data;
2782
			return $r;
2783
		}
2784
	}
2785
2786
	class xmlrpcval
2787
	{
2788
		var $me=array();
2789
		var $mytype=0;
2790
		var $_php_class=null;
2791
2792
		/**
2793
		* @param mixed $val
2794
		* @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2795
		*/
2796
		function __construct($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...
2797
		{
2798
			/// @todo: optimization creep - do not call addXX, do it all inline.
2799
			/// downside: booleans will not be coerced anymore
2800
			if($val!==-1 || $type!='')
2801
			{
2802
				// optimization creep: inlined all work done by constructor
2803
				switch($type)
2804
				{
2805
					case '':
2806
						$this->mytype=1;
2807
						$this->me['string']=$val;
2808
						break;
2809
					case 'i4':
2810
					case 'int':
2811
					case 'double':
2812
					case 'string':
2813
					case 'boolean':
2814
					case 'dateTime.iso8601':
2815
					case 'base64':
2816
					case 'null':
2817
						$this->mytype=1;
2818
						$this->me[$type]=$val;
2819
						break;
2820
					case 'array':
2821
						$this->mytype=2;
2822
						$this->me['array']=$val;
2823
						break;
2824
					case 'struct':
2825
						$this->mytype=3;
2826
						$this->me['struct']=$val;
2827
						break;
2828
					default:
2829
						error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2830
				}
2831
				/*if($type=='')
2832
				{
2833
					$type='string';
2834
				}
2835
				if($GLOBALS['xmlrpcTypes'][$type]==1)
2836
				{
2837
					$this->addScalar($val,$type);
2838
				}
2839
				elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2840
				{
2841
					$this->addArray($val);
2842
				}
2843
				elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2844
				{
2845
					$this->addStruct($val);
2846
				}*/
2847
			}
2848
		}
2849
2850
		/**
2851
		* @deprecated
2852
		*/
2853
		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...
2854
		{
2855
			self::__construct($val, $type);
2856
		}
2857
2858
		/**
2859
		* Add a single php value to an (unitialized) xmlrpcval
2860
		* @param mixed $val
2861
		* @param string $type
2862
		* @return int 1 or 0 on failure
2863
		*/
2864
		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...
2865
		{
2866
			$typeof=@$GLOBALS['xmlrpcTypes'][$type];
2867
			if($typeof!=1)
2868
			{
2869
				error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2870
				return 0;
2871
			}
2872
2873
			// coerce booleans into correct values
2874
			// NB: we should either do it for datetimes, integers and doubles, too,
2875
			// or just plain remove this check, implemented on booleans only...
2876
			if($type==$GLOBALS['xmlrpcBoolean'])
2877
			{
2878
				if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2879
				{
2880
					$val=true;
2881
				}
2882
				else
2883
				{
2884
					$val=false;
2885
				}
2886
			}
2887
2888
			switch($this->mytype)
2889
			{
2890
				case 1:
2891
					error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2892
					return 0;
2893
				case 3:
2894
					error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2895
					return 0;
2896
				case 2:
2897
					// we're adding a scalar value to an array here
2898
					//$ar=$this->me['array'];
2899
					//$ar[]=new xmlrpcval($val, $type);
2900
					//$this->me['array']=$ar;
2901
					// Faster (?) avoid all the costly array-copy-by-val done here...
2902
					$this->me['array'][]=new xmlrpcval($val, $type);
2903
					return 1;
2904
				default:
2905
					// a scalar, so set the value and remember we're scalar
2906
					$this->me[$type]=$val;
2907
					$this->mytype=$typeof;
2908
					return 1;
2909
			}
2910
		}
2911
2912
		/**
2913
		* Add an array of xmlrpcval objects to an xmlrpcval
2914
		* @param array $vals
2915
		* @return int 1 or 0 on failure
2916
		* @access public
2917
		*
2918
		* @todo add some checking for $vals to be an array of xmlrpcvals?
2919
		*/
2920 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...
2921
		{
2922
			if($this->mytype==0)
2923
			{
2924
				$this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2925
				$this->me['array']=$vals;
2926
				return 1;
2927
			}
2928
			elseif($this->mytype==2)
2929
			{
2930
				// we're adding to an array here
2931
				$this->me['array'] = array_merge($this->me['array'], $vals);
2932
				return 1;
2933
			}
2934
			else
2935
			{
2936
				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2937
				return 0;
2938
			}
2939
		}
2940
2941
		/**
2942
		* Add an array of named xmlrpcval objects to an xmlrpcval
2943
		* @param array $vals
2944
		* @return int 1 or 0 on failure
2945
		* @access public
2946
		*
2947
		* @todo add some checking for $vals to be an array?
2948
		*/
2949 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...
2950
		{
2951
			if($this->mytype==0)
2952
			{
2953
				$this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2954
				$this->me['struct']=$vals;
2955
				return 1;
2956
			}
2957
			elseif($this->mytype==3)
2958
			{
2959
				// we're adding to a struct here
2960
				$this->me['struct'] = array_merge($this->me['struct'], $vals);
2961
				return 1;
2962
			}
2963
			else
2964
			{
2965
				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2966
				return 0;
2967
			}
2968
		}
2969
2970
		// poor man's version of print_r ???
2971
		// DEPRECATED!
2972
		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...
2973
		{
2974
			foreach($ar as $key => $val)
2975
			{
2976
				echo "$key => $val<br />";
2977
				if($key == 'array')
2978
				{
2979
					while(list($key2, $val2) = each($val))
2980
					{
2981
						echo "-- $key2 => $val2<br />";
2982
					}
2983
				}
2984
			}
2985
		}
2986
2987
		/**
2988
		* Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2989
		* @return string
2990
		* @access public
2991
		*/
2992
		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...
2993
		{
2994
			switch($this->mytype)
2995
			{
2996
				case 3:
2997
					return 'struct';
2998
					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...
2999
				case 2:
3000
					return 'array';
3001
					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...
3002
				case 1:
3003
					return 'scalar';
3004
					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...
3005
				default:
3006
					return 'undef';
3007
			}
3008
		}
3009
3010
		/**
3011
		* @access private
3012
		*/
3013
		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...
3014
		{
3015
			$rs='';
3016
			switch(@$GLOBALS['xmlrpcTypes'][$typ])
3017
			{
3018
				case 1:
3019
					switch($typ)
3020
					{
3021
						case $GLOBALS['xmlrpcBase64']:
3022
							$rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
3023
							break;
3024
						case $GLOBALS['xmlrpcBoolean']:
3025
							$rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
3026
							break;
3027
						case $GLOBALS['xmlrpcString']:
3028
							// G. Giunta 2005/2/13: do NOT use htmlentities, since
3029
							// it will produce named html entities, which are invalid xml
3030
							$rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
3031
							break;
3032
						case $GLOBALS['xmlrpcInt']:
3033
						case $GLOBALS['xmlrpcI4']:
3034
							$rs.="<${typ}>".(int)$val."</${typ}>";
3035
							break;
3036
						case $GLOBALS['xmlrpcDouble']:
3037
							// avoid using standard conversion of float to string because it is locale-dependent,
3038
							// and also because the xmlrpc spec forbids exponential notation.
3039
							// sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
3040
							// The code below tries its best at keeping max precision while avoiding exp notation,
3041
							// but there is of course no limit in the number of decimal places to be used...
3042
							$rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
3043
							break;
3044
						case $GLOBALS['xmlrpcDateTime']:
3045
							if (is_string($val))
3046
							{
3047
								$rs.="<${typ}>${val}</${typ}>";
3048
							}
3049
							else if(is_a($val, 'DateTime'))
3050
							{
3051
								$rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
3052
							}
3053
							else if(is_int($val))
3054
							{
3055
								$rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
3056
							}
3057
							else
3058
							{
3059
								// not really a good idea here: but what shall we output anyway? left for backward compat...
3060
								$rs.="<${typ}>${val}</${typ}>";
3061
							}
3062
							break;
3063
						case $GLOBALS['xmlrpcNull']:
3064
							if ($GLOBALS['xmlrpc_null_apache_encoding'])
3065
							{
3066
								$rs.="<ex:nil/>";
3067
							}
3068
							else
3069
							{
3070
								$rs.="<nil/>";
3071
							}
3072
							break;
3073
						default:
3074
							// no standard type value should arrive here, but provide a possibility
3075
							// for xmlrpcvals of unknown type...
3076
							$rs.="<${typ}>${val}</${typ}>";
3077
					}
3078
					break;
3079
				case 3:
3080
					// struct
3081
					if ($this->_php_class)
3082
					{
3083
						$rs.='<struct php_class="' . $this->_php_class . "\">\n";
3084
					}
3085
					else
3086
					{
3087
						$rs.="<struct>\n";
3088
					}
3089
					foreach($val as $key2 => $val2)
3090
					{
3091
						$rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3092
						//$rs.=$this->serializeval($val2);
3093
						$rs.=$val2->serialize($charset_encoding);
3094
						$rs.="</member>\n";
3095
					}
3096
					$rs.='</struct>';
3097
					break;
3098
				case 2:
3099
					// array
3100
					$rs.="<array>\n<data>\n";
3101
					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...
3102
					{
3103
						//$rs.=$this->serializeval($val[$i]);
3104
						$rs.=$val[$i]->serialize($charset_encoding);
3105
					}
3106
					$rs.="</data>\n</array>";
3107
					break;
3108
				default:
3109
					break;
3110
			}
3111
			return $rs;
3112
		}
3113
3114
		/**
3115
		* Returns xml representation of the value. XML prologue not included
3116
		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3117
		* @return string
3118
		* @access public
3119
		*/
3120
		function serialize($charset_encoding='')
3121
		{
3122
			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3123
			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3124
			//{
3125
				reset($this->me);
3126
				list($typ, $val) = each($this->me);
3127
				return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3128
			//}
3129
		}
3130
3131
		// DEPRECATED
3132
		function serializeval($o)
3133
		{
3134
			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3135
			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3136
			//{
3137
				$ar=$o->me;
3138
				reset($ar);
3139
				list($typ, $val) = each($ar);
3140
				return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3141
			//}
3142
		}
3143
3144
		/**
3145
		* Checks whether a struct member with a given name is present.
3146
		* Works only on xmlrpcvals of type struct.
3147
		* @param string $m the name of the struct member to be looked up
3148
		* @return boolean
3149
		* @access public
3150
		*/
3151
		function structmemexists($m)
3152
		{
3153
			return array_key_exists($m, $this->me['struct']);
3154
		}
3155
3156
		/**
3157
		* Returns the value of a given struct member (an xmlrpcval object in itself).
3158
		* Will raise a php warning if struct member of given name does not exist
3159
		* @param string $m the name of the struct member to be looked up
3160
		* @return xmlrpcval
3161
		* @access public
3162
		*/
3163
		function structmem($m)
3164
		{
3165
			return $this->me['struct'][$m];
3166
		}
3167
3168
		/**
3169
		* Reset internal pointer for xmlrpcvals of type struct.
3170
		* @access public
3171
		*/
3172
		function structreset()
3173
		{
3174
			reset($this->me['struct']);
3175
		}
3176
3177
		/**
3178
		* Return next member element for xmlrpcvals of type struct.
3179
		* @return xmlrpcval
3180
		* @access public
3181
		*/
3182
		function structeach()
3183
		{
3184
			return each($this->me['struct']);
3185
		}
3186
3187
		// DEPRECATED! this code looks like it is very fragile and has not been fixed
3188
		// for a long long time. Shall we remove it for 2.0?
3189
		function getval()
3190
		{
3191
			// UNSTABLE
3192
			reset($this->me);
3193
			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...
3194
			// contributed by I Sofer, 2001-03-24
3195
			// add support for nested arrays to scalarval
3196
			// i've created a new method here, so as to
3197
			// preserve back compatibility
3198
3199
			if(is_array($b))
3200
			{
3201
				@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...
3202
				while(list($id,$cont) = @each($b))
3203
				{
3204
					$b[$id] = $cont->scalarval();
3205
				}
3206
			}
3207
3208
			// add support for structures directly encoding php objects
3209
			if(is_object($b))
3210
			{
3211
				$t = get_object_vars($b);
3212
				@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...
3213
				while(list($id,$cont) = @each($t))
3214
				{
3215
					$t[$id] = $cont->scalarval();
3216
				}
3217
				@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...
3218
				while(list($id,$cont) = @each($t))
3219
				{
3220
					@$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...
3221
				}
3222
			}
3223
			// end contrib
3224
			return $b;
3225
		}
3226
3227
		/**
3228
		* Returns the value of a scalar xmlrpcval
3229
		* @return mixed
3230
		* @access public
3231
		*/
3232
		function scalarval()
3233
		{
3234
			reset($this->me);
3235
			list(,$b)=each($this->me);
3236
			return $b;
3237
		}
3238
3239
		/**
3240
		* Returns the type of the xmlrpcval.
3241
		* For integers, 'int' is always returned in place of 'i4'
3242
		* @return string
3243
		* @access public
3244
		*/
3245
		function scalartyp()
3246
		{
3247
			reset($this->me);
3248
			list($a,)=each($this->me);
3249
			if($a==$GLOBALS['xmlrpcI4'])
3250
			{
3251
				$a=$GLOBALS['xmlrpcInt'];
3252
			}
3253
			return $a;
3254
		}
3255
3256
		/**
3257
		* Returns the m-th member of an xmlrpcval of struct type
3258
		* @param integer $m the index of the value to be retrieved (zero based)
3259
		* @return xmlrpcval
3260
		* @access public
3261
		*/
3262
		function arraymem($m)
3263
		{
3264
			return $this->me['array'][$m];
3265
		}
3266
3267
		/**
3268
		* Returns the number of members in an xmlrpcval of array type
3269
		* @return integer
3270
		* @access public
3271
		*/
3272
		function arraysize()
3273
		{
3274
			return count($this->me['array']);
3275
		}
3276
3277
		/**
3278
		* Returns the number of members in an xmlrpcval of struct type
3279
		* @return integer
3280
		* @access public
3281
		*/
3282
		function structsize()
3283
		{
3284
			return count($this->me['struct']);
3285
		}
3286
	}
3287
3288
3289
	// date helpers
3290
3291
	/**
3292
	* Given a timestamp, return the corresponding ISO8601 encoded string.
3293
	*
3294
	* Really, timezones ought to be supported
3295
	* but the XML-RPC spec says:
3296
	*
3297
	* "Don't assume a timezone. It should be specified by the server in its
3298
	* documentation what assumptions it makes about timezones."
3299
	*
3300
	* These routines always assume localtime unless
3301
	* $utc is set to 1, in which case UTC is assumed
3302
	* and an adjustment for locale is made when encoding
3303
	*
3304
	* @param int $timet (timestamp)
3305
	* @param int $utc (0 or 1)
3306
	* @return string
3307
	*/
3308
	function iso8601_encode($timet, $utc=0)
3309
	{
3310
		if(!$utc)
3311
		{
3312
			$t=strftime("%Y%m%dT%H:%M:%S", $timet);
3313
		}
3314
		else
3315
		{
3316
			if(function_exists('gmstrftime'))
3317
			{
3318
				// gmstrftime doesn't exist in some versions
3319
				// of PHP
3320
				$t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3321
			}
3322
			else
3323
			{
3324
				$t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3325
			}
3326
		}
3327
		return $t;
3328
	}
3329
3330
	/**
3331
	* Given an ISO8601 date string, return a timet in the localtime, or UTC
3332
	* @param string $idate
3333
	* @param int $utc either 0 or 1
3334
	* @return int (datetime)
3335
	*/
3336
	function iso8601_decode($idate, $utc=0)
3337
	{
3338
		$t=0;
3339
		if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3340
		{
3341
			if($utc)
3342
			{
3343
				$t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3344
			}
3345
			else
3346
			{
3347
				$t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3348
			}
3349
		}
3350
		return $t;
3351
	}
3352
3353
	/**
3354
	* Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3355
	*
3356
	* Works with xmlrpc message objects as input, too.
3357
	*
3358
	* Given proper options parameter, can rebuild generic php object instances
3359
	* (provided those have been encoded to xmlrpc format using a corresponding
3360
	* option in php_xmlrpc_encode())
3361
	* PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3362
	* This means that the remote communication end can decide which php code will
3363
	* get executed on your server, leaving the door possibly open to 'php-injection'
3364
	* style of attacks (provided you have some classes defined on your server that
3365
	* might wreak havoc if instances are built outside an appropriate context).
3366
	* Make sure you trust the remote server/client before eanbling this!
3367
	*
3368
	* @author Dan Libby ([email protected])
3369
	*
3370
	* @param xmlrpcval $xmlrpc_val
3371
	* @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
3372
	* @return mixed
3373
	*/
3374
	function php_xmlrpc_decode($xmlrpc_val, $options=array())
3375
	{
3376
		switch($xmlrpc_val->kindOf())
3377
		{
3378
			case 'scalar':
3379
				if (in_array('extension_api', $options))
3380
				{
3381
					reset($xmlrpc_val->me);
3382
					list($typ,$val) = each($xmlrpc_val->me);
3383
					switch ($typ)
3384
					{
3385
						case 'dateTime.iso8601':
3386
							$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...
3387
							$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...
3388
							$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...
3389
							return $xmlrpc_val;
3390
						case 'base64':
3391
							$xmlrpc_val->scalar = $val;
3392
							$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...
3393
							return $xmlrpc_val;
3394
						default:
3395
							return $xmlrpc_val->scalarval();
3396
					}
3397
				}
3398
				if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3399
				{
3400
					// we return a Datetime object instead of a string
3401
					// since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3402
					// we cater to all 3 cases here
3403
					$out = $xmlrpc_val->scalarval();
3404
					if (is_string($out))
3405
					{
3406
						$out = strtotime($out);
3407
					}
3408
					if (is_int($out))
3409
					{
3410
						$result = new Datetime();
3411
						$result->setTimestamp($out);
3412
						return $result;
3413
					}
3414
					elseif (is_a($out, 'Datetime'))
3415
					{
3416
						return $out;
3417
					}
3418
				}
3419
				return $xmlrpc_val->scalarval();
3420
			case 'array':
3421
				$size = $xmlrpc_val->arraysize();
3422
				$arr = array();
3423
				for($i = 0; $i < $size; $i++)
3424
				{
3425
					$arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3426
				}
3427
				return $arr;
3428
			case 'struct':
3429
				$xmlrpc_val->structreset();
3430
				// If user said so, try to rebuild php objects for specific struct vals.
3431
				/// @todo should we raise a warning for class not found?
3432
				// shall we check for proper subclass of xmlrpcval instead of
3433
				// presence of _php_class to detect what we can do?
3434
				if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3435
					&& class_exists($xmlrpc_val->_php_class))
3436
				{
3437
					$obj = @new $xmlrpc_val->_php_class;
3438
					while(list($key,$value)=$xmlrpc_val->structeach())
3439
					{
3440
						$obj->$key = php_xmlrpc_decode($value, $options);
3441
					}
3442
					return $obj;
3443
				}
3444
				else
3445
				{
3446
					$arr = array();
3447
					while(list($key,$value)=$xmlrpc_val->structeach())
3448
					{
3449
						$arr[$key] = php_xmlrpc_decode($value, $options);
3450
					}
3451
					return $arr;
3452
				}
3453
			case 'msg':
3454
				$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...
3455
				$arr = array();
3456
				for($i = 0; $i < $paramcount; $i++)
3457
				{
3458
					$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...
3459
				}
3460
				return $arr;
3461
			}
3462
	}
3463
3464
	// This constant left here only for historical reasons...
3465
	// it was used to decide if we have to define xmlrpc_encode on our own, but
3466
	// we do not do it anymore
3467
	if(function_exists('xmlrpc_decode'))
3468
	{
3469
		define('XMLRPC_EPI_ENABLED','1');
3470
	}
3471
	else
3472
	{
3473
		define('XMLRPC_EPI_ENABLED','0');
3474
	}
3475
3476
	/**
3477
	* Takes native php types and encodes them into xmlrpc PHP object format.
3478
	* It will not re-encode xmlrpcval objects.
3479
	*
3480
	* Feature creep -- could support more types via optional type argument
3481
	* (string => datetime support has been added, ??? => base64 not yet)
3482
	*
3483
	* If given a proper options parameter, php object instances will be encoded
3484
	* into 'special' xmlrpc values, that can later be decoded into php objects
3485
	* by calling php_xmlrpc_decode() with a corresponding option
3486
	*
3487
	* @author Dan Libby ([email protected])
3488
	*
3489
	* @param mixed $php_val the value to be converted into an xmlrpcval object
3490
	* @param array $options	can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3491
	* @return xmlrpcval
3492
	*/
3493
	function php_xmlrpc_encode($php_val, $options=array())
3494
	{
3495
		$type = gettype($php_val);
3496
		switch($type)
3497
		{
3498
			case 'string':
3499
				if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3500
					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3501
				else
3502
					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3503
				break;
3504
			case 'integer':
3505
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3506
				break;
3507
			case 'double':
3508
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3509
				break;
3510
				// <G_Giunta_2001-02-29>
3511
				// Add support for encoding/decoding of booleans, since they are supported in PHP
3512
			case 'boolean':
3513
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3514
				break;
3515
				// </G_Giunta_2001-02-29>
3516
			case 'array':
3517
				// PHP arrays can be encoded to either xmlrpc structs or arrays,
3518
				// depending on wheter they are hashes or plain 0..n integer indexed
3519
				// A shorter one-liner would be
3520
				// $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3521
				// but execution time skyrockets!
3522
				$j = 0;
3523
				$arr = array();
3524
				$ko = false;
3525
				foreach($php_val as $key => $val)
3526
				{
3527
					$arr[$key] = php_xmlrpc_encode($val, $options);
3528
					if(!$ko && $key !== $j)
3529
					{
3530
						$ko = true;
3531
					}
3532
					$j++;
3533
				}
3534
				if($ko)
3535
				{
3536
					$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...
3537
				}
3538
				else
3539
				{
3540
					$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...
3541
				}
3542
				break;
3543
			case 'object':
3544
				if(is_a($php_val, 'xmlrpcval'))
3545
				{
3546
					$xmlrpc_val = $php_val;
3547
				}
3548
				else if(is_a($php_val, 'DateTime'))
3549
				{
3550
					$xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3551
				}
3552
				else
3553
				{
3554
					$arr = array();
3555
					reset($php_val);
3556
					while(list($k,$v) = each($php_val))
3557
					{
3558
						$arr[$k] = php_xmlrpc_encode($v, $options);
3559
					}
3560
					$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...
3561
					if (in_array('encode_php_objs', $options))
3562
					{
3563
						// let's save original class name into xmlrpcval:
3564
						// might be useful later on...
3565
						$xmlrpc_val->_php_class = get_class($php_val);
3566
					}
3567
				}
3568
				break;
3569
			case 'NULL':
3570
				if (in_array('extension_api', $options))
3571
				{
3572
					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3573
				}
3574
				else if (in_array('null_extension', $options))
3575
				{
3576
					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3577
				}
3578
				else
3579
				{
3580
					$xmlrpc_val = new xmlrpcval();
3581
				}
3582
				break;
3583
			case 'resource':
3584
				if (in_array('extension_api', $options))
3585
				{
3586
					$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...
3587
				}
3588
				else
3589
				{
3590
					$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...
3591
				}
3592
			// catch "user function", "unknown type"
3593
			default:
3594
				// giancarlo pinerolo <[email protected]>
3595
				// it has to return
3596
				// an empty object in case, not a boolean.
3597
				$xmlrpc_val = new xmlrpcval();
3598
				break;
3599
			}
3600
			return $xmlrpc_val;
3601
	}
3602
3603
	/**
3604
	* Convert the xml representation of a method response, method request or single
3605
	* xmlrpc value into the appropriate object (a.k.a. deserialize)
3606
	* @param string $xml_val
3607
	* @param array $options
3608
	* @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3609
	*/
3610
	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...
3611
	{
3612
		$GLOBALS['_xh'] = array();
3613
		$GLOBALS['_xh']['ac'] = '';
3614
		$GLOBALS['_xh']['stack'] = array();
3615
		$GLOBALS['_xh']['valuestack'] = array();
3616
		$GLOBALS['_xh']['params'] = array();
3617
		$GLOBALS['_xh']['pt'] = array();
3618
		$GLOBALS['_xh']['isf'] = 0;
3619
		$GLOBALS['_xh']['isf_reason'] = '';
3620
		$GLOBALS['_xh']['method'] = false;
3621
		$GLOBALS['_xh']['rt'] = '';
3622
3623
		// 'guestimate' encoding
3624
		$val_encoding = guess_encoding('', $xml_val);
3625
3626
		// Since parsing will fail if charset is not specified in the xml prologue,
3627
		// the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that...
3628
		// The following code might be better for mb_string enabled installs, but
3629
		// makes the lib about 200% slower...
3630
		//if (!is_valid_charset($val_encoding, array('UTF-8')))
3631 View Code Duplication
		if (!in_array($val_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($xml_val)) {
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...
3632
			if ($val_encoding == 'ISO-8859-1') {
3633
				$xml_val = utf8_encode($xml_val);
3634
			} else {
3635
				if (extension_loaded('mbstring')) {
3636
					$xml_val = mb_convert_encoding($xml_val, 'UTF-8', $val_encoding);
3637
				} else {
3638
					error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $val_encoding);
3639
				}
3640
			}
3641
		}
3642
3643
		$parser = xml_parser_create();
3644
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3645
		// What if internal encoding is not in one of the 3 allowed?
3646
		// we use the broadest one, ie. utf8!
3647 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...
3648
		{
3649
			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3650
		}
3651
		else
3652
		{
3653
			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3654
		}
3655
		xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3656
		xml_set_character_data_handler($parser, 'xmlrpc_cd');
3657
		xml_set_default_handler($parser, 'xmlrpc_dh');
3658
		if(!xml_parse($parser, $xml_val, 1))
3659
		{
3660
			$errstr = sprintf('XML error: %s at line %d, column %d',
3661
						xml_error_string(xml_get_error_code($parser)),
3662
						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3663
			error_log($errstr);
3664
			xml_parser_free($parser);
3665
			return false;
3666
		}
3667
		xml_parser_free($parser);
3668
		if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3669
		{
3670
			error_log($GLOBALS['_xh']['isf_reason']);
3671
			return false;
3672
		}
3673
		switch ($GLOBALS['_xh']['rt'])
3674
		{
3675
			case 'methodresponse':
3676
				$v =& $GLOBALS['_xh']['value'];
3677
				if ($GLOBALS['_xh']['isf'] == 1)
3678
				{
3679
					$vc = $v->structmem('faultCode');
3680
					$vs = $v->structmem('faultString');
3681
					$r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3682
				}
3683
				else
3684
				{
3685
					$r = new xmlrpcresp($v);
3686
				}
3687
				return $r;
3688
			case 'methodcall':
3689
				$m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3690 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...
3691
				{
3692
					$m->addParam($GLOBALS['_xh']['params'][$i]);
3693
				}
3694
				return $m;
3695
			case 'value':
3696
				return $GLOBALS['_xh']['value'];
3697
			default:
3698
				return false;
3699
		}
3700
	}
3701
3702
	/**
3703
	* decode a string that is encoded w/ "chunked" transfer encoding
3704
	* as defined in rfc2068 par. 19.4.6
3705
	* code shamelessly stolen from nusoap library by Dietrich Ayala
3706
	*
3707
	* @param string $buffer the string to be decoded
3708
	* @return string
3709
	*/
3710
	function decode_chunked($buffer)
3711
	{
3712
		// length := 0
3713
		$length = 0;
3714
		$new = '';
3715
3716
		// read chunk-size, chunk-extension (if any) and crlf
3717
		// get the position of the linebreak
3718
		$chunkend = strpos($buffer,"\r\n") + 2;
3719
		$temp = substr($buffer,0,$chunkend);
3720
		$chunk_size = hexdec( trim($temp) );
3721
		$chunkstart = $chunkend;
3722
		while($chunk_size > 0)
3723
		{
3724
			$chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3725
3726
			// just in case we got a broken connection
3727
			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...
3728
			{
3729
				$chunk = substr($buffer,$chunkstart);
3730
				// append chunk-data to entity-body
3731
				$new .= $chunk;
3732
				$length += strlen($chunk);
3733
				break;
3734
			}
3735
3736
			// read chunk-data and crlf
3737
			$chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3738
			// append chunk-data to entity-body
3739
			$new .= $chunk;
3740
			// length := length + chunk-size
3741
			$length += strlen($chunk);
3742
			// read chunk-size and crlf
3743
			$chunkstart = $chunkend + 2;
3744
3745
			$chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3746
			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...
3747
			{
3748
				break; //just in case we got a broken connection
3749
			}
3750
			$temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3751
			$chunk_size = hexdec( trim($temp) );
3752
			$chunkstart = $chunkend;
3753
		}
3754
		return $new;
3755
	}
3756
3757
	/**
3758
	* xml charset encoding guessing helper function.
3759
	* Tries to determine the charset encoding of an XML chunk received over HTTP.
3760
	* NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3761
	* we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3762
	* which will be most probably using UTF-8 anyway...
3763
	*
3764
	* @param string $httpheader the http Content-type header
3765
	* @param string $xmlchunk xml content buffer
3766
	* @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3767
	* @return string
3768
	*
3769
	* @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3770
	*/
3771
	function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3772
	{
3773
		// discussion: see http://www.yale.edu/pclt/encoding/
3774
		// 1 - test if encoding is specified in HTTP HEADERS
3775
3776
		//Details:
3777
		// LWS:           (\13\10)?( |\t)+
3778
		// token:         (any char but excluded stuff)+
3779
		// quoted string: " (any char but double quotes and cointrol chars)* "
3780
		// header:        Content-type = ...; charset=value(; ...)*
3781
		//   where value is of type token, no LWS allowed between 'charset' and value
3782
		// Note: we do not check for invalid chars in VALUE:
3783
		//   this had better be done using pure ereg as below
3784
		// Note 2: we might be removing whitespace/tabs that ought to be left in if
3785
		//   the received charset is a quoted string. But nobody uses such charset names...
3786
3787
		/// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3788
		$matches = array();
3789
		if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3790
		{
3791
			return strtoupper(trim($matches[1], " \t\""));
3792
		}
3793
3794
		// 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3795
		//     (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3796
		//     NOTE: actually, according to the spec, even if we find the BOM and determine
3797
		//     an encoding, we should check if there is an encoding specified
3798
		//     in the xml declaration, and verify if they match.
3799
		/// @todo implement check as described above?
3800
		/// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3801
		if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3802
		{
3803
			return 'UCS-4';
3804
		}
3805
		elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3806
		{
3807
			return 'UTF-16';
3808
		}
3809
		elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3810
		{
3811
			return 'UTF-8';
3812
		}
3813
3814
		// 3 - test if encoding is specified in the xml declaration
3815
		// Details:
3816
		// SPACE:         (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3817
		// EQ:            SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3818
		if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3819
			'\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3820
			$xmlchunk, $matches))
3821
		{
3822
			return strtoupper(substr($matches[2], 1, -1));
3823
		}
3824
3825
		// 4 - if mbstring is available, let it do the guesswork
3826
		// NB: we favour finding an encoding that is compatible with what we can process
3827
		if(extension_loaded('mbstring'))
3828
		{
3829
			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...
3830
			{
3831
				$enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3832
			}
3833
			else
3834
			{
3835
				$enc = mb_detect_encoding($xmlchunk);
3836
			}
3837
			// NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3838
			// IANA also likes better US-ASCII, so go with it
3839
			if($enc == 'ASCII')
3840
			{
3841
				$enc = 'US-'.$enc;
3842
			}
3843
			return $enc;
3844
		}
3845
		else
3846
		{
3847
			// no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3848
			// Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3849
			// this should be the standard. And we should be getting text/xml as request and response.
3850
			// BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3851
			return $GLOBALS['xmlrpc_defencoding'];
3852
		}
3853
	}
3854
3855
	/**
3856
	 * Helper function: checks if an xml chunk as a charset declaration (BOM or in the xml declaration)
3857
	 *
3858
	 * @param string $xmlChunk
3859
	 * @return bool
3860
	 */
3861
	function has_encoding($xmlChunk)
3862
	{
3863
		// scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3864
		//	 (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3865
		if (preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlChunk))
3866
		{
3867
			return true;
3868
		}
3869
		elseif (preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlChunk))
3870
		{
3871
			return true;
3872
		}
3873
		elseif (preg_match('/^(\xEF\xBB\xBF)/', $xmlChunk))
3874
		{
3875
			return true;
3876
		}
3877
3878
		// test if encoding is specified in the xml declaration
3879
		// Details:
3880
		// SPACE:		(#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3881
		// EQ:			SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3882
		if (preg_match('/^<\?xml\s+version\s*=\s*' . "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))" .
3883
			'\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3884
			$xmlChunk, $matches))
3885
		{
3886
			return true;
3887
		}
3888
3889
		return false;
3890
	}
3891
3892
	/**
3893
	* Checks if a given charset encoding is present in a list of encodings or
3894
	* if it is a valid subset of any encoding in the list
3895
	* @param string $encoding charset to be tested
3896
	* @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3897
	* @return bool
3898
	*/
3899
	function is_valid_charset($encoding, $validlist)
3900
	{
3901
		$charset_supersets = array(
3902
			'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3903
				'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3904
				'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3905
				'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3906
				'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3907
		);
3908
		if (is_string($validlist))
3909
			$validlist = explode(',', $validlist);
3910
		if (@in_array(strtoupper($encoding), $validlist))
3911
			return true;
3912
		else
3913
		{
3914
			if (array_key_exists($encoding, $charset_supersets))
3915
				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...
3916
					if (in_array($allowed, $charset_supersets[$encoding]))
3917
						return true;
3918
			return false;
3919
		}
3920
	}
3921
3922
?>
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...