I tweeted last week that, in the course of answering a discussion board post, I’d fixed a couple of minor issues in the Force.com Toolkit for PHP that had made it impossible to use the Metadata API to add custom fields to an object from PHP. Although my fix worked, I noticed that only the CustomObject and CustomField metadata types are supported by the PHP toolkit; it’s not possible to, for example, set the Login IP Range on a Profile from PHP. John Casimiro suggested taking a look at the WSO2 Web Services Framework for PHP (aka WSF/PHP) wsdl2php tool to generate PHP classes from the Metadata API WSDL.

Generating the classes was very straightforward – I downloaded and extracted WSF/PHP, generated and downloaded the current Metadata API WSDL from my org, and ran the wsdl2php script:

php wsdl2php.php > metadata.php

This gave me PHP classes corresponding to the types in the WSDL, plus some sample code showing how I could use those types with WSF/PHP’s WSClient. Since I wanted to use the existing PHP toolkit to manage the connection with the server, I just trimmed off the sample code (everything from the $class_map on) and added a PHP namespace at the top. Here is the resulting metadata.php.

With a couple more tweaks to the PHP toolkit, I was able to create, update and delete any metadata types from PHP. Here is my script to set Login IP Ranges on a Profile:

// Change this to point to wherever you've put the soapclient directory
$soapclient = '../Force.com-Toolkit-for-PHP/soapclient';

require_once ($soapclient.'/SforcePartnerClient.php');
require_once ($soapclient.'/SforceMetadataClient.php');

// metadata.php contains the classes generated from the Metadata API WSDL
require_once ('./metadata.php');

// I use environment variables for credentials - this is good practice
// You can just put your username/password instead if you like
define("SF_USERNAME", getenv("USERNAME"));
define("SF_PASSWORD", getenv("PASSWORD"));

define("SF_PARTNER_WSDL", $soapclient.'/partner.wsdl.xml');
define("SF_METADATA_WSDL", $soapclient.'/metadata.wsdl.xml');

// "" or "namespace__"

// Get the initial connection from the Partner API
$mySforceConnection = new SforcePartnerClient();
$mySoapClient = $mySforceConnection->createConnection(SF_PARTNER_WSDL);
$myLoginResult = $mySforceConnection->login(SF_USERNAME, 

// Now create a metadata client using the partner session
$myMetadataConnection = new SforceMetadataClient(SF_METADATA_WSDL, 
  $myLoginResult, $mySforceConnection);

// Setting login IP ranges is really easy...
$profileLoginIpRange = new metadata\ProfileLoginIpRange();

$profileLoginIpRange->startAddress = '';
$profileLoginIpRange->endAddress = '';

$profile = new metadata\Profile();
$profile->loginIpRanges = array($profileLoginIpRange);

$updateMetadata = new metadata\UpdateMetadata();
$updateMetadata->metadata = $profile;
$updateMetadata->currentName = 'Standard';

$result = $myMetadataConnection->update($updateMetadata);

// It's an asynchronous operation, so we'll have to periodically
// check the status
echo "Updating profile - result:\n";

$sleep = 1;
while (!$result->result->done) {
  $sleep *= 2;

  $result = $myMetadataConnection->checkStatus(array($result->result->id));  
  echo "Checked status - result:\n";

if ($result->result->state == 'Error') {
  die("Error updating profile: ".$result->result->statusCode.
    " ".$result->result->message."\n");

echo "Profile updated successfully\n";

I haven’t decided yet whether to include metadata.php in the toolkit itself, just document the process to create it from the current Metadata WSDL, or both. I’d be interested in feedback from the community on this, so please leave your comments!

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS