diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..522fa4a --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. +#ECCN:Open Source diff --git a/README.md b/README.md index 948a02a..39ff9e9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ Force.com Toolkit for PHP ========================= +### Notice: + +This repo and toolkit are no longer supported. This toolkit is based on HTTP 1.0 and may have vulnerabilities. HTTP/1.0 support *likely* will be deprecated. Additionally, this toolkit is currently using v27.0 of the Salesforce API and that version will be officially deprecated as detailed in the [Salesforce Platform API Versions 21.0 through 30.0 Retirement](https://help.salesforce.com/s/articleView?id=000354473&type=1) knowledge article. + +--- The Force.com PHP Toolkit provides an easy-to-use wrapper for the Force.com Web Services SOAP API, presenting SOAP client implementations for both the enterprise and partner WSDLs. -See the [getting started guide](http://wiki.developerforce.com/index.php/Getting_Started_with_the_Force.com_Toolkit_for_PHP) for sample code to create, retrieve, update and delete records in the Force.com database. +See the [getting started guide](https://developer.salesforce.com/page/PHP_Toolkit_13.0_Getting_Started) for sample code to create, retrieve, update and delete records in the Force.com database. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0dcace9 --- /dev/null +++ b/composer.json @@ -0,0 +1,15 @@ +{ + "name": "developerforce/force.com-toolkit-for-php", + "description": "A wrapper for the Force.com Web Services SOAP API", + "license": "BSD-3-Clause", + "autoload": { + "classmap": [ + "soapclient" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/license b/license index 16a6267..5201380 100644 --- a/license +++ b/license @@ -1,3 +1,9 @@ +This project is licensed under the 'BSD 3-Clause' license [1], also known as +the 'Modified BSD' license [2]. + +[1] http://www.opensource.org/licenses/BSD-3-Clause +[2] http://www.gnu.org/licenses/license-list.html#ModifiedBSD + Copyright (c) 2010, Salesforce.com All rights reserved. diff --git a/samples/userAuth.php b/samples/userAuth.php index f38f219..1f5072e 100644 --- a/samples/userAuth.php +++ b/samples/userAuth.php @@ -1,14 +1,14 @@ +?> \ No newline at end of file diff --git a/soapclient/SforceBaseClient.php b/soapclient/SforceBaseClient.php index 16fadca..4a59196 100644 --- a/soapclient/SforceBaseClient.php +++ b/soapclient/SforceBaseClient.php @@ -29,7 +29,6 @@ require_once ('ProxySettings.php'); require_once ('SforceHeaderOptions.php'); - /** * This file contains one class. * @package SalesforceSoapClient @@ -42,7 +41,7 @@ class SforceBaseClient { protected $sforce; protected $sessionId; protected $location; - protected $version = '20.0'; + protected $version = '27.0'; protected $namespace; @@ -61,6 +60,10 @@ class SforceBaseClient { protected $localeOptions; protected $packageVersionHeader; + protected function getSoapClient($wsdl, $options) { + return new SoapClient($wsdl, $options); + } + public function getNamespace() { return $this->namespace; } @@ -90,22 +93,27 @@ public function printDebugInfo() { echo 'False'; } } - + /** * Connect method to www.salesforce.com * * @param string $wsdl Salesforce.com Partner WSDL + * @param object $proxy (optional) proxy settings with properties host, port, + * login and password + * @param array $soap_options (optional) Additional options to send to the + * SoapClient constructor. @see + * http://php.net/manual/en/soapclient.soapclient.php */ - public function createConnection($wsdl, $proxy=null) { + public function createConnection($wsdl, $proxy=null, $soap_options=array()) { $phpversion = substr(phpversion(), 0, strpos(phpversion(), '-')); - $soapClientArray = array ( + $soapClientArray = array_merge(array ( 'user_agent' => 'salesforce-toolkit-php/'.$this->version, 'encoding' => 'utf-8', 'trace' => 1, 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP - ); + ), $soap_options); // We don't need to parse out any subversion suffix - e.g. "-01" since // PHP type conversion will ignore it @@ -122,7 +130,8 @@ public function createConnection($wsdl, $proxy=null) { $soapClientArray = array_merge($soapClientArray, $proxySettings); } - $this->sforce = new SoapClient($wsdl, $soapClientArray); + $this->sforce = $this->getSoapClient($wsdl, $soapClientArray); + return $this->sforce; } @@ -169,8 +178,8 @@ public function login($username, $password) { * @return LogoutResult */ public function logout() { - $this->setHeaders("logout"); - $arg = new stdClass; + $this->setHeaders("logout"); + $arg = new stdClass(); return $this->sforce->logout(); } @@ -180,9 +189,9 @@ public function logout() { * @return invalidateSessionsResult */ public function invalidateSessions() { - $this->setHeaders("invalidateSessions"); - $arg = new stdClass; - $this->logout(); + $this->setHeaders("invalidateSessions"); + $arg = new stdClass(); + $this->logout(); return $this->sforce->invalidateSessions(); } @@ -517,6 +526,7 @@ public function sendSingleEmail($request) { $email = new SoapVar($r, SOAP_ENC_OBJECT, 'SingleEmailMessage', $this->namespace); array_push($messages, $email); } + $arg = new stdClass(); $arg->messages = $messages; return $this->_sendEmail($arg); } else { @@ -532,6 +542,7 @@ public function sendMassEmail($request) { $email = new SoapVar($r, SOAP_ENC_OBJECT, 'MassEmailMessage', $this->namespace); array_push($messages, $email); } + $arg = new stdClass(); $arg->messages = $messages; return $this->_sendEmail($arg); } else { @@ -554,7 +565,7 @@ protected function _sendEmail($arg) { */ public function convertLead($leadConverts) { $this->setHeaders("convertLead"); - $arg = new stdClass; + $arg = new stdClass(); $arg->leadConverts = $leadConverts; return $this->sforce->convertLead($arg); } @@ -567,9 +578,20 @@ public function convertLead($leadConverts) { */ public function delete($ids) { $this->setHeaders("delete"); - $arg = new stdClass; - $arg->ids = $ids; - return $this->sforce->delete($arg)->result; + if(count($ids) > 200) { + $result = array(); + $chunked_ids = array_chunk($ids, 200); + foreach($chunked_ids as $cids) { + $arg = new stdClass; + $arg->ids = $cids; + $result = array_merge($result, $this->sforce->delete($arg)->result); + } + } else { + $arg = new stdClass; + $arg->ids = $ids; + $result = $this->sforce->delete($arg)->result; + } + return $result; } /** @@ -580,7 +602,7 @@ public function delete($ids) { */ public function undelete($ids) { $this->setHeaders("undelete"); - $arg = new stdClass; + $arg = new stdClass(); $arg->ids = $ids; return $this->sforce->undelete($arg)->result; } @@ -593,7 +615,7 @@ public function undelete($ids) { */ public function emptyRecycleBin($ids) { $this->setHeaders(); - $arg = new stdClass; + $arg = new stdClass(); $arg->ids = $ids; return $this->sforce->emptyRecycleBin($arg)->result; } @@ -609,6 +631,7 @@ public function processSubmitRequest($processRequestArray) { foreach ($processRequestArray as &$process) { $process = new SoapVar($process, SOAP_ENC_OBJECT, 'ProcessSubmitRequest', $this->namespace); } + $arg = new stdClass(); $arg->actions = $processRequestArray; return $this->_process($arg); } else { @@ -628,6 +651,7 @@ public function processWorkitemRequest($processRequestArray) { foreach ($processRequestArray as &$process) { $process = new SoapVar($process, SOAP_ENC_OBJECT, 'ProcessWorkitemRequest', $this->namespace); } + $arg = new stdClass(); $arg->actions = $processRequestArray; return $this->_process($arg); } else { @@ -656,10 +680,12 @@ public function describeGlobal() { * @param string Type Object Type * @return DescribeLayoutResult */ - public function describeLayout($type) { + public function describeLayout($type, array $recordTypeIds=null) { $this->setHeaders("describeLayout"); - $arg = new stdClass; + $arg = new stdClass(); $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); + if (isset($recordTypeIds) && count($recordTypeIds)) + $arg->recordTypeIds = $recordTypeIds; return $this->sforce->describeLayout($arg)->result; } @@ -672,7 +698,7 @@ public function describeLayout($type) { */ public function describeSObject($type) { $this->setHeaders("describeSObject"); - $arg = new stdClass; + $arg = new stdClass(); $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); return $this->sforce->describeSObject($arg)->result; } @@ -710,7 +736,7 @@ public function describeTabs() { */ public function describeDataCategoryGroups($sObjectType) { $this->setHeaders('describeDataCategoryGroups'); - $arg = new stdClass; + $arg = new stdClass(); $arg->sObjectType = new SoapVar($sObjectType, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); return $this->sforce->describeDataCategoryGroups($arg)->result; } @@ -724,7 +750,7 @@ public function describeDataCategoryGroups($sObjectType) { */ public function describeDataCategoryGroupStructures(array $pairs, $topCategoriesOnly) { $this->setHeaders('describeDataCategoryGroupStructures'); - $arg = new stdClass; + $arg = new stdClass(); $arg->pairs = $pairs; $arg->topCategoriesOnly = new SoapVar($topCategoriesOnly, XSD_BOOLEAN, 'boolean', 'http://www.w3.org/2001/XMLSchema'); @@ -742,7 +768,7 @@ public function describeDataCategoryGroupStructures(array $pairs, $topCategories */ public function getDeleted($type, $startDate, $endDate) { $this->setHeaders("getDeleted"); - $arg = new stdClass; + $arg = new stdClass(); $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); $arg->startDate = $startDate; $arg->endDate = $endDate; @@ -760,7 +786,7 @@ public function getDeleted($type, $startDate, $endDate) { */ public function getUpdated($type, $startDate, $endDate) { $this->setHeaders("getUpdated"); - $arg = new stdClass; + $arg = new stdClass(); $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); $arg->startDate = $startDate; $arg->endDate = $endDate; @@ -777,10 +803,12 @@ public function getUpdated($type, $startDate, $endDate) { */ public function query($query) { $this->setHeaders("query"); - $QueryResult = $this->sforce->query(array ( + $raw = $this->sforce->query(array ( 'queryString' => $query ))->result; - return new QueryResult($QueryResult); + $QueryResult = new QueryResult($raw); + $QueryResult->setSf($this); // Dependency Injection + return $QueryResult; } /** @@ -792,10 +820,12 @@ public function query($query) { */ public function queryMore($queryLocator) { $this->setHeaders("queryMore"); - $arg = new stdClass; + $arg = new stdClass(); $arg->queryLocator = $queryLocator; - $QueryResult = $this->sforce->queryMore($arg)->result; - return new QueryResult($QueryResult); + $raw = $this->sforce->queryMore($arg)->result; + $QueryResult = new QueryResult($raw); + $QueryResult->setSf($this); // Dependency Injection + return $QueryResult; } /** @@ -807,10 +837,12 @@ public function queryMore($queryLocator) { */ public function queryAll($query, $queryOptions = NULL) { $this->setHeaders("queryAll"); - $QueryResult = $this->sforce->queryAll(array ( + $raw = $this->sforce->queryAll(array ( 'queryString' => $query ))->result; - return new QueryResult($QueryResult); + $QueryResult = new QueryResult($raw); + $QueryResult->setSf($this); // Dependency Injection + return $QueryResult; } @@ -824,7 +856,7 @@ public function queryAll($query, $queryOptions = NULL) { */ public function retrieve($fieldList, $sObjectType, $ids) { $this->setHeaders("retrieve"); - $arg = new stdClass; + $arg = new stdClass(); $arg->fieldList = $fieldList; $arg->sObjectType = new SoapVar($sObjectType, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); $arg->ids = $ids; @@ -839,7 +871,7 @@ public function retrieve($fieldList, $sObjectType, $ids) { */ public function search($searchString) { $this->setHeaders("search"); - $arg = new stdClass; + $arg = new stdClass(); $arg->searchString = new SoapVar($searchString, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); return new SforceSearchResult($this->sforce->search($arg)->result); } @@ -867,7 +899,7 @@ public function getUserInfo() { */ public function setPassword($userId, $password) { $this->setHeaders("setPassword"); - $arg = new stdClass; + $arg = new stdClass(); $arg->userId = new SoapVar($userId, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); $arg->password = $password; return $this->sforce->setPassword($arg); @@ -881,7 +913,7 @@ public function setPassword($userId, $password) { */ public function resetPassword($userId) { $this->setHeaders("resetPassword"); - $arg = new stdClass; + $arg = new stdClass(); $arg->userId = new SoapVar($userId, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); return $this->sforce->resetPassword($arg)->result; } @@ -911,16 +943,22 @@ public function __construct($response) { } } -class QueryResult { +class QueryResult implements Iterator{ public $queryLocator; public $done; public $records; public $size; + public $pointer; // Current iterator location + private $sf; // SOAP Client + public function __construct($response) { $this->queryLocator = $response->queryLocator; $this->done = $response->done; $this->size = $response->size; + + $this->pointer = 0; + $this->sf = false; if($response instanceof QueryResult) { $this->records = $response->records; @@ -929,16 +967,40 @@ public function __construct($response) { if (isset($response->records)) { if (is_array($response->records)) { foreach ($response->records as $record) { - $sobject = new SObject($record); - array_push($this->records, $sobject); + array_push($this->records, $record); }; } else { - $sobject = new SObject($response->records); - array_push($this->records, $sobject); + array_push($this->records, $record); } } } } + + public function setSf(SforceBaseClient $sf) { $this->sf = $sf; } // Dependency Injection + + // Basic Iterator implementation functions + public function rewind() { $this->pointer = 0; } + public function next() { ++$this->pointer; } + public function key() { return $this->pointer; } + public function current() { return new SObject($this->records[$this->pointer]); } + + public function valid() { + while ($this->pointer >= count($this->records)) { + // Pointer is larger than (current) result set; see if we can fetch more + if ($this->done === false) { + if ($this->sf === false) throw new Exception("Dependency not met!"); + $response = $this->sf->queryMore($this->queryLocator); + $this->records = array_merge($this->records, $response->records); // Append more results + $this->done = $response->done; + $this->queryLocator = $response->queryLocator; + } else { + return false; // No more records to fetch + } + } + if (isset($this->records[$this->pointer])) return true; + + throw new Exception("QueryResult has gaps in the record data?"); + } } class SObject { @@ -952,7 +1014,7 @@ public function __construct($response=NULL) { } foreach ($response as $responseKey => $responseValue) { - if (in_array($responseKey, array('Id', 'type', 'any'))) { + if (in_array(strval($responseKey), array('Id', 'type', 'any'))) { continue; } $this->$responseKey = $responseValue; @@ -1068,6 +1130,9 @@ public function __construct($response=NULL) { } } } + + function __get($name) { return (isset($this->fields->$name))? $this->fields->$name : false; } + function __isset($name) { return isset($this->fields->$name); } /** * Parse the "any" string from an sObject. First strip out the sf: and then @@ -1079,7 +1144,7 @@ function convertFields($any) { $array = $this->xml2array(''.$str.'', 2); - $xml = new stdClass; + $xml = new stdClass(); if (!count($array['Object'])) return $xml; @@ -1152,9 +1217,9 @@ function xml2array($contents, $get_attributes=1) { $result = $value; } - //Set the attributes too. - if(isset($attributes) && isset($attributes['xsi:nil'])) { - $result = $attributes['xsi:nil']; + //Check for nil and ignore other attributes. + if (isset($attributes) && isset($attributes['xsi:nil']) && !strcasecmp($attributes['xsi:nil'], 'true')) { + $result = null; } break; } diff --git a/soapclient/SforceEmail.php b/soapclient/SforceEmail.php index 1a7f876..b0b37a4 100644 --- a/soapclient/SforceEmail.php +++ b/soapclient/SforceEmail.php @@ -81,6 +81,10 @@ public function setHtmlBody($htmlBody) { $this->htmlBody = $htmlBody; } + public function setOrgWideEmailAddressId($orgWideEmailAddressId) { + $this->orgWideEmailAddressId = $orgWideEmailAddressId; + } + public function setPlainTextBody($plainTextBody) { $this->plainTextBody = $plainTextBody; } diff --git a/soapclient/SforceEnterpriseClient.php b/soapclient/SforceEnterpriseClient.php index 8b269ad..b018efc 100644 --- a/soapclient/SforceEnterpriseClient.php +++ b/soapclient/SforceEnterpriseClient.php @@ -25,7 +25,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ require_once ('SforceBaseClient.php'); - /** * This file contains two classes. * @package SalesforceSoapClient @@ -37,11 +36,9 @@ */ class SforceEnterpriseClient extends SforceBaseClient { const ENTERPRISE_NAMESPACE = 'urn:enterprise.soap.sforce.com'; - - function SforceEnterpriseClient() { + public function __construct() { $this->namespace = self::ENTERPRISE_NAMESPACE; } - /** * Adds one or more new individual objects to your organization's data. * @param array $sObjects Array of one or more sObjects (up to 200) to create. @@ -50,14 +47,27 @@ function SforceEnterpriseClient() { * @return SaveResult */ public function create($sObjects, $type) { - foreach ($sObjects as &$sobject) { - $sobject = new SoapVar($sobject, SOAP_ENC_OBJECT, $type, $this->namespace); + $arg = []; + foreach ($sObjects as $sObject) { + // FIX for fieldsToNull issue - allow array in fieldsToNull (STEP #1) + $xmlStr = ''; + if(isset($sObject->fieldsToNull) && is_array($sObject->fieldsToNull)) { + foreach($sObject->fieldsToNull as $fieldToNull) { + $xmlStr .= '' . $fieldToNull . ''; + } + } + // ------ + + $soapObject = new SoapVar($sObject, SOAP_ENC_OBJECT, $type, $this->namespace); + // FIX for fieldsToNull issue - allow array in fieldsToNull (STEP #2) + if($xmlStr != '') { + $soapObject->enc_value->fieldsToNull = new SoapVar(new SoapVar($xmlStr, XSD_ANYXML), SOAP_ENC_ARRAY); + } + // ------ + $arg[] = $soapObject; } - $arg = $sObjects; - return parent::_create(new SoapParam($arg, "sObjects")); } - /** * Updates one or more new individual objects to your organization's data. * @param array sObjects Array of sObjects @@ -66,9 +76,9 @@ public function create($sObjects, $type) { * @return UpdateResult */ public function update($sObjects, $type, $assignment_header = NULL, $mru_header = NULL) { - - foreach ($sObjects as &$sObject) { - + $arg = new stdClass; + $arg->sObjects = []; + foreach ($sObjects as $sObject) { // FIX for fieldsToNull issue - allow array in fieldsToNull (STEP #1) $xmlStr = ''; if(isset($sObject->fieldsToNull) && is_array($sObject->fieldsToNull)) { @@ -78,18 +88,18 @@ public function update($sObjects, $type, $assignment_header = NULL, $mru_header } // ------ - $sObject = new SoapVar($sObject, SOAP_ENC_OBJECT, $type, $this->namespace); + $soapObject = new SoapVar($sObject, SOAP_ENC_OBJECT, $type, $this->namespace); // FIX for fieldsToNull issue - allow array in fieldsToNull (STEP #2) if($xmlStr != '') { - $sObject->enc_value->fieldsToNull = new SoapVar(new SoapVar($xmlStr, XSD_ANYXML), SOAP_ENC_ARRAY); + $soapObject->enc_value->fieldsToNull = new SoapVar(new SoapVar($xmlStr, XSD_ANYXML), SOAP_ENC_ARRAY); } // ------ + $arg->sObjects[] = $soapObject; } - $arg->sObjects = $sObjects; + return parent::_update($arg); } - /** * Creates new objects and updates existing objects; uses a custom field to * determine the presence of existing objects. In most cases, we recommend @@ -103,8 +113,9 @@ public function update($sObjects, $type, $assignment_header = NULL, $mru_header */ public function upsert($ext_Id, $sObjects, $type = 'Contact') { $arg = new stdClass; + $arg->sObjects = []; $arg->externalIDFieldName = new SoapVar($ext_Id, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema'); - foreach ($sObjects as &$sObject) { + foreach ($sObjects as $sObject) { // FIX for fieldsToNull issue - allow array in fieldsToNull (STEP #1) $xmlStr = ''; if(isset($sObject->fieldsToNull) && is_array($sObject->fieldsToNull)) { @@ -114,18 +125,17 @@ public function upsert($ext_Id, $sObjects, $type = 'Contact') { } // ------ - $sObject = new SoapVar($sObject, SOAP_ENC_OBJECT, $type, $this->namespace); - + $soapObject = new SoapVar($sObject, SOAP_ENC_OBJECT, $type, $this->namespace); // FIX for fieldsToNull issue - allow array in fieldsToNull (STEP #2) if($xmlStr != '') { - $sObject->enc_value->fieldsToNull = new SoapVar(new SoapVar($xmlStr, XSD_ANYXML), SOAP_ENC_ARRAY); + $soapObject->enc_value->fieldsToNull = new SoapVar(new SoapVar($xmlStr, XSD_ANYXML), SOAP_ENC_ARRAY); } // ------ + $arg->sObjects[] = $soapObject; } - $arg->sObjects = $sObjects; + return parent::_upsert($arg); } - /** * Merge records * @@ -135,6 +145,7 @@ public function upsert($ext_Id, $sObjects, $type = 'Contact') { */ public function merge($mergeRequest, $type) { $mergeRequest->masterRecord = new SoapVar($mergeRequest->masterRecord, SOAP_ENC_OBJECT, $type, $this->namespace); + $arg = new stdClass; $arg->request = new SoapVar($mergeRequest, SOAP_ENC_OBJECT, 'MergeRequest', $this->namespace); return parent::_merge($arg); } diff --git a/soapclient/SforceMetaObject.php b/soapclient/SforceMetaObject.php index a1c3baf..5c045af 100644 --- a/soapclient/SforceMetaObject.php +++ b/soapclient/SforceMetaObject.php @@ -79,6 +79,10 @@ public function setPluralLabel($pluralLabel) { $this->pluralLabel = $pluralLabel; } + public function setSharingModel($sharingModel) { + $this->sharingModel = $sharingModel; + } + public function setStartsWith($startsWith) { $this->startsWith = $startsWith; } diff --git a/soapclient/SforceMetadataClient.php b/soapclient/SforceMetadataClient.php index 24806f6..7477d72 100644 --- a/soapclient/SforceMetadataClient.php +++ b/soapclient/SforceMetadataClient.php @@ -30,7 +30,7 @@ class SforceMetadataClient { public $sforce; protected $sessionId; protected $location; - protected $version = '20.0'; + protected $version = '27.0'; protected $namespace = 'http://soap.sforce.com/2006/04/metadata'; @@ -116,15 +116,34 @@ public function setSessionHeader($sessionId) { $this->_setClientId($header_array); $this->sforce->__setSoapHeaders($header_array); } + + private function getObjtype($obj) { + $classArray = explode('\\', get_class($obj)); + $objtype = array_pop($classArray); + if (strpos($objtype, 'Sforce', 0) === 0) { + $objtype = substr($objtype, 6); + } + return $objtype; + } public function create($obj) { - $encodedObj->metadata = new SoapVar($obj, SOAP_ENC_OBJECT, 'CustomObject', $this->namespace); + $encodedObj = new stdClass(); + $encodedObj->metadata = new SoapVar($obj, SOAP_ENC_OBJECT, $this->getObjtype($obj), $this->namespace); return $this->sforce->create($encodedObj); } + public function update($obj) { + $encodedObj = new stdClass(); + $encodedObj->UpdateMetadata = $obj; + $encodedObj->UpdateMetadata->metadata = new SoapVar($obj->metadata, SOAP_ENC_OBJECT, $this->getObjtype($obj->metadata), $this->namespace); + + return $this->sforce->update($encodedObj); + } + public function delete($obj) { - $encodedObj->metadata = new SoapVar($obj, SOAP_ENC_OBJECT, 'CustomObject', $this->namespace); + $encodedObj = new stdClass(); + $encodedObj->metadata = new SoapVar($obj, SOAP_ENC_OBJECT, $this->getObjtype($obj), $this->namespace); return $this->sforce->delete($encodedObj); } diff --git a/soapclient/SforcePartnerClient.php b/soapclient/SforcePartnerClient.php index 5a818ad..38c30e8 100644 --- a/soapclient/SforcePartnerClient.php +++ b/soapclient/SforcePartnerClient.php @@ -32,6 +32,40 @@ * This file contains two classes. * @package SalesforceSoapClient */ +/** + * SforceSoapClient class. + * + * @package SalesforceSoapClient + */ + // When parsing partner WSDL, when PHP SOAP sees NewValue and OldValue, since + // the element has a xsi:type attribute with value 'string', it drops the + // string content into the parsed output and loses the tag name. Removing the + // xsi:type forces PHP SOAP to just leave the tags intact + class SforceSoapClient extends SoapClient { + function __doRequest($request, $location, $action, $version, $one_way=0) { + $response = parent::__doRequest($request, $location, $action, $version, $one_way); + + // Quick check to only parse the XML here if we think we need to + if (strpos($response, 'loadXML($response); + + $nodeList = $dom->getElementsByTagName('NewValue'); + foreach ($nodeList as $node) { + $node->removeAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'type'); + } + $nodeList = $dom->getElementsByTagName('OldValue'); + foreach ($nodeList as $node) { + $node->removeAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'type'); + } + + return $dom->saveXML(); + } + } + /** * SforcePartnerClient class. * @@ -40,9 +74,14 @@ class SforcePartnerClient extends SforceBaseClient { const PARTNER_NAMESPACE = 'urn:partner.soap.sforce.com'; - function SforcePartnerClient() { + public function __construct() { $this->namespace = self::PARTNER_NAMESPACE; } + + protected function getSoapClient($wsdl, $options) { + // Workaround an issue in parsing OldValue and NewValue in histories + return new SforceSoapClient($wsdl, $options); + } /** * Adds one or more new individual objects to your organization's data. @@ -184,4 +223,4 @@ private function _retrieveResult($response) { return $arr; } -} \ No newline at end of file +} diff --git a/soapclient/enterprise.wsdl.xml b/soapclient/enterprise.wsdl.xml index 423bda2..06b1c74 100644 --- a/soapclient/enterprise.wsdl.xml +++ b/soapclient/enterprise.wsdl.xml @@ -1,6 +1,6 @@ @@ -3331,7 +3331,7 @@ Copyright 2006-2010 Salesforce.com, inc. All Rights Reserved Manage your Salesforce.com metadata - + \ No newline at end of file diff --git a/soapclient/partner.wsdl.xml b/soapclient/partner.wsdl.xml index 8d75c97..fa3f984 100644 --- a/soapclient/partner.wsdl.xml +++ b/soapclient/partner.wsdl.xml @@ -1,6 +1,6 @@