The Thinkery Author
Published: July 13, 2026
Read: 5 min
In: Ideas & Tech

Having got the “hello World” ‘wstemplate’ Web-service plugin working as described in last night’s post, I tried modifying it to create a function of my own, that doubles its argument. This is how. I’d unzipped the plugin into a directory called wstemplate. First, I edited wstemplate/externallib.php:

require_once($CFG->libdir . "/externallib.php");

class local_jnip_external extends external_api 
{
  /**
   * Returns description of method parameters.
   * @return external_function_parameters
   */
  public static function double_parameters() 
  {
    return new external_function_parameters(
      array( 'n' => new external_value( PARAM_TEXT
                                      , 'The number to be doubled.'
                                      )
           )
    );
  }

  /**
   * Returns welcome message.
   * @return string welcome message
   */
  public static function double( $n ) 
  {
    global $USER;

    // Parameter validation.
    // REQUIRED.
    $params = self::validate_parameters( self::double_parameters()
                                       , array( 'n' => $n ) 
                                       );

    // Context validation.
    // OPTIONAL. Not needed for this function.

    // Capability checking.
    // OPTIONAL. Not needed for this function.

    return $params['n'] * 2;
  }

  /**
   * Returns description of method result value.
   * @return external_description
   */
  public static function double_returns() 
  {
    return new external_value( PARAM_TEXT, 'The argument doubled.' );
  }
}

In the above, I’ve changed the classname to local_jnip_external. (My initials are JNIP). I’ve replaced ‘hello_world’ by ‘double’ in the names of the three functions above. I’ve also altered the parameter and result descriptions so that they described double’s argument and result. Unlike hello_world, double doesn’t have a default value for its parameter, so I have removed that part of the parameter description. The parameter name, I’ve changed to ‘n’.

And I’ve removed the capability check at the end of ‘double’. I don’t know how likely one would be to write some kind of arithmetic Web service in real life — perhaps one would for a statistics add-on — but I can’t see that any security checks are needed at all. Therefore, I have also removed the context validation.

Next, I edited wstemplate/db/services.php:

// We define the Web-service functions to install.
//
$functions = array(
        'local_jnip_double' => array(
                'classname'   => 'local_jnip_external',
                'methodname'  => 'double',
                'classpath'   => 'local/jnip/externallib.php',
                'description' => 'Doubles its argument.',
                'type'        => 'read',
        )
);

// We define the services to install as pre-built services. 
// A pre-built service is not editable by the administrator.
//
$services = array(
        "Jocelyn's service" => array(
                'functions' => array ('local_jnip_double'),
                'restrictedusers' => 0,
                'enabled'=>1,
        )
);

Here, I’ve changed the outer key in the $functions array to local_jnip_double, and the class name, method name, class path, and description in the inner array to their new values. In the $services array, I’ve changed the service name and function name.

I left wstemplate/version.php and wstemplate/lang/en/local_wstemplate.php alone. Then I changed the directory name from wstemplate to jnip, so that on my Moodle’s Linux host, it sat at /var/www/moodle/moodle/local/jnip/ .

To make Moodle install the plugin, I went to Site administration > Notifications. Moodle had noticed the new directory, and showed it to me in a plugins table. I pressed the Upgrade button on that page, and then Continue on the next page.

I then went to Site administration > Plugins > Web services > External services, and saw a table listing my services, including a row for “Jocelyn’s service”. I clicked on the Functions link — and got an error saying that the description of the return value was corrupt. I’d mistyped it. So I corrected it in externallib.php . (The code above shows the corrected one.) Then I had to make Moodle notice this update. I remembered reading that it notices when the version number in a version.php file has been increased. So I edited wstemplate/version.php. The original version, the one I’d copied from wstemplate, looked like this:

$plugin->version  = 2011101202;   // The (date) version of this module + 2 extra digital for daily versions
                                  // This version number is displayed into /admin/forms.php
                                  // TODO: if ever this plugin get branched, the old branch number
                                  // will not be updated to the current date but just incremented. We will
                                  // need then a $plugin->release human friendly date. For the moment, we use
                                  // display this version number with userdate (dev friendly)
$plugin->requires = 2010112400;  // Requires this Moodle version - at least 2.0
$plugin->cron     = 0;
$plugin->release = '1.0 (Build: 2011101202)';
$plugin->maturity = MATURITY_STABLE;

I wasn’t really sure what to do — I’ve never seen a Moodle document about the correct care and feeding of version files — but it seemed reasonable to replace both occurrences of 2011101202 by 2011101203. I then went back to Site administration > Notifications. Again, Moodle showed me the table of plugins, saying that this one needed upgrading. So it had noticed the change. I pressed Upgrade, and then Continue.

For my client, I wrote this program:

  echo "Demo of Web service doubling function using REST\n";
  echo "================================================\n";

  $token = '401eae223b346c10c3298d74itv7ddf5';
  $domain = 'http://moodle.ireson-paine.com';

  $n = 5;

  $function_name = 'local_jnip_double';

  $params = array( 'n' => $n );

  $serverurl = $domain . '/webservice/rest/server.php'. '?wstoken=' . $token . '&wsfunction='.$function_name;

  require_once( './curl.php' );
  $curl = new curl;

  echo "\nAbout to call function.\n";

  $response = $curl->post( $serverurl, $params );

  echo "\nCalled function.\n";

  echo "\nResponse = $response.\n";

The token in it, I made by going to Site administration > Plugins > Web services > Manage tokens, and adding a new token for my Web-services user and the service called “Jocelyn’s service”.

And running the client produced this output:

Demo of Web service doubling function using REST
================================================

About to call function.

Called function.

Response = 

10

.

I also tried an XML-RPC version of the client, like this:

  echo "Demo of Web service doubling function using XML-RPC\n";
  echo "===================================================\n";

  $token = '401eae223b346c10c3298d74itv7ddf5';
  $domain = 'http://moodle.ireson-paine.com';

  $n = 5;

  $function_name = 'local_jnip_double';

  $params = array( $n );

  $serverurl = $domain . '/webservice/xmlrpc/server.php'. '?wstoken=' . $token . '&wsfunction='.$function_name;

  require_once( './curl.php' );
  $curl = new curl;

  echo "\nAbout to call function.\n";

  $post = xmlrpc_encode_request( $function_name, $params );

  $response = xmlrpc_decode($curl->post($serverurl, $post));

  echo "\nCalled function.\n";

  echo "\nResponse = "; print_r($response); echo ".\n";

But I got an error message saying that the parameter didn’t match its description. This happened also when I set $params to array( n=>$n ). I haven’t worked out what I should write, but because I’m likely to keep on using REST rather than XML-RPC, I’m not worrying about it.

Join the Discourse