3 // This file is part of Moodle - http://moodle.org/
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
19 * AMF web service implementation classes and methods.
22 * @copyright 2009 Moodle Pty Ltd (http://moodle.com)
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 require_once("$CFG->dirroot/webservice/lib.php");
27 require_once( "{$CFG->dirroot}/webservice/amf/introspector.php");
28 require_once 'Zend/Amf/Server.php';
30 * Exception indicating an invalid return value from a function.
31 * Used when an externallib function does not return values of the expected structure.
33 class invalid_return_value_exception
extends moodle_exception
{
36 * @param string $debuginfo some detailed information
38 function __construct($debuginfo=null) {
39 parent
::__construct('invalidreturnvalue', 'webservice_amf', '', $debuginfo, $debuginfo);
44 * AMF service server implementation.
45 * @author Petr Skoda (skodak)
47 class webservice_amf_server
extends webservice_zend_server
{
50 * @param integer $authmethod authentication method - one of WEBSERVICE_AUTHMETHOD_*
52 public function __construct($authmethod) {
53 parent
::__construct($authmethod, 'Moodle_Amf_Server');
54 $this->wsname
= 'amf';
56 protected function init_service_class(){
57 parent
::init_service_class();
58 //allow access to data about methods available.
59 $this->zend_server
->setClass( "MethodDescriptor" );
60 MethodDescriptor
::$classnametointrospect = $this->service_class
;
63 protected function service_class_method_body($function, $params){
64 //cast the param from object to array (validate_parameters except array only)
67 $paramstocast = explode(',', $params);
68 foreach ($paramstocast as $paramtocast) {
69 $paramtocast = trim($paramtocast);
70 $castingcode .= $paramtocast .
71 '=webservice_zend_server::cast_objects_to_array('.$paramtocast.');';
76 $externallibcall = $function->classname
.'::'.$function->methodname
.'('.$params.')';
77 $descriptionmethod = $function->methodname
.'_returns()';
78 $callforreturnvaluedesc = $function->classname
.'::'.$descriptionmethod;
80 ' return webservice_amf_server::validate_and_cast_values('.$callforreturnvaluedesc.', '.$externallibcall.');';
83 * Validates submitted value, comparing it to a description. If anything is incorrect
84 * invalid_return_value_exception is thrown. Also casts the values to the type specified in
86 * @param mixed $description description of parameters or null if no return value
87 * @param mixed $value the actual values
88 * @param boolean $singleasobject specifies whether a external_single_structure should be cast to a stdClass object
89 * should always be false for use in validating parameters in externallib functions.
90 * @return mixed params with added defaults for optional items, invalid_parameters_exception thrown if any problem found
92 public static function validate_and_cast_values($description, $value) {
93 if (is_null($description)){
96 if ($description instanceof external_value
) {
97 if (is_array($value) or is_object($value)) {
98 throw new invalid_return_value_exception('Scalar type expected, array or object received.');
101 if ($description->type
== PARAM_BOOL
) {
102 // special case for PARAM_BOOL - we want true/false instead of the usual 1/0 - we can not be too strict here ;-)
103 if (is_bool($value) or $value === 0 or $value === 1 or $value === '0' or $value === '1') {
107 return validate_param($value, $description->type
, $description->allownull
, 'Invalid external api parameter');
109 } else if ($description instanceof external_single_structure
) {
110 if (!is_array($value)) {
111 throw new invalid_return_value_exception('Only arrays accepted.');
114 foreach ($description->keys
as $key=>$subdesc) {
115 if (!array_key_exists($key, $value)) {
116 if ($subdesc->required
== VALUE_REQUIRED
) {
117 throw new invalid_return_value_exception('Missing required key in single structure: '.$key);
119 if ($subdesc instanceof external_value
) {
120 if ($subdesc->required
== VALUE_DEFAULT
) {
121 $result[$key] = self
::validate_and_cast_values($subdesc, $subdesc->default);
125 $result[$key] = self
::validate_and_cast_values($subdesc, $value[$key]);
129 /* Was decided that extra keys should just be ignored and not returned.
130 * if (!empty($value)) {
131 throw new invalid_return_value_exception('Unexpected keys detected in parameter array.');
133 return (object)$result;
135 } else if ($description instanceof external_multiple_structure
) {
136 if (!is_array($value)) {
137 throw new invalid_return_value_exception('Only arrays accepted.');
140 foreach ($value as $param) {
141 $result[] = self
::validate_and_cast_values($description->content
, $param);
146 throw new invalid_return_value_exception('Invalid external api description.');
151 * Set up zend service class
154 protected function init_zend_server() {
155 parent
::init_zend_server();
156 $this->zend_server
->setProduction(false); //set to false for development mode
157 //(complete error message displayed into your AMF client)
158 // TODO: add some exception handling
163 class Moodle_Amf_Server
extends Zend_Amf_Server
{
165 * Raise a server fault
167 * @param string|Exception $fault
170 public function fault($fault = null, $code = 404)
172 if (!$fault instanceof Exception
) {
173 $fault = new Exception($fault);
175 $request = $this->getRequest();
176 // Get the object encoding of the request.
177 $objectEncoding = $request->getObjectEncoding();
179 // create a response object to place the output from the services.
180 $response = $this->getResponse();
182 // set reponse encoding
183 $response->setObjectEncoding($objectEncoding);
185 $responseBody = $request->getAmfBodies();
187 foreach($responseBody as $body){
188 $return = $this->_errorMessage($objectEncoding, $fault->getMessage(),
189 $fault->getMessage(), $fault->getTraceAsString(),$fault->getCode(), $fault->getLine());
190 $responseType = Zend_AMF_Constants
::STATUS_METHOD
;
193 $responseURI = $body->getResponseURI() . $responseType;
194 $newBody = new Zend_Amf_Value_MessageBody($responseURI, null, $return);
195 $response->addAmfBody($newBody);
197 $response->finalize();
202 // TODO: implement AMF test client somehow, maybe we could use moodle form to feed the data to the flash app somehow