Components/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc::wrap_xmlrpc_method()   F
last analyzed

Complexity

Conditions 25
Paths > 20000

Size

Total Lines 130
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
dl 0
loc 130
rs 2
c 0
b 0
f 0
eloc 71
nc 215424
nop 6

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * PHP-XMLRPC "wrapper" functions
4
 * Generate stubs to transparently access xmlrpc methods as php functions and viceversa
5
 *
6
 * @version $Id: xmlrpc_wrappers.inc,v 1.13 2008/09/20 01:23:47 ggiunta Exp $
7
 * @author Gaetano Giunta
8
 * @copyright (C) 2006-2009 G. Giunta
9
 * @license code licensed under the BSD License: http://phpxmlrpc.sourceforge.net/license.txt
10
 *
11
 * @todo separate introspection from code generation for func-2-method wrapping
12
 * @todo use some better templating system for code generation?
13
 * @todo implement method wrapping with preservation of php objs in calls
14
 * @todo when wrapping methods without obj rebuilding, use return_type = 'phpvals' (faster)
15
 * @todo implement self-parsing of php code for PHP <= 4
16
 */
17
18
	// requires: xmlrpc.inc
19
20
	/**
21
	* Given a string defining a php type or phpxmlrpc type (loosely defined: strings
22
	* accepted come from javadoc blocks), return corresponding phpxmlrpc type.
23
	* NB: for php 'resource' types returns empty string, since resources cannot be serialized;
24
	* for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
25
	* for php arrays always return array, even though arrays sometiles serialize as json structs
26
	* @param string $phptype
27
	* @return string
28
	*/
29
	function php_2_xmlrpc_type($phptype)
30
	{
31
		switch(strtolower($phptype))
32
		{
33
			case 'string':
34
				return $GLOBALS['xmlrpcString'];
35
			case 'integer':
36
			case $GLOBALS['xmlrpcInt']: // 'int'
37
			case $GLOBALS['xmlrpcI4']:
38
				return $GLOBALS['xmlrpcInt'];
39
			case 'double':
40
				return $GLOBALS['xmlrpcDouble'];
41
			case 'boolean':
42
				return $GLOBALS['xmlrpcBoolean'];
43
			case 'array':
44
				return $GLOBALS['xmlrpcArray'];
45
			case 'object':
46
				return $GLOBALS['xmlrpcStruct'];
47
			case $GLOBALS['xmlrpcBase64']:
48
			case $GLOBALS['xmlrpcStruct']:
49
				return strtolower($phptype);
50
			case 'resource':
51
				return '';
52
			default:
53
				if(class_exists($phptype))
54
				{
55
					return $GLOBALS['xmlrpcStruct'];
56
				}
57
				else
58
				{
59
					// unknown: might be any 'extended' xmlrpc type
60
					return $GLOBALS['xmlrpcValue'];
61
				}
62
		}
63
	}
64
65
	/**
66
	* Given a string defining a phpxmlrpc type return corresponding php type.
67
	* @param string $xmlrpctype
68
	* @return string
69
	*/
70
	function xmlrpc_2_php_type($xmlrpctype)
71
	{
72
		switch(strtolower($xmlrpctype))
73
		{
74
			case 'base64':
75
			case 'datetime.iso8601':
76
			case 'string':
77
				return $GLOBALS['xmlrpcString'];
78
			case 'int':
79
			case 'i4':
80
				return 'integer';
81
			case 'struct':
82
			case 'array':
83
				return 'array';
84
			case 'double':
85
				return 'float';
86
			case 'undefined':
87
				return 'mixed';
88
			case 'boolean':
89
			case 'null':
90
			default:
91
				// unknown: might be any xmlrpc type
92
				return strtolower($xmlrpctype);
93
		}
94
	}
95
96
	/**
97
	* Given a user-defined PHP function, create a PHP 'wrapper' function that can
98
	* be exposed as xmlrpc method from an xmlrpc_server object and called from remote
99
	* clients (as well as its corresponding signature info).
100
	*
101
	* Since php is a typeless language, to infer types of input and output parameters,
102
	* it relies on parsing the javadoc-style comment block associated with the given
103
	* function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
104
	* in the @param tag is also allowed, if you need the php function to receive/send
105
	* data in that particular format (note that base64 encoding/decoding is transparently
106
	* carried out by the lib, while datetime vals are passed around as strings)
107
	*
108
	* Known limitations:
109
	* - requires PHP 5.0.3 +
110
	* - only works for user-defined functions, not for PHP internal functions
111
	*   (reflection does not support retrieving number/type of params for those)
112
	* - functions returning php objects will generate special xmlrpc responses:
113
	*   when the xmlrpc decoding of those responses is carried out by this same lib, using
114
	*   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
115
	*   In short: php objects can be serialized, too (except for their resource members),
116
	*   using this function.
117
	*   Other libs might choke on the very same xml that will be generated in this case
118
	*   (i.e. it has a nonstandard attribute on struct element tags)
119
	* - usage of javadoc @param tags using param names in a different order from the
120
	*   function prototype is not considered valid (to be fixed?)
121
	*
122
	* Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
123
	* php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
124
	* is by making use of the functions_parameters_type class member.
125
	*
126
	* @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') and array('class', 'methodname') are ok too
127
	* @param string $newfuncname (optional) name for function to be created
128
	* @param array $extra_options (optional) array of options for conversion. valid values include:
129
	*        bool  return_source when true, php code w. function definition will be returned, not evaluated
130
	*        bool  encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
131
	*        bool  decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
132
	*        bool  suppress_warnings  remove from produced xml any runtime warnings due to the php function being invoked
133
	* @return false on error, or an array containing the name of the new php function,
0 ignored issues
show
Documentation introduced by
Should the return type not be false|array?

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

Loading history...
134
	*         its signature and docs, to be used in the server dispatch map
135
	*
136
	* @todo decide how to deal with params passed by ref: bomb out or allow?
137
	* @todo finish using javadoc info to build method sig if all params are named but out of order
138
	* @todo add a check for params of 'resource' type
139
	* @todo add some trigger_errors / error_log when returning false?
140
	* @todo what to do when the PHP function returns NULL? we are currently returning an empty string value...
141
	* @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
142
	* @todo if $newfuncname is empty, we could use create_user_func instead of eval, as it is possibly faster
143
	* @todo add a verbatim_object_copy parameter to allow avoiding the same obj instance?
144
	*/
145
	function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
146
	{
147
		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
148
		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
149
		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
150
		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
151
		$catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
152
153
		if(version_compare(phpversion(), '5.0.3') == -1)
154
		{
155
			// up to php 5.0.3 some useful reflection methods were missing
156
			error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
157
			return false;
158
		}
159
160
        $exists = false;
0 ignored issues
show
Unused Code introduced by
$exists 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...
161
	    if (is_string($funcname) && strpos($funcname, '::') !== false)
162
	    {
163
	        $funcname = explode('::', $funcname);
164
	    }
165
        if(is_array($funcname))
166
        {
167
            if(count($funcname) < 2 || (!is_string($funcname[0]) && !is_object($funcname[0])))
168
            {
169
    			error_log('XML-RPC: syntax for function to be wrapped is wrong');
170
    			return false;
171
            }
172
            if(is_string($funcname[0]))
173
            {
174
                $plainfuncname = implode('::', $funcname);
175
            }
176
            elseif(is_object($funcname[0]))
177
            {
178
                $plainfuncname = get_class($funcname[0]) . '->' . $funcname[1];
179
            }
180
            $exists = method_exists($funcname[0], $funcname[1]);
181
            if (!$exists && version_compare(phpversion(), '5.1') < 0)
182
            {
183
               // workaround for php 5.0: static class methods are not seen by method_exists
184
               $exists = is_callable( $funcname );
185
            }
186
        }
187
        else
188
        {
189
            $plainfuncname = $funcname;
190
            $exists = function_exists($funcname);
191
        }
192
193
		if(!$exists)
194
		{
195
			error_log('XML-RPC: function to be wrapped is not defined: '.$plainfuncname);
0 ignored issues
show
Bug introduced by
The variable $plainfuncname 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...
196
			return false;
197
		}
198
		else
199
		{
200
			// determine name of new php function
201
			if($newfuncname == '')
202
			{
203
				if(is_array($funcname))
204
				{
205
    				if(is_string($funcname[0]))
206
        				$xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
207
    				else
208
    					$xmlrpcfuncname = "{$prefix}_".get_class($funcname[0]) . '_' . $funcname[1];
209
				}
210
				else
211
				{
212
					$xmlrpcfuncname = "{$prefix}_$funcname";
213
				}
214
			}
215
			else
216
			{
217
				$xmlrpcfuncname = $newfuncname;
218
			}
219
			while($buildit && function_exists($xmlrpcfuncname))
220
			{
221
				$xmlrpcfuncname .= 'x';
222
			}
223
224
			// start to introspect PHP code
225
			if(is_array($funcname))
226
			{
227
    			$func = new ReflectionMethod($funcname[0], $funcname[1]);
228
    			if($func->isPrivate())
229
    			{
230
    				error_log('XML-RPC: method to be wrapped is private: '.$plainfuncname);
231
    				return false;
232
    			}
233
    			if($func->isProtected())
234
    			{
235
    				error_log('XML-RPC: method to be wrapped is protected: '.$plainfuncname);
236
    				return false;
237
    			}
238
     			if($func->isConstructor())
239
    			{
240
    				error_log('XML-RPC: method to be wrapped is the constructor: '.$plainfuncname);
241
    				return false;
242
    			}
243
			    // php 503 always says isdestructor = true...
244
                if( version_compare(phpversion(), '5.0.3') != 0 && $func->isDestructor())
245
    			{
246
    				error_log('XML-RPC: method to be wrapped is the destructor: '.$plainfuncname);
247
    				return false;
248
    			}
249
    			if($func->isAbstract())
250
    			{
251
    				error_log('XML-RPC: method to be wrapped is abstract: '.$plainfuncname);
252
    				return false;
253
    			}
254
                /// @todo add more checks for static vs. nonstatic?
255
            }
256
			else
257
			{
258
    			$func = new ReflectionFunction($funcname);
259
            }
260
			if($func->isInternal())
261
			{
262
				// Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
263
				// instead of getparameters to fully reflect internal php functions ?
264
				error_log('XML-RPC: function to be wrapped is internal: '.$plainfuncname);
265
				return false;
266
			}
267
268
			// retrieve parameter names, types and description from javadoc comments
269
270
			// function description
271
			$desc = '';
272
			// type of return val: by default 'any'
273
			$returns = $GLOBALS['xmlrpcValue'];
274
			// desc of return val
275
			$returnsDocs = '';
276
			// type + name of function parameters
277
			$paramDocs = array();
278
279
			$docs = $func->getDocComment();
280
			if($docs != '')
281
			{
282
				$docs = explode("\n", $docs);
283
				$i = 0;
284
				foreach($docs as $doc)
285
				{
286
					$doc = trim($doc, " \r\t/*");
287
					if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
288
					{
289
						if($desc)
290
						{
291
							$desc .= "\n";
292
						}
293
						$desc .= $doc;
294
					}
295
					elseif(strpos($doc, '@param') === 0)
296
					{
297
						// syntax: @param type [$name] desc
298
						if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
299
						{
300
							if(strpos($matches[1], '|'))
301
							{
302
								//$paramDocs[$i]['type'] = explode('|', $matches[1]);
303
								$paramDocs[$i]['type'] = 'mixed';
304
							}
305
							else
306
							{
307
								$paramDocs[$i]['type'] = $matches[1];
308
							}
309
							$paramDocs[$i]['name'] = trim($matches[2]);
310
							$paramDocs[$i]['doc'] = $matches[3];
311
						}
312
						$i++;
313
					}
314
					elseif(strpos($doc, '@return') === 0)
315
					{
316
						// syntax: @return type desc
317
						//$returns = preg_split('/\s+/', $doc);
318
						if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
319
						{
320
							$returns = php_2_xmlrpc_type($matches[1]);
321
							if(isset($matches[2]))
322
							{
323
								$returnsDocs = $matches[2];
324
							}
325
						}
326
					}
327
				}
328
			}
329
330
			// execute introspection of actual function prototype
331
			$params = array();
332
			$i = 0;
333
			foreach($func->getParameters() as $paramobj)
334
			{
335
				$params[$i] = array();
336
				$params[$i]['name'] = '$'.$paramobj->getName();
337
				$params[$i]['isoptional'] = $paramobj->isOptional();
338
				$i++;
339
			}
340
341
342
			// start  building of PHP code to be eval'd
343
			$innercode = '';
344
			$i = 0;
345
			$parsvariations = array();
346
			$pars = array();
347
			$pnum = count($params);
348
			foreach($params as $param)
349
			{
350
				if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
351
				{
352
					// param name from phpdoc info does not match param definition!
353
					$paramDocs[$i]['type'] = 'mixed';
354
				}
355
356
				if($param['isoptional'])
357
				{
358
					// this particular parameter is optional. save as valid previous list of parameters
359
					$innercode .= "if (\$paramcount > $i) {\n";
360
					$parsvariations[] = $pars;
361
				}
362
				$innercode .= "\$p$i = \$msg->getParam($i);\n";
363
				if ($decode_php_objects)
364
				{
365
					$innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
366
				}
367
				else
368
				{
369
					$innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
370
				}
371
372
				$pars[] = "\$p$i";
373
				$i++;
374
				if($param['isoptional'])
375
				{
376
					$innercode .= "}\n";
377
				}
378
				if($i == $pnum)
379
				{
380
					// last allowed parameters combination
381
					$parsvariations[] = $pars;
382
				}
383
			}
384
385
			$sigs = array();
386
			$psigs = array();
387
			if(count($parsvariations) == 0)
388
			{
389
				// only known good synopsis = no parameters
390
				$parsvariations[] = array();
391
				$minpars = 0;
392
			}
393
			else
394
			{
395
				$minpars = count($parsvariations[0]);
396
			}
397
398
			if($minpars)
399
			{
400
				// add to code the check for min params number
401
				// NB: this check needs to be done BEFORE decoding param values
402
				$innercode = "\$paramcount = \$msg->getNumParams();\n" .
403
				"if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
404
			}
405
			else
406
			{
407
				$innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
408
			}
409
410
			$innercode .= "\$np = false;\n";
411
			// since there are no closures in php, if we are given an object instance,
412
            // we store a pointer to it in a global var...
413
			if ( is_array($funcname) && is_object($funcname[0]) )
414
			{
415
			    $GLOBALS['xmlrpcWPFObjHolder'][$xmlrpcfuncname] =& $funcname[0];
416
			    $innercode .= "\$obj =& \$GLOBALS['xmlrpcWPFObjHolder']['$xmlrpcfuncname'];\n";
417
			    $realfuncname = '$obj->'.$funcname[1];
418
			}
419
			else
420
			{
421
    			$realfuncname = $plainfuncname;
422
            }
423
			foreach($parsvariations as $pars)
424
			{
425
				$innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$realfuncname(" . implode(',', $pars) . "); else\n";
426
				// build a 'generic' signature (only use an appropriate return type)
427
				$sig = array($returns);
428
				$psig = array($returnsDocs);
429
				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...
430
				{
431
					if (isset($paramDocs[$i]['type']))
432
					{
433
						$sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
434
					}
435
					else
436
					{
437
						$sig[] = $GLOBALS['xmlrpcValue'];
438
					}
439
					$psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
440
				}
441
				$sigs[] = $sig;
442
				$psigs[] = $psig;
443
			}
444
			$innercode .= "\$np = true;\n";
445
			$innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
446
			//$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
447
			$innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
448
			if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
449
			{
450
				$innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
451
			}
452
			else
453
			{
454
				if ($encode_php_objects)
455
					$innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
456
				else
457
					$innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
458
			}
459
			// shall we exclude functions returning by ref?
460
			// if($func->returnsReference())
461
			// 	return false;
462
			$code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
463
			//print_r($code);
464
			if ($buildit)
465
			{
466
				$allOK = 0;
467
				eval($code.'$allOK=1;');
468
				// alternative
469
				//$xmlrpcfuncname = create_function('$m', $innercode);
470
471
				if(!$allOK)
472
				{
473
					error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$plainfuncname);
474
					return false;
475
				}
476
			}
477
478
			/// @todo examine if $paramDocs matches $parsvariations and build array for
479
			/// usage as method signature, plus put together a nice string for docs
480
481
			$ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
482
			return $ret;
483
		}
484
	}
485
486
    /**
487
    * Given a user-defined PHP class or php object, map its methods onto a list of
488
	* PHP 'wrapper' functions that can be exposed as xmlrpc methods from an xmlrpc_server
489
	* object and called from remote clients (as well as their corresponding signature info).
490
	*
491
    * @param mixed $classname the name of the class whose methods are to be exposed as xmlrpc methods, or an object instance of that class
492
    * @param array $extra_options see the docs for wrap_php_method for more options
493
    *        string method_type 'static', 'nonstatic', 'all' and 'auto' (default); the latter will switch between static and non-static depending on wheter $classname is a class name or object instance
494
    * @return array or false on failure
0 ignored issues
show
Documentation introduced by
Should the return type not be false|array?

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

Loading history...
495
    *
496
    * @todo get_class_methods will return both static and non-static methods.
497
    *       we have to differentiate the action, depending on wheter we recived a class name or object
498
    */
499
    function wrap_php_class($classname, $extra_options=array())
500
    {
501
		$methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
502
		$methodtype = isset($extra_options['method_type']) ? $extra_options['method_type'] : 'auto';
503
504
        if(version_compare(phpversion(), '5.0.3') == -1)
505
		{
506
			// up to php 5.0.3 some useful reflection methods were missing
507
			error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
508
			return false;
509
		}
510
511
        $result = array();
512
		$mlist = get_class_methods($classname);
513
		foreach($mlist as $mname)
514
		{
515
    		if ($methodfilter == '' || preg_match($methodfilter, $mname))
516
			{
517
    			// echo $mlist."\n";
518
    			$func = new ReflectionMethod($classname, $mname);
519
    			if(!$func->isPrivate() && !$func->isProtected() && !$func->isConstructor() && !$func->isDestructor() && !$func->isAbstract())
520
    			{
521
        			if(($func->isStatic && ($methodtype == 'all' || $methodtype == 'static' || ($methodtype == 'auto' && is_string($classname)))) ||
0 ignored issues
show
Bug introduced by
The property isStatic does not seem to exist in ReflectionMethod.

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...
522
            			(!$func->isStatic && ($methodtype == 'all' || $methodtype == 'nonstatic' || ($methodtype == 'auto' && is_object($classname)))))
523
            		{
524
                        $methodwrap = wrap_php_function(array($classname, $mname), '', $extra_options);
0 ignored issues
show
Documentation introduced by
array($classname, $mname) is of type array<integer,*,{"0":"*","1":"?"}>, but the function expects a string.

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...
525
                        if ( $methodwrap )
526
                        {
527
                            $result[$methodwrap['function']] = $methodwrap['function'];
528
                        }
529
                    }
530
    			}
531
			}
532
		}
533
        return $result;
534
    }
535
536
	/**
537
	* Given an xmlrpc client and a method name, register a php wrapper function
538
	* that will call it and return results using native php types for both
539
	* params and results. The generated php function will return an xmlrpcresp
540
	* oject for failed xmlrpc calls
541
	*
542
	* Known limitations:
543
	* - server must support system.methodsignature for the wanted xmlrpc method
544
	* - for methods that expose many signatures, only one can be picked (we
545
	*   could in priciple check if signatures differ only by number of params
546
	*   and not by type, but it would be more complication than we can spare time)
547
	* - nested xmlrpc params: the caller of the generated php function has to
548
	*   encode on its own the params passed to the php function if these are structs
549
	*   or arrays whose (sub)members include values of type datetime or base64
550
	*
551
	* Notes: the connection properties of the given client will be copied
552
	* and reused for the connection used during the call to the generated
553
	* php function.
554
	* Calling the generated php function 'might' be slow: a new xmlrpc client
555
	* is created on every invocation and an xmlrpc-connection opened+closed.
556
	* An extra 'debug' param is appended to param list of xmlrpc method, useful
557
	* for debugging purposes.
558
	*
559
	* @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
560
	* @param string        $methodname the xmlrpc method to be mapped to a php function
561
	* @param array         $extra_options array of options that specify conversion details. valid ptions include
0 ignored issues
show
Documentation introduced by
Should the type for parameter $extra_options not be integer?

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

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

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

Loading history...
562
	*        integer       signum      the index of the method signature to use in mapping (if method exposes many sigs)
563
	*        integer       timeout     timeout (in secs) to be used when executing function/calling remote method
564
	*        string        protocol    'http' (default), 'http11' or 'https'
565
	*        string        new_function_name the name of php function to create. If unsepcified, lib will pick an appropriate name
566
	*        string        return_source if true return php code w. function definition instead fo function name
567
	*        bool          encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
568
	*        bool          decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
569
	*        mixed         return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
570
	*        bool          debug        set it to 1 or 2 to see debug results of querying server for method synopsis
571
	* @return string                   the name of the generated php function (or false) - OR AN ARRAY...
572
	*/
