logo

Using Moodle Web Services

What is Moodle?

No, it's not Mudilo, not even related to $mudilo - the variable my brother programmer used for objects in his code... well, until his boss once asked what the name meant... pronouncing it in a rather fancy way...

Moodle is web-based software written in PHP that provides online courses. See moodle.org. For example, one could create a course about something, and then people from all over may learn about it.

I was given an assignment to research how to create Moodle users programmatically from a PHP program. Additionally, my client wanted an ability to enroll users in courses. This manual describes how to do it.

If You Need Help with Moodle

I do occasional work via doitcontractors. If you need something done consider posting a project there. If you are a coder, you can also register and maybe get an occasional project to work on.

Versions

Here are the versions of things I experimented with.
  • Moodle 2.4.3.
  • Running on Windows 7 with PHP 5.4.11.
  • MySQL 5.5.27.
As things in Moodle camp seem to change rather quickly, like {mountain weather | product_placement_here}, procedures should be adjusted for your versions of software.

Preliminary Notes

I had to go through many steps before things started to work. Also, based on my personal experience, Moodle web services API tends to change from version to version as they re-write stuff. IMHO, this API needs re-work anyway, so it is normal to expect things to be different as time flies. I hope someone will find the troubleshooting techniques described below useful.

Briefly, all you need to do to make use of Moodle web services API is just two things:
  • Configure Moodle server properly. This is a rather lengthy, multiple-step procedure.
  • Make your API calls with properly formatted parameters. You may need to get creative here when things don't work.

Configuring Moodle

The following were the steps I needed to do to make my code examples working.

Install Moodle Server

In this step we install Moodle 2.4.3. I did it on a Windows 7, with already installed Apache 2.2 and PHP 5.4.11. Installation process looked like this:
  • Download and deploy Moodle code from moodle.org code on web server.
  • Create database, grant access permissions to the database.
  • Access Moodle instance via web browser and follow a lengthy process of Moodle install.

Documentation for Web Services

At the time of this writing, web services documentation for my version of Moodle was at http://docs.moodle.org/24/en/Web_services

Enable Web Services

  • Go to Home - Site administration - Advanced features. Mark the Enable web services option. Click Save changes button on the bottom.
  • Go to Home - Site administration - Plugins - Web services - Manage protocols. Enable the XML-RPC protocol (by clicking on the marker in the Enable column). Click Save changes button on the bottom.

Create a Web Service

  • Go to Home - Site administration - Plugins - Web services - External services. Click Add. Specify a name (for example, nik_test) and mark the Enabled box.
  • Add functions to just created web service. Go to Home - Site administration - Plugins - Web services - External services - Functions. Click Add functions. I needed to add the following functions:
    • core_user_get_users_by_id - to get information about users.
    • core_user_create_users - to create users.
    • core_user_update_users - to update users.
    • core_user_delete_users - to update users.
    • core_course_get_courses - to get information about courses.
    • enrol_manual_enrol_users - to enroll users in courses.
 Adding functions to a web service
Adding functions to a web service

On the picture above, notice the location of the page in the Site administration tree, see the 6 functions added to the service and pay attention to the Required capabilities column - these are access rights required to execute the corresponding functions.

Create a User Account to Use Web Services

Go to Home - Site administration - Users - Accounts - Browse list of users. Click the Add a new user link. Create a user, for example, web_service_user with all default other values.
 Creating a user account to access the web service
Creating a user account to access the web service

Create a New System Role

Go to Home - Site Administration - Users - Permissions - Define roles and create a new role, for example Web Services Users.
 Creating a new system role in Moodle
Creating a new system role in Moodle


Make sure the role can be assigned in the "System" context by editing the role and marking the "System" checkbox in "Context types where this role can be assigned".

Assign Capabilities to the Role

