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 |