diff --git a/lib/externallib.php b/lib/externallib.php index e6d5e99b955..24002e0bc44 100644 --- a/lib/externallib.php +++ b/lib/externallib.php @@ -182,7 +182,7 @@ class external_api { require_once($CFG->libdir . "/pagelib.php"); - $externalfunctioninfo = self::external_function_info($function); + $externalfunctioninfo = static::external_function_info($function); $currentpage = $PAGE; $currentcourse = $COURSE; @@ -252,7 +252,7 @@ class external_api { $response['error'] = false; $response['data'] = $result; - } catch (Exception $e) { + } catch (Throwable $e) { $exception = get_exception_info($e); unset($exception->a); $exception->backtrace = format_backtrace($exception->backtrace, true); diff --git a/lib/tests/externallib_test.php b/lib/tests/externallib_test.php index 619ae286a6c..aa8101f3f4e 100644 --- a/lib/tests/externallib_test.php +++ b/lib/tests/externallib_test.php @@ -535,7 +535,7 @@ class core_externallib_testcase extends advanced_testcase { public function test_call_external_function() { - global $PAGE, $COURSE; + global $PAGE, $COURSE, $CFG; $this->resetAfterTest(true); @@ -570,6 +570,16 @@ class core_externallib_testcase extends advanced_testcase { $this->assertSame($beforepage, $PAGE); $this->assertSame($beforecourse, $COURSE); + + // Test a function that triggers a PHP exception. + require_once($CFG->dirroot . '/lib/tests/fixtures/test_external_function_throwable.php'); + + // Call our test function. + $result = test_external_function_throwable::call_external_function('core_throw_exception', array(), false); + + $this->assertTrue($result['error']); + $this->assertArrayHasKey('exception', $result); + $this->assertEquals($result['exception']->message, 'Exception - Modulo by zero'); } /** diff --git a/lib/tests/fixtures/test_external_function_throwable.php b/lib/tests/fixtures/test_external_function_throwable.php new file mode 100644 index 00000000000..b1e975d73ff --- /dev/null +++ b/lib/tests/fixtures/test_external_function_throwable.php @@ -0,0 +1,86 @@ +<?php +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + +/** + * An external function that throws an exception, for tests. + * + * @package core + * @category phpunit + * @copyright 2020 Dani Palou + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once("$CFG->libdir/externallib.php"); + +/** + * Create an external function that throws an exception, for tests. + * + * @copyright 2020 Dani Palou + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class test_external_function_throwable extends external_api { + + /** + * Returns description of throw_exception() parameters. + * + * @return external_function_parameters + */ + public static function throw_exception_parameters() { + return new external_function_parameters(array()); + } + + /** + * Throws a PHP error. + * + * @return array empty array. + */ + public static function throw_exception() { + $a = 1 % 0; + + return array(); + } + + /** + * Returns description of throw_exception() result value. + * + * @return external_description + */ + public static function throw_exception_returns() { + return new external_single_structure(array()); + } + + /** + * Override external_function_info to accept our fake WebService. + */ + public static function external_function_info($function, $strictness=MUST_EXIST) { + if ($function == 'core_throw_exception') { + // Convert it to an object. + $function = new stdClass(); + $function->name = $function; + $function->classname = 'test_external_function_throwable'; + $function->methodname = 'throw_exception'; + $function->classpath = ''; // No need to define class path because current file is already loaded. + $function->component = 'fake'; + $function->capabilities = ''; + $function->services = 'moodle_mobile_app'; + $function->loginrequired = false; + } + + return parent::external_function_info($function, $strictness); + } +}