573
	function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
574
	{
575
		// mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
576
		// OR the 2.0 calling convention (no options) - we really love backward compat, don't we?
577
		if (!is_array($extra_options))
578
		{
579
			$signum = $extra_options;
580
			$extra_options = array();
581
		}
582
		else
583
		{
584
			$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
585
			$timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
586
			$protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
587
			$newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
588
		}
589
		//$encode_php_objects = in_array('encode_php_objects', $extra_options);
590
		//$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
591
		//	in_array('build_class_code', $extra_options) ? 2 : 0;
592
593
		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
594
		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
595
		$simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
596
		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
597
		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
598
		if (isset($extra_options['return_on_fault']))
599
		{
600
			$decode_fault = true;
601
			$fault_response = $extra_options['return_on_fault'];
602
		}
603
		else
604
		{
605
			$decode_fault = false;
606
			$fault_response = '';
607
		}
608
		$debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
609
610
		$msgclass = $prefix.'msg';
611
		$valclass = $prefix.'val';
612
		$decodefunc = 'php_'.$prefix.'_decode';
613
614
		$msg = new $msgclass('system.methodSignature');
615
		$msg->addparam(new $valclass($methodname));
616
		$client->setDebug($debug);
617
		$response =& $client->send($msg, $timeout, $protocol);
618
		if($response->faultCode())
619
		{
620
			error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
621
			return false;
622
		}
623
		else
624
		{
625
			$msig = $response->value();
626
			if ($client->return_type != 'phpvals')
627
			{
628
				$msig = $decodefunc($msig);
629
			}
630
			if(!is_array($msig) || count($msig) <= $signum)
631
			{
632
				error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
633
				return false;
634
			}
635
			else
636
			{
637
				// pick a suitable name for the new function, avoiding collisions
638
				if($newfuncname != '')
639
				{
640
					$xmlrpcfuncname = $newfuncname;
641
				}
642
				else
643
				{
644
					// take care to insure that methodname is translated to valid
645
					// php function name
646
					$xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
647
						array('_', ''), $methodname);
648
				}
649
				while($buildit && function_exists($xmlrpcfuncname))
650
				{
651
					$xmlrpcfuncname .= 'x';
652
				}
653
654
				$msig = $msig[$signum];
655
				$mdesc = '';
656
				// if in 'offline' mode, get method description too.
657
				// in online mode, favour speed of operation
658
				if(!$buildit)
659
				{
660
					$msg = new $msgclass('system.methodHelp');
661
					$msg->addparam(new $valclass($methodname));
662
					$response =& $client->send($msg, $timeout, $protocol);
663
					if (!$response->faultCode())
664
					{
665
						$mdesc = $response->value();
666
						if ($client->return_type != 'phpvals')
667
						{
668
							$mdesc = $mdesc->scalarval();
0 ignored issues
show
Bug introduced by
The method scalarval cannot be called on $mdesc (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...
669
						}
670
					}
671
				}
672
673
				$results = build_remote_method_wrapper_code($client, $methodname,
674
					$xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
675
					$prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
676
					$fault_response);
677
678
				//print_r($code);
679
				if ($buildit)
680
				{
681
					$allOK = 0;
682
					eval($results['source'].'$allOK=1;');
683
					// alternative
684
					//$xmlrpcfuncname = create_function('$m', $innercode);
685
					if($allOK)
686
					{
687
						return $xmlrpcfuncname;
688
					}
689
					else
690
					{
691
						error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
692
						return false;
693
					}
694
				}
695
				else
696
				{
697
					$results['function'] = $xmlrpcfuncname;
698
					return $results;
699
				}
700
			}
701
		}
702
	}
703
704
	/**
705
	* Similar to wrap_xmlrpc_method, but will generate a php class that wraps
706
	* all xmlrpc methods exposed by the remote server as own methods.
707
	* For more details see wrap_xmlrpc_method.
708
	* @param xmlrpc_client $client the client obj all set to query the desired server
709
	* @param array $extra_options list of options for wrapped code
710
	* @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
711
	*/
712
	function wrap_xmlrpc_server($client, $extra_options=array())
713
	{
714
		$methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
715
		//$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
716
		$timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
717
		$protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
718
		$newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
719
		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
720
		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
721
		$verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
722
		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
723
		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
724
725
		$msgclass = $prefix.'msg';
726
		//$valclass = $prefix.'val';
727
		$decodefunc = 'php_'.$prefix.'_decode';
728
729
		$msg = new $msgclass('system.listMethods');
730
		$response =& $client->send($msg, $timeout, $protocol);
731
		if($response->faultCode())
732
		{
733
			error_log('XML-RPC: could not retrieve method list from remote server');
734
			return false;
735
		}
736
		else
737
		{
738
			$mlist = $response->value();
739
			if ($client->return_type != 'phpvals')
740
			{
741
				$mlist = $decodefunc($mlist);
742
			}
743
			if(!is_array($mlist) || !count($mlist))
744
			{
745
				error_log('XML-RPC: could not retrieve meaningful method list from remote server');
746
				return false;
747
			}
748
			else
749
			{
750
				// pick a suitable name for the new function, avoiding collisions
751
				if($newclassname != '')
752
				{
753
					$xmlrpcclassname = $newclassname;
754
				}
755
				else
756
				{
757
					$xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
758
						array('_', ''), $client->server).'_client';
759
				}
760
				while($buildit && class_exists($xmlrpcclassname))
761
				{
762
					$xmlrpcclassname .= 'x';
763
				}
764
765
				/// @todo add function setdebug() to new class, to enable/disable debugging
766
				$source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
767
				$source .= "function $xmlrpcclassname()\n{\n";
768
				$source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
769
				$source .= "\$this->client =& \$client;\n}\n\n";
770
				$opts = array('simple_client_copy' => 2, 'return_source' => true,
771
					'timeout' => $timeout, 'protocol' => $protocol,
772
					'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
773
					'decode_php_objs' => $decode_php_objects
774
					);
775
				/// @todo build javadoc for class definition, too
776
				foreach($mlist as $mname)
777
				{
778
					if ($methodfilter == '' || preg_match($methodfilter, $mname))
779
					{
780
						$opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
781
							array('_', ''), $mname);
782
						$methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
0 ignored issues
show
Documentation introduced by
$opts is of type array<string,?,{"new_function_name":"?"}>, 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...
783
						if ($methodwrap)
784
						{
785
							if (!$buildit)
786
							{
787
								$source .= $methodwrap['docstring'];
788
							}
789
							$source .= $methodwrap['source']."\n";
790
						}
791
						else
792
						{
793
							error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
794
						}
795
					}
796
				}
797
				$source .= "}\n";
798
				if ($buildit)
799
				{
800
					$allOK = 0;
801
					eval($source.'$allOK=1;');
802
					// alternative
803
					//$xmlrpcfuncname = create_function('$m', $innercode);
804
					if($allOK)
805
					{
806
						return $xmlrpcclassname;
807
					}
808
					else
809
					{
810
						error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
811
						return false;
812
					}
813
				}
814
				else
815
				{
816
					return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
817
				}
818
			}
819
		}
820
	}
821
822
	/**
823
	* Given the necessary info, build php code that creates a new function to
824
	* invoke a remote xmlrpc method.
825
	* Take care that no full checking of input parameters is done to ensure that
826
	* valid php code is emitted.
827
	* Note: real spaghetti code follows...
828
	* @access private
829
	*/
830
	function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
831
		$msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
832
		$decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
833
		$fault_response='')
834
	{
835
		$code = "function $xmlrpcfuncname (";
836
		if ($client_copy_mode < 2)
837
		{
838
			// client copy mode 0 or 1 == partial / full client copy in emitted code
839
			$innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
840
			$innercode .= "\$client->setDebug(\$debug);\n";
841
			$this_ = '';
842
		}
843
		else
844
		{
845
			// client copy mode 2 == no client copy in emitted code
846
			$innercode = '';
847
			$this_ = 'this->';
848
		}
849
		$innercode .= "\$msg = new {$prefix}msg('$methodname');\n";
850
851
		if ($mdesc != '')
852
		{
853
			// take care that PHP comment is not terminated unwillingly by method description
854
			$mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
855
		}
856
		else
857
		{
858
			$mdesc = "/**\nFunction $xmlrpcfuncname\n";
859
		}
860
861
		// param parsing
862
		$plist = array();
863
		$pcount = count($msig);
864
		for($i = 1; $i < $pcount; $i++)
865
		{
866
			$plist[] = "\$p$i";
867
			$ptype = $msig[$i];
868
			if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
869
				$ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
870
			{
871
				// only build directly xmlrpcvals when type is known and scalar
872
				$innercode .= "\$p$i = new {$prefix}val(\$p$i, '$ptype');\n";
873
			}
874
			else
875
			{
876
				if ($encode_php_objects)
877
				{
878
					$innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
879
				}
880
				else
881
				{
882
					$innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
883
				}
884
			}
885
			$innercode .= "\$msg->addparam(\$p$i);\n";
886
			$mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
887
		}
888
		if ($client_copy_mode < 2)
889
		{
890
			$plist[] = '$debug=0';
891
			$mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
892
		}
893
		$plist = implode(', ', $plist);
894
		$mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
895
896
		$innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
897
		if ($decode_fault)
898
		{
899
			if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
900
			{
901
				$respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
902
			}
903
			else
904
			{
905
				$respcode = var_export($fault_response, true);
906
			}
907
		}
908
		else
909
		{
910
			$respcode = '$res';
911
		}
912
		if ($decode_php_objects)
913
		{
914
			$innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
915
		}
916
		else
917
		{
918
			$innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
919
		}
920
921
		$code = $code . $plist. ") {\n" . $innercode . "\n}\n";
922
923
		return array('source' => $code, 'docstring' => $mdesc);
924
	}
925
926
	/**
927
	* Given necessary info, generate php code that will rebuild a client object
928
	* Take care that no full checking of input parameters is done to ensure that
929
	* valid php code is emitted.
930
	* @access private
931
	*/
932
	function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
933
	{
934
		$code = "\$client = new {$prefix}_client('".str_replace("'", "\'", $client->path).
935
			"', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
936
937
		// copy all client fields to the client that will be generated runtime
938
		// (this provides for future expansion or subclassing of client obj)
939
		if ($verbatim_client_copy)
940
		{
941
			foreach($client as $fld => $val)
942
			{
943
				if($fld != 'debug' && $fld != 'return_type')
944
				{
945
					$val = var_export($val, true);
946
					$code .= "\$client->$fld = $val;\n";
947
				}
948
			}
949
		}
950
		// only make sure that client always returns the correct data type
951
		$code .= "\$client->return_type = '{$prefix}vals';\n";
952
		//$code .= "\$client->setDebug(\$debug);\n";
953
		return $code;
954
	}
955
?>
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...