gggeek /
phpxmlrpc
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Defines functions and signatures which can be registered as methods exposed by an XML-RPC Server |
||
| 4 | * |
||
| 5 | * To use this, use something akin to: |
||
| 6 | * $signatures = include('functions.php'); |
||
| 7 | * |
||
| 8 | * Demoes a simple possible way to implement webservices without cluttering the global scope: create xml-rpc-aware static |
||
| 9 | * methods in a class, and use them for the Server's dispatch map without the need to instantiate an object of that class. |
||
| 10 | * |
||
| 11 | * Alternative implementation strategies are possible as well: |
||
| 12 | * 1. same as above, but use non-static class methods and an object instance |
||
| 13 | * 2. define functions in the global scope to be used as xml-rpc method handlers: see interop.php |
||
| 14 | * 3. define xml-rpc method handlers as anonymous functions directly within the dispatch map: see validator1.php |
||
| 15 | * 4. use php methods or functions which are not aware of xml-rpc and let the Server do all the necessary type conversion: |
||
| 16 | * see discuss.php |
||
| 17 | * 5. use the PhpXmlRpc\Wrapper class to achieve the same as in point 4, with no need to manually write the dispatch map |
||
| 18 | * configuration (but taking instead a performance hit) |
||
| 19 | 559 | * 6. use the PhpXmlRpc\Wrapper class to generate php code in offline mode, achieving the same as in point 5 with no |
|
| 20 | * performance hit at runtime: see codegen.php |
||
| 21 | */ |
||
| 22 | |||
| 23 | use PhpXmlRpc\Encoder; |
||
| 24 | use PhpXmlRpc\PhpXmlRpc; |
||
| 25 | use PhpXmlRpc\Response; |
||
| 26 | use PhpXmlRpc\Server; |
||
| 27 | use PhpXmlRpc\Value; |
||
| 28 | |||
| 29 | class exampleMethods |
||
| 30 | { |
||
| 31 | 559 | public static $stateNames = array( |
|
| 32 | "Alabama", "Alaska", "Arizona", "Arkansas", "California", |
||
| 33 | 559 | "Colorado", "Columbia", "Connecticut", "Delaware", "Florida", |
|
| 34 | "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", |
||
| 35 | "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", |
||
| 36 | 22 | "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", |
|
| 37 | "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", |
||
| 38 | 22 | "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", |
|
| 39 | "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", |
||
| 40 | "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming", |
||
| 41 | ); |
||
| 42 | |||
| 43 | 22 | public static $findstate_sig = array(array('string', 'int')); |
|
| 44 | public static $findstate_doc = 'When passed an integer between 1 and 51 returns the name of a US state, where the integer is the index of that state name in an alphabetic order.'; |
||
| 45 | 22 | public static function findState($req) |
|
| 46 | 22 | { |
|
| 47 | $err = ''; |
||
| 48 | |||
| 49 | // get the first param |
||
| 50 | // param must be there and of the correct type: server object does the validation for us |
||
| 51 | $sno = $req->getParam(0); |
||
| 52 | |||
| 53 | 22 | // extract the value of the state number |
|
| 54 | $snv = $sno->scalarVal(); |
||
| 55 | |||
| 56 | // look it up in our array (zero-based) |
||
| 57 | 22 | if (isset(self::$stateNames[$snv - 1])) { |
|
| 58 | $stateName = self::$stateNames[$snv - 1]; |
||
| 59 | } else { |
||
| 60 | // not there, so complain |
||
| 61 | $err = "I don't have a state for the index '" . $snv . "'"; |
||
| 62 | } |
||
| 63 | |||
| 64 | if ($err != '') { |
||
| 65 | // if we generated an error, create an error return response |
||
| 66 | return new Response(0, PhpXmlRpc::$xmlrpcerruser, $err); |
||
| 67 | } else { |
||
| 68 | // otherwise, we create the right response with the state name |
||
| 69 | return new Response(new Value($stateName)); |
||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
| 70 | } |
||
| 71 | } |
||
| 72 | |||
| 73 | public static $agesorter_sig = array(array('array', 'array')); |
||
| 74 | public static $agesorter_doc = 'Send this method an array of [string, int] structs, eg: |
||
| 75 | <pre> |
||
| 76 | Dave 35 |
||
| 77 | Edd 45 |
||
| 78 | Fred 23 |
||
| 79 | Barney 37 |
||
| 80 | </pre> |
||
| 81 | And the array will be returned with the entries sorted by their numbers.'; |
||
| 82 | public static function ageSorter($req) |
||
| 83 | { |
||
| 84 | Server::xmlrpc_debugmsg("Entering 'agesorter'"); |
||
| 85 | |||
| 86 | // error string for [if|when] things go wrong |
||
| 87 | $err = ''; |
||
| 88 | 559 | ||
| 89 | 559 | // get the parameter, turn it into an easy-to-manipulate php array |
|
| 90 | $enc = new Encoder(); |
||
| 91 | $v = $enc->decode($req->getParam(0)); |
||
| 92 | |||
| 93 | $max = count($v); |
||
| 94 | Server::xmlrpc_debugmsg("Found $max array elements"); |
||
| 95 | |||
| 96 | // extract name and age from struct. The values nested inside it were not type-checked, so we do it |
||
| 97 | $agar = array(); |
||
| 98 | foreach ($v as $i => $rec) { |
||
| 99 | if (!is_array($rec)) { |
||
| 100 | $err = "Found non-struct in array at element $i"; |
||
| 101 | break; |
||
| 102 | } |
||
| 103 | if (!isset($rec['name']) || !isset($rec['age'])) { |
||
| 104 | Server::xmlrpc_debugmsg("Invalid array element $i: miss name or age"); |
||
| 105 | continue; |
||
| 106 | } |
||
| 107 | $agar[$rec["name"]] = $rec["age"]; |
||
| 108 | } |
||
| 109 | |||
| 110 | if ($err != '') { |
||
| 111 | Server::xmlrpc_debugmsg("Aborting 'agesorter'"); |
||
| 112 | return new Response(0, PhpXmlRpc::$xmlrpcerruser, $err); |
||
| 113 | } |
||
| 114 | |||
| 115 | asort($agar); |
||
| 116 | |||
| 117 | // create the output value |
||
| 118 | $o = array(); |
||
| 119 | foreach ($agar as $name => $age) { |
||
| 120 | $o[] = array("name" => $name, "age" => $age); |
||
| 121 | } |
||
| 122 | |||
| 123 | Server::xmlrpc_debugmsg("Leaving 'agesorter'"); |
||
| 124 | |||
| 125 | return new Response($enc->encode($o)); |
||
| 126 | } |
||
| 127 | |||
| 128 | public static $addtwo_sig = array(array('int', 'int', 'int')); |
||
| 129 | public static $addtwo_doc = 'Add two integers together and return the result'; |
||
| 130 | public static function addTwo($req) |
||
| 131 | { |
||
| 132 | $s = $req->getParam(0); |
||
| 133 | $t = $req->getParam(1); |
||
| 134 | |||
| 135 | return new Response(new Value($s->scalarVal() + $t->scalarVal(), Value::$xmlrpcInt)); |
||
| 136 | } |
||
| 137 | |||
| 138 | public static $addtwodouble_sig = array(array('double', 'double', 'double')); |
||
| 139 | public static $addtwodouble_doc = 'Add two doubles together and return the result'; |
||
| 140 | public static function addTwoDouble($req) |
||
| 141 | { |
||
| 142 | $s = $req->getParam(0); |
||
| 143 | $t = $req->getParam(1); |
||
| 144 | |||
| 145 | return new Response(new Value($s->scalarVal() + $t->scalarVal(), Value::$xmlrpcDouble)); |
||
| 146 | } |
||
| 147 | |||
| 148 | public static $stringecho_sig = array(array('string', 'string')); |
||
| 149 | 559 | public static $stringecho_doc = 'Accepts a string parameter, returns the string.'; |
|
| 150 | 559 | public static function stringEcho($req) |
|
| 151 | { |
||
| 152 | // just sends back a string |
||
| 153 | 43 | return new Response(new Value($req->getParam(0)->scalarVal())); |
|
| 154 | 43 | } |
|
| 155 | |||
| 156 | 43 | public static $echoback_sig = array(array('string', 'string')); |
|
| 157 | public static $echoback_doc = 'Accepts a string parameter, returns the entire incoming payload'; |
||
| 158 | public static function echoBack($req) |
||
| 159 | 559 | { |
|
| 160 | 559 | // just sends back a string with what I got sent to me, that's all |
|
| 161 | |||
| 162 | /// @todo file_get_contents does not take into account either receiving compressed requests, or requests with |
||
| 163 | 22 | /// data which is not in UTF-8. Otoh using req->serialize means that what we are sending back is not |
|
| 164 | 22 | /// byte-for-byte identical to what we received, and that <, >, ', " and & will be double-encoded. |
|
| 165 | /// In fact, we miss some API (or extra data) in the Request... |
||
| 166 | 22 | //$payload = file_get_contents('php://input'); |
|
| 167 | $payload = $req->serialize(PhpXmlRpc::$xmlrpc_internalencoding); |
||
| 168 | $s = "I got the following message:\n" . $payload; |
||
| 169 | 559 | ||
| 170 | 559 | return new Response(new Value($s)); |
|
| 171 | } |
||
| 172 | |||
| 173 | public static $echosixtyfour_sig = array(array('string', 'base64')); |
||
| 174 | 72 | public static $echosixtyfour_doc = 'Accepts a base64 parameter and returns it decoded as a string'; |
|
| 175 | public static function echoSixtyFour($req) |
||
| 176 | { |
||
| 177 | 559 | // Accepts an encoded value, but sends it back as a normal string. |
|
| 178 | 559 | // This is to test that base64 encoding is working as expected |
|
| 179 | $incoming = $req->getParam(0); |
||
| 180 | |||
| 181 | return new Response(new Value($incoming->scalarVal(), Value::$xmlrpcString)); |
||
| 182 | } |
||
| 183 | |||
| 184 | public static $bitflipper_sig = array(array('array', 'array')); |
||
| 185 | public static $bitflipper_doc = 'Accepts an array of booleans, and returns them inverted'; |
||
| 186 | public static function bitFlipper($req) |
||
| 187 | 559 | { |
|
| 188 | 559 | $v = $req->getParam(0); |
|
| 189 | $rv = new Value(array(), Value::$xmlrpcArray); |
||
| 190 | |||
| 191 | foreach ($v as $b) { |
||
| 192 | if ($b->scalarVal()) { |
||
| 193 | 22 | $rv[] = new Value(false, Value::$xmlrpcBoolean); |
|
| 194 | } else { |
||
| 195 | 22 | $rv[] = new Value(true, Value::$xmlrpcBoolean); |
|
| 196 | } |
||
| 197 | } |
||
| 198 | 559 | ||
| 199 | 559 | return new Response($rv); |
|
| 200 | } |
||
| 201 | |||
| 202 | 22 | public static $mailsend_sig = array(array( |
|
| 203 | 22 | 'boolean', 'string', 'string', |
|
| 204 | 'string', 'string', 'string', |
||
| 205 | 22 | 'string', 'string', |
|
| 206 | 22 | )); |
|
| 207 | 22 | public static $mailsend_doc = 'mail.send(recipient, subject, text, sender, cc, bcc, mimetype)<br/> |
|
| 208 | recipient, cc, and bcc are strings, comma-separated lists of email addresses, as described above.<br/> |
||
| 209 | 22 | subject is a string, the subject of the message.<br/> |
|
| 210 | sender is a string, it\'s the email address of the person sending the message. This string can not be |
||
| 211 | a comma-separated list, it must contain a single email address only.<br/> |
||
| 212 | text is a string, it contains the body of the message.<br/> |
||
| 213 | 22 | mimetype, a string, is a standard MIME type, for example, text/plain.'; |
|
| 214 | /** |
||
| 215 | * WARNING: this functionality depends on the sendmail -t option, it may not work with Windows machines properly; |
||
| 216 | 559 | * particularly the Bcc option. |
|
| 217 | 559 | * Sneak on your friends at your own risk! |
|
| 218 | */ |
||
| 219 | public static function mailSend($req) |
||
| 220 | { |
||
| 221 | $err = ""; |
||
| 222 | |||
| 223 | $mTo = $req->getParam(0); |
||
| 224 | $mSub = $req->getParam(1); |
||
| 225 | $mBody = $req->getParam(2); |
||
| 226 | $mFrom = $req->getParam(3); |
||
| 227 | $mCc = $req->getParam(4); |
||
| 228 | $mBcc = $req->getParam(5); |
||
| 229 | $mMime = $req->getParam(6); |
||
| 230 | |||
| 231 | if ($mTo->scalarVal() == "") { |
||
| 232 | $err = "Error, no 'To' field specified"; |
||
| 233 | } |
||
| 234 | |||
| 235 | if ($mFrom->scalarVal() == "") { |
||
| 236 | $err = "Error, no 'From' field specified"; |
||
| 237 | } |
||
| 238 | 559 | ||
| 239 | 559 | /// @todo in real life, we should check for presence of return characters to avoid header injection! |
|
| 240 | |||
| 241 | $msgHdr = "From: " . $mFrom->scalarVal() . "\n"; |
||
| 242 | 22 | $msgHdr .= "To: " . $mTo->scalarVal() . "\n"; |
|
| 243 | 22 | ||
| 244 | 22 | if ($mCc->scalarVal() != "") { |
|
| 245 | 22 | $msgHdr .= "Cc: " . $mCc->scalarVal() . "\n"; |
|
| 246 | 22 | } |
|
| 247 | if ($mBcc->scalarVal() != "") { |
||
| 248 | $msgHdr .= "Bcc: " . $mBcc->scalarVal() . "\n"; |
||
| 249 | 22 | } |
|
| 250 | if ($mMime->scalarVal() != "") { |
||
| 251 | $msgHdr .= "Content-type: " . $mMime->scalarVal() . "\n"; |
||
| 252 | 559 | } |
|
| 253 | 559 | $msgHdr .= "X-Mailer: XML-RPC for PHP mailer 1.0"; |
|
| 254 | |||
| 255 | if ($err == "") { |
||
| 256 | 1 | if (!mail("", $mSub->scalarVal(), $mBody->scalarVal(), $msgHdr)) { |
|
| 257 | 1 | $err = "Error, could not send the mail."; |
|
| 258 | } |
||
| 259 | } |
||
| 260 | |||
| 261 | 559 | if ($err) { |
|
| 262 | 559 | return new Response(0, PhpXmlRpc::$xmlrpcerruser, $err); |
|
| 263 | 559 | } else { |
|
| 264 | return new Response(new Value(true, Value::$xmlrpcBoolean)); |
||
| 265 | 559 | } |
|
| 266 | } |
||
| 267 | |||
| 268 | } |
||
| 269 | |||
| 270 | return array( |
||
| 271 | "examples.getStateName" => array( |
||
| 272 | "function" => array("exampleMethods", "findState"), |
||
| 273 | "signature" => exampleMethods::$findstate_sig, |
||
| 274 | "docstring" => exampleMethods::$findstate_doc, |
||
| 275 | ), |
||
| 276 | "examples.sortByAge" => array( |
||
| 277 | "function" => array("exampleMethods", "ageSorter"), |
||
| 278 | "signature" => exampleMethods::$agesorter_sig, |
||
| 279 | "docstring" => exampleMethods::$agesorter_doc, |
||
| 280 | ), |
||
| 281 | "examples.addtwo" => array( |
||
| 282 | "function" => array("exampleMethods", "addTwo"), |
||
| 283 | "signature" => exampleMethods::$addtwo_sig, |
||
| 284 | "docstring" => exampleMethods::$addtwo_doc, |
||
| 285 | ), |
||
| 286 | "examples.addtwodouble" => array( |
||
| 287 | "function" => array("exampleMethods", "addTwoDouble"), |
||
| 288 | "signature" => exampleMethods::$addtwodouble_sig, |
||
| 289 | "docstring" => exampleMethods::$addtwodouble_doc, |
||
| 290 | ), |
||
| 291 | "examples.stringecho" => array( |
||
| 292 | "function" => array("exampleMethods", "stringEcho"), |
||
| 293 | "signature" => exampleMethods::$stringecho_sig, |
||
| 294 | "docstring" => exampleMethods::$stringecho_doc, |
||
| 295 | ), |
||
| 296 | "examples.echo" => array( |
||
| 297 | "function" => array("exampleMethods", "echoBack"), |
||
| 298 | "signature" => exampleMethods::$echoback_sig, |
||
| 299 | "docstring" => exampleMethods::$echoback_doc, |
||
| 300 | ), |
||
| 301 | "examples.decode64" => array( |
||
| 302 | "function" => array("exampleMethods", "echoSixtyFour"), |
||
| 303 | "signature" => exampleMethods::$echosixtyfour_sig, |
||
| 304 | "docstring" => exampleMethods::$echosixtyfour_doc, |
||
| 305 | ), |
||
| 306 | "examples.invertBooleans" => array( |
||
| 307 | "function" => array("exampleMethods", "bitFlipper"), |
||
| 308 | "signature" => exampleMethods::$bitflipper_sig, |
||
| 309 | "docstring" => exampleMethods::$bitflipper_doc, |
||
| 310 | ), |
||
| 311 | |||
| 312 | // same as examples_getStateName, but with no dot - so that it is easier to map this into a method call |
||
| 313 | // by clients which map f.e. xmlrpc method names into php object method names |
||
| 314 | "examples_getStateName" => array( |
||
| 315 | "function" => array("exampleMethods", "findState"), |
||
| 316 | "signature" => exampleMethods::$findstate_sig, |
||
| 317 | "docstring" => exampleMethods::$findstate_doc, |
||
| 318 | ), |
||
| 319 | |||
| 320 | // left in as an example, but disabled by default, to avoid this being abused if left on an open server |
||
| 321 | /*"mail.send" => array( |
||
| 322 | "function" => array("exampleMethods", "mailSend"), |
||
| 323 | "signature" => exampleMethods::$mailsend_sig, |
||
| 324 | "docstring" => exampleMethods::$mailsend_doc, |
||
| 325 | ),*/ |
||
| 326 | ); |
||
| 327 |