Now we need to assign capabilities to this role. It is critical for this role to have all the required capabilities in order to execute the functions above. When a capability is missing, Moodle generates and catches exceptions internally, leaving you in the dark about what may be wrong, see debugging section below. Here is a list of capabilities we need:
  • webservice/xmlrpc:use - or Use XML-RPC protocol - required to process XML-RPC communications.
  • moodle/user:viewdetails - or View user profiles - required to view user info.
  • moodle/user:viewhiddendetails - or View hidden details of users - required to view user info.
  • moodle/course:useremail - or Enable/disable email address - required to view user info.
  • moodle/user:create - or Create users - required to create user accounts.
  • moodle/user:delete - or Delete users - required to delete user accounts.
  • moodle/user:update - or Update user profiles - required to update user accounts.
  • moodle/course:view - or View courses without participation - required to get course info.
  • moodle/course:viewhiddencourses - or View hidden courses - required to get course info.
  • moodle/course:update - or Update course settings - required to get course info.
  • enrol/manual:enrol - or Enrol users - required to enroll users in courses.

Allow to Assign the Student Role

There is an additional step that is required for the enroll function to work. We need to allow Web Services Users to assign the Student role to accounts. This is done on the Allow role assignments tab in Site administration - Users - Permissions - Define Roles.
 Allow Web Services Users to assign the Student role
Allow Web Services Users to assign the Student role

Make sure that the Student checkbox is checked for Web Services Users role. By now we defined a system role that is capable to execute our functions.

Assign User to the Role

Assign web_service_user account to the Web Services Users role by going to Home - Site administration - Users - Permissions - Assign system roles. Click on the Web Services Users and add the web_service_user to the role on the next page. This makes web_service_user to have the capabilities assigned to the Web Services Users role in the previous steps. By now we have a Moodle user account with the capabilities necessary to execute our functions.

Create a Token

We'll need a token for programmatic access to the web service we created. Go to Home - Site administration - Plugins - Web services - Manage tokens. Click Add. Select a user and a service and generate token.
 Create a token to access the web service programmatically
Create a token to access the web service programmatically

Summary

By this point we have:
  • Installed Moodle 2.4.3.
  • Enabled web services in it.
  • Created nik_test web service with a few functions.
  • Created web_service_user account.
  • Created "Web Services Users" system role with necessary capabilities to execute our functions and also a capability to assign the "Student" role to other accounts.
  • Created a token for web_service_user account for programmatic access to nik_test service.

Debugging and Troubleshooting

As you can see from the above description, the setup procedure is quite demanding. However, in addition to a potential configuration issue (in case you did everything right with Moodle setup, as described above, with adjustments for your version), many other points of failure are also possible, here are a few possibilities:
  • Moodle expects parameters in a certain way, documentation conflicting, incorrect, missing, or deprecated.
  • Coding errors.
  • Component errors. For example, PEAR XML_RPC package that I used in earlier samples could not decode some replies from Moodle, such as containing <value><nil/></value> elements.
  • Moodle returns a call that contains nothing and it is unclear what's wrong.
Here are a couple of troubleshooting ideas that worked for me.
  1. Slightly modify Moodle code to output additional info to web server log. This was the primary thing.
  2. Had a special tool to observe incoming XML that hits the server.
I tried to work like this:
  • Understand the XML format of the request that I need to send to Moodle.
  • Create such request and observe it somewhere to make sure the format is right. I did it by writing a simple Java servlet running under Tomcat that was dumping the POST body to its console. Then configured my client to talk to it, and looked at XML. When you see XML everything becomes so much easier.
 Observing XML that hits Moodle server
Observing XML that hits Moodle server

However, if you use xmlrpc_encode_request as I do, you can also see the XML right in PHP client by doing var_dump:

// Create XML for Moodle.
$request = xmlrpc_encode_request('core_user_get_users_by_id', array(array((string) $user_id)), array('encoding'=>'UTF-8'));
var_dump($request);  // In case you want to see XML.

  • When we are sure the request is correct we fire it at Moodle and see what happens. At this point, in case of problems, we need to examine web server error log. It will help if you start with a clean error log before making a call. From that point on, we determine where in Moodle the problem occurs, and insert additional debug output in Moodle code around the place to localize the issue precisely.
  • You can dump simple values and strings using the error_log() function but dumping arrays does not work, until you know this trick, which works a bit better:
ob_start();
print_r($sigParams);
$contents = ob_get_contents();
ob_end_clean();
error_log($contents);


Solving Problems

Here are three problems that were most challenging for me in the scope of this project, and how I solved them.

1) My previous code samples used PEAR XML_RPC package to do XML-RPC communications. Things kind of worked but not always. In the end and after a long struggle, I dumped the content of replies that Moodle sends back to error.log and saw this:

