diff --git a/lib/externallib.php b/lib/externallib.php index b2990257eb816..f69050a4366a5 100644 --- a/lib/externallib.php +++ b/lib/externallib.php @@ -48,7 +48,7 @@ function external_function_info($function, $strictness=MUST_EXIST) { // Fallback to explicit include of externallib.php. $function->classpath = empty($function->classpath) ? core_component::get_component_directory($function->component).'/externallib.php' : $CFG->dirroot.'/'.$function->classpath; if (!file_exists($function->classpath)) { - throw new coding_exception('Cannot find file with external function implementation'); + throw new coding_exception('Cannot find file with external function implementation: ' . $function->classname); } require_once($function->classpath); if (!class_exists($function->classname)) { @@ -526,6 +526,30 @@ public function __construct(external_description $content, $desc='', * @since Moodle 2.0 */ class external_function_parameters extends external_single_structure { + + /** + * Constructor - does extra checking to prevent top level optional parameters. + * + * @param array $keys + * @param string $desc + * @param bool $required + * @param array $default + */ + public function __construct(array $keys, $desc='', $required=VALUE_REQUIRED, $default=null) { + global $CFG; + + if ($CFG->debugdeveloper) { + foreach ($keys as $key => $value) { + if ($value instanceof external_value) { + if ($value->required == VALUE_OPTIONAL) { + debugging('External function parameters: invalid OPTIONAL value specified.', DEBUG_DEVELOPER); + break; + } + } + } + } + parent::__construct($keys, $desc, $required, $default); + } } /** diff --git a/lib/tests/externallib_test.php b/lib/tests/externallib_test.php index 1012a6d605301..02d64774e6ac4 100644 --- a/lib/tests/externallib_test.php +++ b/lib/tests/externallib_test.php @@ -213,6 +213,35 @@ public function test_get_context_params3() { $this->setExpectedException('invalid_parameter_exception'); test_exernal_api::get_context_wrapper(array('roleid' => 3, 'userid' => $USER->id, 'instanceid' => $course->id)); } + + public function all_external_info_provider() { + global $DB; + + // We are testing here that all the external function descriptions can be generated without + // producing warnings. E.g. misusing optional params will generate a debugging message which + // will fail this test. + $functions = $DB->get_records('external_functions', array(), 'name'); + $return = array(); + foreach ($functions as $f) { + $return[$f->name] = array($f); + } + return $return; + } + + /** + * @dataProvider all_external_info_provider + */ + public function test_all_external_info($f) { + $desc = external_function_info($f); + $this->assertNotEmpty($desc->name); + $this->assertNotEmpty($desc->classname); + $this->assertNotEmpty($desc->methodname); + $this->assertEquals($desc->component, clean_param($desc->component, PARAM_COMPONENT)); + $this->assertInstanceOf('external_function_parameters', $desc->parameters_desc); + if ($desc->returns_desc != null) { + $this->assertInstanceOf('external_description', $desc->returns_desc); + } + } } /*