Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
call_function_node
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
5 / 5
8
100.00% covered (success)
100.00%
1 / 1
 type
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 params
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 branches
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 call
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2// This file is part of Moodle - https://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <https://www.gnu.org/licenses/>.
16
17namespace tool_autoflow\local\nodes;
18
19use core_external\external_api;
20use tool_autoflow\local\engine\node_failed;
21use tool_autoflow\local\engine\node_result;
22use tool_autoflow\local\engine\node_type;
23use tool_autoflow\local\engine\param;
24
25/**
26 * Calls any Moodle web service function: the Moodle API as one node.
27 *
28 * The call follows external_api::call_external_function() without its sesskey check,
29 * which protects browser sessions and has no meaning in a cron task: the arguments are
30 * validated against the function's definition, the function runs its own context and
31 * capability checks as the user the workflow runs as, and the result is cleaned.
32 * Arguments are a JSON object whose values may hold placeholders. Placeholders are
33 * filled value by value, never pasted into JSON text, so trigger data cannot add
34 * or change keys.
35 *
36 * @package    tool_autoflow
37 * @copyright  2026 DevLMS <contact@devlms.com>
38 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 */
40final class call_function_node implements node_type {
41    /**
42     * Type name.
43     *
44     * @return string
45     */
46    public function type(): string {
47        return 'moodle.call_function';
48    }
49
50    /**
51     * Parameters.
52     *
53     * @return param[]
54     */
55    public function params(): array {
56        return [new param('function', 'function', true), new param('args', 'json', false)];
57    }
58
59    /**
60     * Branches.
61     *
62     * @return string[]
63     */
64    public function branches(): array {
65        return [];
66    }
67
68    /**
69     * Call the function.
70     *
71     * @param array $params function, args
72     * @param array $context
73     * @return node_result
74     */
75    public function execute(array $params, array $context): node_result {
76        $function = (string) ($params['function'] ?? '');
77        $args = $params['args'] ?? [];
78        if (!is_array($args)) {
79            throw new node_failed('The arguments must be a JSON object.');
80        }
81        return new node_result(['result' => self::call($function, $args)]);
82    }
83
84    /**
85     * Call a web service function as the current user, the way the web service layer does.
86     *
87     * Other Moodle nodes use this too, so they get the same validation and capability checks.
88     *
89     * @param string $function
90     * @param array $args
91     * @return mixed The cleaned result.
92     */
93    public static function call(string $function, array $args): mixed {
94        try {
95            $info = external_api::external_function_info($function);
96            $values = array_values(external_api::validate_parameters($info->parameters_desc, $args));
97            $result = call_user_func_array([$info->classname, $info->methodname], $values);
98            if ($info->returns_desc !== null) {
99                $result = external_api::clean_returnvalue($info->returns_desc, $result);
100            }
101        } catch (\Throwable $e) {
102            throw new node_failed("$function: " . $e->getMessage());
103        }
104        return $result;
105    }
106}