<?xml version="1.0" encoding="UTF-8"?>\n
<methodResponse>
  <params>
    <param>
      <value><nil/></value>
    </param>
  </params>
</methodResponse>


See the <nil/> inside the <value> element. I turned out that the XML_RPC package on the client could not decode such elements. The solution was to abandon XML_RPC package and use the experimental xmlrpc functions that are a part of PHP.

2) The second, and probably the most important problem, was to figure out how to construct parameters in XML for each function call. They need to be built in a certain fashion. For me, the difficult function was enrol_manual_enrol_users, as I could not figure it out, and the docs as well as code seemed conflicting to me. So, I narrowed the problem down to a place where Moodle compares a signature for a function with the actual structure received. Dumped the (beginnings of...) signatures and saw some differences. Finally, I figured out that the parameters need to be like:

$params = array(array(array('roleid'=>'5', 'userid'=>'42', 'courseid'=>'2')));
$request = xmlrpc_encode_request('enrol_manual_enrol_users', $params, array('encoding'=>'UTF-8'));
var_dump($request);  // In case you want to see XML.

3) Missing capabilities for the web service account. When this happens, Moodle generates exceptions. You can localize them in web server logs and then use the technique above to find out the exact reason. Well, there is a thing that may save you a lot of time: in Moodle look at the Home - Site administration - Plugins - Web services - Manage tokens page. If any capabilities are missing for your functions you will see them there.

Code Samples with Moodle Class

I wrote a class called Moodle that you can instantiate and call. Something like this:

$token = 'cface2c6d6e7888f5e33485008dbda88';
$server = 'http://localhost:8080';
$dir = '/moodle'; // May be null if moodle runs in the root directory in the server.

// To do things with Moodle, we create a new Moodle class, initialize it, and then call its functions.
$moodle = new Moodle();

// Initialize the class.
$fields = array('token'=>$token, 'server'=>$server, 'dir'=>$dir);
$moodle->init($fields);

// Usage examples.
// Normally, a function returns something useful, such as an array of user properties, or TRUE, on success.
// When something happens the return is FALSE, and the last error string is in $moodle->error string.
// A lot of things need to be done in Moodle for web services API to work properly. See readme in this project.

// Get user information.
$id = 1; // User id in Moodle. 1 for guest user.
$user = $moodle->getUser($id);
if ($user)
  var_dump($user);          // Success, normal result.
else
  var_dump($moodle->error); // Error.

The following functions are supported by the class at the moment:
  • getUser($user_id)
  • createUser($fields)
  • updateUser($fields)
  • deleteUser($user_id)
  • getCourse($course_id)
  • enrollUser($user_id, $course_id)
They all work in a similar fashion: they return something useful such as the result of the requested operation. If an error occurs, the return is FALSE, and you can look at the $error member for information about the last error.

Code for the Moodle Class


<?php

class Moodle {
  var $token = null;  // Token to access Moodle server. Must be configured in Moodle. See readme.
  var $server = null; // Moodle URL, for example http://localhost:8080.
  var $dir = null;    // Directory on the server. For example, /moodle. If your moodle runs as root, this is empty.
  var $error = '';    // Last error of the class. We'll write the last error here when something wrong happens.
 	
  // The init function initializes the variable of class (so that it can be used).
  function init($fields) {
    $this->token = $fields['token'];
    $this->server = $fields['server'];
    $this->dir = $fields['dir'];
  }
  
  // The getUser function obtains information for a Moodle user identified by its id.
  function getUser($user_id) {
  	// Clear last error.
  	$this->error = null;
    
  	// Create XML for the request. XML must be set properly for this to work.
    $request = xmlrpc_encode_request('core_user_get_users_by_id', array(array((string) $user_id)), array('encoding'=>'UTF-8'));
    // var_dump($request);  // In case you want to see XML.
    
    $context = stream_context_create(array('http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request
    )));
    
    $path = $this->server.$this->dir."/webservice/xmlrpc/server.php?wstoken=".$this->token;
    // Send XML to server and get a reply from it.
    $file = file_get_contents($path, false, $context); // $file is the reply from server.
    // Decode the reply.
    $response = xmlrpc_decode($file);

    // Note: lack of permissions on Moodle will get us an XML-formatted response with NULL values.
    // In other words, one must be absolutely sure to give all the required capabilities to web services account
    // in order to execute this function successfully. Moodle says that we need the following:
    // moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update
    // for core_user_get_users_by_id call.

    // Handle errors.
    if (!is_array($response) || !is_array($response[0]) || !array_key_exists('id', $response[0])) {
      // We have an error.
      if ($response[faultCode])
        $this->error = 'Moodle error: ' . $response[faultString] . ". Fault code: ".$response[faultCode]. ".";
      else
        $this->error = 'Moodle returned no info. Check if user id exists and whether the web service
          account has capabilities required to execute core_user_get_users_by_id call.';
      $this->error .= " Actual reply from server: ".$file;
      return false;
    }
    
    // This is our normal exit (returning an array of user properties).
    $user = $response[0];
    return $user;
  }
  
  // The createUser function tries to create a new Moodle user.
  function createUser($fields) {
  	// Clear last error.
  	$this->error = null;

  	// Construct user fields array.
    $userFields = array();
    if (isset($fields['username'])) $userFields['username'] = $fields['username'];
    if (isset($fields['password'])) $userFields['password'] = $fields['password'];
    if (isset($fields['firstname'])) $userFields['firstname'] = $fields['firstname'];
    if (isset($fields['lastname'])) $userFields['lastname'] = $fields['lastname'];
    if (isset($fields['email'])) $userFields['email'] = $fields['email'];
    if (isset($fields['city'])) $userFields['city'] = $fields['city'];
    if (isset($fields['country'])) $userFields['country'] = $fields['country'];
    // if (isset($fields['auth'])) $userFields['auth'] = $fields['auth'];
    if (isset($fields['preferences'])) $userFields['preferences'] = $fields['preferences'];
  	
    // Create XML for the request. XML must be set properly for this to work.
    $request = xmlrpc_encode_request('core_user_create_users', array(array($userFields)), array('encoding'=>'UTF-8'));
    // var_dump($request);  // In case you want to see XML.
    
    $context = stream_context_create(array('http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request
    )));
    
    $path = $this->server.$this->dir."/webservice/xmlrpc/server.php?wstoken=".$this->token;
    // Send XML to server and get a reply from it.
    $file = file_get_contents($path, false, $context); // $file is the reply from server.
    // Decode the reply.
    $response = xmlrpc_decode($file);

    // Note: lack of permissions on Moodle will get us an error.
    // moodle/user:create capability is required for web service account to call core_user_create_users.

    // Handle errors.
    if (!is_array($response) || !is_array($response[0]) || !array_key_exists('id', $response[0])) {
      // We have an error.
      if ($response[faultCode])
        $this->error = 'Moodle error: ' . $response[faultString] . ". Fault code: ".$response[faultCode]. ".";
      else
        $this->error = 'Moodle returned no info. Check if Moodle is set up properly (see readme).';
      $this->error .= " Actual reply from server: ".$file;
      return false;
    }
    
    // This is our normal exit. Returning a 2-member array with new user id and username.
    $user = $response[0];
    return $user;
  } 

  // The createUser function tries to update an existing Moodle user.
  function updateUser($fields) {
  	// Clear last error.
  	$this->error = null;
  	
  	// Check if user exists.
  	$user = $this->getUser($fields['id']);
  	if (!$user)
  	  return false;

  	// Construct user fields array.
    $userFields = array();
    if (isset($fields['id'])) $userFields['id'] = $fields['id'];
    if (isset($fields['username'])) $userFields['username'] = $fields['username'];
    if (isset($fields['password'])) $userFields['password'] = $fields['password'];
    if (isset($fields['firstname'])) $userFields['firstname'] = $fields['firstname'];
    if (isset($fields['lastname'])) $userFields['lastname'] = $fields['lastname'];
    if (isset($fields['email'])) $userFields['email'] = $fields['email'];
    if (isset($fields['city'])) $userFields['city'] = $fields['city'];
    if (isset($fields['country'])) $userFields['country'] = $fields['country'];
    if (isset($fields['auth'])) $userFields['auth'] = $fields['auth'];
    if (isset($fields['preferences'])) $userFields['preferences'] = $fields['preferences'];
  	
  	// Create XML for the request. XML must be set properly for this to work.
    $request = xmlrpc_encode_request('core_user_update_users', array(array($userFields)), array('encoding'=>'UTF-8'));
    // var_dump($request);  // In case you want to see XML.
    
    $context = stream_context_create(array('http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request
    )));
    
    $path = $this->server.$this->dir."/webservice/xmlrpc/server.php?wstoken=".$this->token;
    // Send XML to server and get a reply from it.
    $file = file_get_contents($path, false, $context); // $file is the reply from server.
    // Decode the reply.
    $response = xmlrpc_decode($file);

    // Note: lack of permissions on Moodle will get us an error.
    // moodle/user:update capability is required for web service account to call core_user_update_users.

    if ($response && xmlrpc_is_fault($response)) {
      $this->error = 'Moodle error: ' . $response[faultString] . ". Fault code: ".$response[faultCode]. ".";
      $this->error .= " Actual reply from server: ".$file;
      return false;
    }
    
    // This is our normal exit after a successful update.
    return true;
  }
  
  // The deleteUser function tries to delete an existing Moodle user.
  function deleteUser($user_id) {
  	// Clear last error.
  	$this->error = null;
  	
  	// Check if user exists.
  	$user = $this->getUser($user_id);
  	if (!$user)
  	  return false;
  	  
  	// Create XML for the request. XML must be set properly for this to work.
  	$request = xmlrpc_encode_request('core_user_delete_users', array(array((string) $user_id)), array('encoding' => 'UTF-8'));
    // var_dump($request);  // In case you want to see XML.
        
    $context = stream_context_create(array('http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request
    )));
    
    $path = $this->server.$this->dir."/webservice/xmlrpc/server.php?wstoken=".$this->token;
    // Send XML to server and get a reply from it.
    $file = file_get_contents($path, false, $context); // $file is the reply from server.
    // Decode the reply.
    $response = xmlrpc_decode($file);
     
    if ($response && xmlrpc_is_fault($response)) {
      $this->error = "Moodle error: " . $response[faultString]." Fault code: " . $response[faultCode];
      return false;
    }
    
    // This is our normal exit after a successful delete.
    return true;
  }
  
  // The getCourse function obtains information for a Moodle course identified by its id.
  function getCourse($id) {
  	// Clear last error.
  	$this->error = null;
    
  	// Create XML for the request. XML must be set properly for this to work.
  	$courseids = array( $id );
    // $params = array('options'=>array('ids'=>$courseids)); // This does not work, gets us an exception inside Moodle.
    $params = array(array('ids'=>$courseids)); // This works.
  	$request = xmlrpc_encode_request('core_course_get_courses', $params, array('encoding'=>'UTF-8'));
    // var_dump($request);  // In case you want to see XML.
    
    $context = stream_context_create(array('http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request
    )));
    
    $path = $this->server.$this->dir."/webservice/xmlrpc/server.php?wstoken=".$this->token;
    // Send XML to server and get a reply from it.
    $file = file_get_contents($path, false, $context); // $file is the reply from server.
    // Decode the reply.
    $response = xmlrpc_decode($file);

    // Note: lack of permissions on Moodle will get us an error.
    // Required capabilities for core_course_get_courses call:
    // moodle/course:view,moodle/course:update,moodle/course:viewhiddencourses
    // Make sure that your web service account role has those.

    // Handle errors.
    if (!is_array($response) || !is_array($response[0]) || !array_key_exists('id', $response[0])) {
      // We have an error.
      if ($response[faultCode])
        $this->error = 'Moodle error: ' . $response[faultString] . ". Fault code: ".$response[faultCode]. ".";
      else
        $this->error = "Moodle returned no info. Check if course id exists and whether the web service
          account has capabilities required to execute core_course_get_courses call.";
      $this->error .= " Actual reply from server: ".$file;
      return false;
    }
    
    // This is our normal exit (returning an array of course properties).
    $course = $response[0];
    return $course;
  }
  
  // The enrollUser function tries to enroll user in a course.
  function enrollUser($user_id, $course_id) {
  	// Clear last error.
  	$this->error = null;

  	// Check whether user exists.
  	$user = $this->getUser($user_id);
  	if (!$user)
  	  return false;
 	
  	// Here, you may wish to check $user['enrolledcourses'] to see if a user is already enrolled in a course.
    
  	// Check whether course exists.
  	$course = $this->getCourse($course_id);
  	if (!$course)
  	  return false;
	  
  	// Create XML for the request. XML must be set properly for this to work.  This format was hard to figure out.
  	// I needed to debug the server code so see why method signatures did not match.
    $params = array(array(array('roleid'=>'5', 'userid'=>$user_id, 'courseid'=>$course_id))); // roleid 5 is "student".
  	$request = xmlrpc_encode_request('enrol_manual_enrol_users', $params, array('encoding'=>'UTF-8'));
    // var_dump($request);  // In case you want to see XML.
    
  	$context = stream_context_create(array('http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request
    )));
    
    $path = $this->server.$this->dir."/webservice/xmlrpc/server.php?wstoken=".$this->token;
    // Send XML to server and get a reply from it.
    $file = file_get_contents($path, false, $context); // $file is the reply from server.
    // Decode the reply.
    $response = xmlrpc_decode($file);
  	
    // enrol/manual:enrol capability is required for the web services account.
    // Also, the account must be abble to assign the "Student" role - this is configured in
    // Site administration - Users - Permissions - Define roles - Allow role assignments (make sure that the "Student" role
    // is checked for Web Services Users category (this is my custom role for web services account).
    
    if ($response && xmlrpc_is_fault($response)) {
      $this->error = "Moodle error: " . $response[faultString]." Fault code: " . $response[faultCode];
      return false;
    }
    
    // Here, you may wish to check $user['enrolledcourses'] to see if a user gor enrolled, just to be safe.
    // $user = $this->getUser($user_id);
  	    
    // This is our normal exit after a successful enrollment.
    return true;
  }
}
?>

