The CreatorCon Call for Content is officially open! Get started here.

SOAP PHP Implementation

sherman_1206
Tera Contributor

So after a lot of SOAP trial and error and a lack of examples or any documentation on using Service now soap web service with PHP I decided to implement a php class for using Service Now SOAP Web Service. The implementation is thus far only as robust as I have needed it to be but feel free to comment or add to it... I think this could be a good thing going forward.

Currently supported functionality:
-construct new ServiceNowSoapClient

-setLocation($location) - a way to change the web service link you are using if you wish to query some other cmdb_ci table

-listAvailableFunctions - a fun extra function to dump a list of functions available at the respective web service link

- getRecords($query) - accepts an array to query the web service for, this can be one of two things:
- encoded query of the form array("__encoded_query" => "");
- regular query of form array("param1" => "value1", "param2" => "value2", ... )

get($sys_id) - accepts an id of a service now record at the respective instance and returns this object

Next implementation:

-getKeys function - although I have yet to need this one although I suppose you could call getKeys to get a list of system ids and then do something like foreach($keys as $key){ // call SoapServiceNowClient->get($key) }
that way you could call getKeys to delimit your result set and then use get to get each record individually although I think getRecords would be easier for this purpose

-insert

-update

-delete

Although as far as those go, thus far the only application we have for SOAP is to produce text view of service now information, therefore, I will get to these when I do but feel free to contact me if you want to discuss these and I can help you implement them as I do not see them as being difficult at all.

Now for the best part the code!



<?php
/**
*Class: SeviceNowSoapClient
*Description: a wrapper class for php SoapClient to ease the usage of service
*now soap web services
*Date: 06.25.2010
*Author: Chris Sherman
*License: none, use it as you wish but do give credit to those who help 😉
*/
class ServiceNowSoapClient{
private $client, $instance, $login, $password;

/**
*location must be to a WSDL service location within service-now.com
*ie. https://demo.service-now.com/cmdb_ci_service.do?WSDL
*login and password must be a service-now.com user with SOAP access added to its account
*/
public function __construct($location, $login, $password){
$this->setLogin($login);
$this->setPassword($password);
$this->location = $location;

$this->client = new soapclient($this->location,$this->getAuthorization());

}

/**
*Function: getRecords($query)
*Purpose: Implements the ServiceNow Soap GetRecords method. The idea is that
*we accept an array containing the query and always return back an array.
*This method is what makes this class so great. Service Now Server
*can return to to soap client multiple types,
*-either an array of record objects when multiple
*objects are in the result set
*-a record object when one item is in result set
*-a record object which is empty when no results are returned
*Instead of handling that mess with all soap queries this function
*handles it for you by determining if the result is an array, or
*or not and it will always return an array. The array can be empty,
*for no results, or have some number of recorc objects in it. 🙂
*param: array, the query for soap web service
*return: array, array containing record objects or an empty array for no results
*/
public function getRecords($query){
$result = $this->client->__soapCall('getRecords',array('GetRecords' => $query));
$returnResult = array();

if(isset($result)){
if(isset($result->getRecordsResult) and is_array($result->getRecordsResult)){
$returnResult = $result->getRecordsResult;
}else{
$returnResult = array($result->getRecordsResult);
}
}

return $returnResult;
}

/**
*Function: set login
*Purpose: Helper function you will not need as of now.
*sets $this->login = $login
*param: string, the login for this service now soap web service
*return: void
*/
public function setLogin($login){
$this->login = $login;
}
/**
*Function: setPassword($password)
*Purpose: Helper function you will not need as of now.
*sets $this->password = $password
*param: string, the password for this soap web service
*return: void
*/
public function setPassword($password){
$this->password = $password;
}

//sets a new end point for soap client
//SoapClient::__setLocation throws error on the call and
//for time being I will just construct a new soap client
/**
*Function: setLocation($location)
*Purpose: Updates the location to the WSDL SOAP Service
*Now web service
*As of now method constructs a new SoapClient with updated
*location as I was having problems with using SoapClient->__setLocation
*I attribute this to the WSDL but thats for another day.
*param: string, the location of the new web service
*return: void
*/
public function setLocation($location){
//$this->client->__setLocation($location);
$this->location = $location;
$this->client = new soapclient($this->location,$this->getAuthorization());
}
/**
*Function: getAuthorization()
*Purpose: Helper function you will not need as of now.
*Used when constructor constructs a new soap client or
*when set location is called. Creates the array to authenticate
*with the web service.
*param: null
*return: associative array, this.array('login' -> $this->login, 'password'->$this->password')
*/
public function getAuthorization(){
return array('login'=> $this->login, 'password' => $this->password);
}

//auxilary function to merely dump the list of available functions
//specific this.client.currentLocation
public function listAvailableFunctions(){
var_dump($this->client);
print_r($this->client->__getFunctions());
}

/**
*Function: getRecords($query)
*Purpose: Implements the ServiceNow Soap GetRecords method. The idea is that
*we accept an array containing the query and always return back an array.
*This method is what makes this class so great. Service Now Server
*can return to to soap client multiple types,
*-either an array of record objects when multiple
*objects are in the result set
*-a record object when one item is in result set
*-a record object which is empty when no results are returned
*Instead of handling that mess with all soap queries this function
*handles it for you by determining if the result is an array, or
*or not and it will always return an array. The array can be empty,
*for no results, or have some number of recorc objects in it. 🙂
*param: array, the query for soap web service
*return: array, array containing record objects or an empty array for no results
*/
public function getRecords($query){
$result = $this->client->__soapCall('getRecords',array('GetRecords' => $query));
$returnResult = array();

if(isset($result)){
if(isset($result->getRecordsResult) and is_array($result->getRecordsResult)){
$returnResult = $result->getRecordsResult;
}else{
$returnResult = array($result->getRecordsResult);
}
}

return $returnResult;
}
/**
*Fucntion: get($sys_id)
*@todo: finish implementation of get call
*/
public function get($sys_id){
echo $sys_id;
$result = $this->client->__soapCall('get',array('sys_id' => $sys_id));
var_dump($result);
}

/*
public function buildEncodedQuery(){

}
*/
}

?>

12 REPLIES 12

sherman_1206
Tera Contributor

I have reworked the soap library one last time, now using an interface to define the SoapClient and defining a parent class Client which provides the soap interactions. Thus the end result is you can compose a class which extends Client and therefore inherits all the soap methods. Which is nice because WSDL's all provide the same functions but require a different WSDL location. See class Incident for how to extend and use this at the bottom.


Also note: processEmptyValues simply replaces "" the empty string with "NULL" which by service now soap server requires this. Basically if you pass "" as a value for a field in a soap request it will ignore this value, but if you send "NULL" as a value for a field service now will process this and replace the field value with the empty string. (Not quite sure how you could send the string "NULL" itself but I doubt many soap queries will require inserting the explicit string "NULL". 🙂



interface ServiceNowSoapClient{

/**
*function insert($query)
*inserts a record into service now specific to the given WSDL.
*Function does not assume you have provided the required fields
*this should be done elsewhere.
*
*@require the query array at least includes required fields specific
*to the WSDL.
*@param string end point WSDL
*@param associative_array field -> value pairs
*@return boolean true on success, false for failures
*
*/
public function insert($query);

/**
*Function: get($sys_id)
*Gets a specific record by its system id. Specific to a given
*WSDL.
*
*@param string the end point WSDL such as incident.do?WSDL
*@param: string the system id
*@return a record specific to provided WSDL
*/
public function get($id);

/**
*Implements the ServiceNow Soap GetRecords method. The idea is that
*we accept an array containing the query and always return back an array.
*This method is what makes this class so great. Service Now Server
*can return to to soap client multiple types,
*-either an array of record objects when multiple
*objects are in the result set
*-a record object when one item is in result set
*-a record object which is empty when no results are returned
*Instead of handling that mess with all soap queries this function
*handles it for you by determining if the result is an array, or
*or not and it will always return an array. The array can be empty,
*for no results, or have some number of recorc objects in it. 🙂
*param: array, the query for soap web service
*return: array, array containing record objects or an empty array for no results
*/
public function getRecords($query=array());

/**
*Updates a specific record in service now, the record is sepcific
* to provided WSDL location
*
*@param array - associative array of field -> value pairs
*@requires adhering to WSDL of specific location with at least
*the min requirements.
*/
public function update($query);
public function deleteRecord($id);

/**
*for any query which is an insert or update we want to replace "" (empty strings)
*with the string "NULL" per requirement of service now soap client.
*@param array $query an associative array of key/value pairs for soap insert/update
*@return array $query an associative array of key/value pairs where for any key
*pointing to a value = "" (empty string) is now replaced with value = "NULL"
*
*/
function processEmptyValues($query);
}

/**
*Client implements the ServiceNowSoapClient interface.
*@see ServiceNowSoapClient interface for documentation on
*methods in this class which implement those of that interface.
*@author Chris Sherman
*@date 08.06.2010
*
*/
class Client implements ServiceNowSoapClient{
public $WSDL = 'https://osuitsmtest.service-now.com/';
const LOGIN='USER';
const PASSWORD='PASS';

function __construct($WSDL) {
$this->WSDL .= $WSDL;
}
/**
*Used when soap client or needs to perform interaction.
*Creates the array to authenticate with the web service.
*
*@return associative_array, this.array('login' -> $this->login,
*'password'->$this->password')
*/
public static function getAuthorization(){
return array('login'=> self::LOGIN, 'password' => self::PASSWORD);
}

//auxilary function to merely dump the list of available functions
//specific this.client.currentLocation
public static function listAvailableFunctions($WSDL){
try{
$client = new soapclient(self::$WSDL,self::getAuthorization());
$result = $client->__getFunctions();
foreach($result as $key=>$value){
echo $key.' -> '.$value.'
';
}
}catch(SoapFault $exception){
echo $exception;
}

}


public function getRecords($query=array()){
$returnResult = array();
try{
$client = new soapclient($this->WSDL,self::getAuthorization());
$result = $client->__soapCall('getRecords',array('GetRecords' => $query));

if(isset($result)){
if(isset($result->getRecordsResult)){
if(is_array($result->getRecordsResult)){
$returnResult = $result->getRecordsResult;
}else if(isset($result->getRecordsResult->name)){
$returnResult = array($result->getRecordsResult);
}
}
}//end if isset
return $returnResult;
}catch(SoapFault $exception){
//echo $exception;
return $returnResult;
}

}
/**
*processEmptyValuse accepts insert/update calls argument and replaces
*string("") with string("NULL") because the soap server functinos in this way
*
*/
function processEmptyValues($query){
foreach($query as $field=>$value){
if(empty($value)){
$query[$field] = "NULL";
}
}
return $query;
}

public function insert($query){
try{
$query = $this->processEmptyValues($query);
$client = new soapclient($this->WSDL,self::getAuthorization());
$result = $client->__soapCall('insert',array('insert' => $query));
return true;
}catch(SoapFault $exception){
echo $exception;
//return false;
}

}

public function update($query){
try{
$query = $this->processEmptyValues($query);
$client = new soapclient($this->WSDL,self::getAuthorization());
$result = $client->__soapCall('update',array('update' => $query));
return true;
}catch(SoapFault $exception){
//echo $exception
return false;
}
}


public function get($id){
try{
$client = new soapclient($this->WSDL,self::getAuthorization());
$result = $client->__soapCall('get', array('get'=> array('sys_id'=>$id)));
return $result;
}catch(SoapFault $exception){
//echo $exception;
return false;
}

}//end get()


public function deleteRecord($id){
try{
$client = new soapclient($this->WSDL,self::getAuthorization());
$result = $client->__soapCall('deleteRecord', array('deleteRecord'=> array('sys_id'=>$id)));
return $result;
}catch(SoapFault $exception){
//echo $exception;
return false;
}
}//end deleteRecord

/**
*
*
*
*/
public static function isAllowed($required, $provided){
$total = count($required);
$count = 0;
foreach($provided as $key=>$value){
if(in_array($key,$required)){
$count++;
}
}
return ($count == $total);
}
}

class Incident extends Client{

public function __construct(){
parent::__construct('incident.do?displayvalue=all&WSDL');
}

public function getByID($id){
return parent::get($id);
}//end getByID
}

$i = new Incident();
$i->insert($query=<some query>); //can call Client (incident's parent) methods
$i->getByID("sdflksdgw4g34g34g"); //can call Incident methods



shahid971
Kilo Explorer

Thanks...
I got the solution...


sherman_1206
Tera Contributor

Greetings everyone -

The code posted here was more for example purposes than production use. However, from this we have implemented some PHP classes that are easily extendable for interacting with servicenow using SOAP in PHP.

Additionally, the code now lives on github

There is a PHP library, a python library, some basic soap examples and much more to come.

For those who just want the class and cannot wait: https://github.com/notyourwork/snsoapclient/blob/master/phpsoapclient/Class.SoapClient.php

Your best bet would be something like:



class IncidentClient extends SNSoapClient{
//put your incident goodness right here.
}

$options = array(
'instance' => '<your sn instance>',
'tableName' => 'incident',
'login' => 'soap account login',
'password' => 'soap account password'
);

$i = new IncidentClient($options);
$incident_sys_id = '123....456';
$result = $i->get( $incident_sys_id );

var_dump( $result );


Feel free to fork the repository, or ask questions if you need help!