Below are some code samples that show how to use the Moodle class. Do not forget to initialize the class first.

Code Example: Creating a New Moodle User

$fields = array(
  'username' => 'test',
  'password' => '_aA1234567_',
  'firstname' => 'Test',
  'lastname' => 'via Moodle class',
  'email' => 'test@example.com',
  'city' => 'Hope, BC',
  'country' => 'CA',
  'preferences' => array(array('type' => 'auth_forcepasswordchange', 'value' => true)), // This forces user to change password on first login.
);

$user = $moodle->createUser($fields);
if ($user)
  var_dump($user);          // Normal result contains a 2-member array with new user id and username.
else
  var_dump($moodle->error); // Error.


Code Example: Update and Existing Moodle User

$fields = array(
  'id' => '42',
  'lastname' => 'via Moodle class, Updated!',
);
$result = $moodle->updateUser($fields);
if ($result)
  var_dump($result);        // Normal result.
else
  var_dump($moodle->error); // Error.


Code Example: Set User for LDAP Authentication

You may want to set Moodle users to authenticate against an LDAP server. One would normally try the core_user_create_user API with "auth" parameter set to "ldap". What appears to be happening in this case (tried with Moodle 2.4.3) - is that Moodle tries to set a provided password on LDAP server, while your environment may not allow for it. A workaround is to create user first as above, and then update its authentication method as below.

$fields = array(
  'id' => '42',
  'auth' => 'ldap',
);
$result = $moodle->updateUser($fields);
if ($result)
  var_dump($result);        // Normal result.
else
  var_dump($moodle->error); // Error.



Code Example: Delete a Moodle User

// Delete user.
$id = 1222; // User id in Moodle.
$deleted = $moodle->deleteUser($id);
if ($deleted)
  var_dump($deleted);       // Normal result. We have deleted a user.
else
  var_dump($moodle->error); // Error.


Code Example: Get Course Information

// Get course information.
$id = 2; // Course id in Moodle.
$course = $moodle->getCourse($id);
if ($course)
  var_dump($course);        // Success, normal result.
else
  var_dump($moodle->error); // Error.


Code Example: Enroll User in a Course

// Enroll a user in a course.
$user_id = 42;
$course_id = 2;
$enrolled = $moodle->enrollUser($user_id, $course_id);
if ($enrolled)
  var_dump($enrolled);      // Success, normal result.
else
  var_dump($moodle->error); // Error.