PageRenderTime 61ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/soapclient/SforceBaseClient.php

https://github.com/AndrewEastwood/Force.com-Toolkit-for-PHP
PHP | 1270 lines | 811 code | 143 blank | 316 comment | 171 complexity | 17e8dbd8b65f7279dfad1cb7dbb3a705 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /*
  3. * Copyright (c) 2007, salesforce.com, inc.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification, are permitted provided
  7. * that the following conditions are met:
  8. *
  9. * Redistributions of source code must retain the above copyright notice, this list of conditions and the
  10. * following disclaimer.
  11. *
  12. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
  13. * the following disclaimer in the documentation and/or other materials provided with the distribution.
  14. *
  15. * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
  16. * promote products derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
  19. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  20. * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  21. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  22. * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  24. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  25. * POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. require_once ('SforceEmail.php');
  28. require_once ('SforceProcessRequest.php');
  29. require_once ('ProxySettings.php');
  30. require_once ('SforceHeaderOptions.php');
  31. /**
  32. * This file contains one class.
  33. * @package SalesforceSoapClient
  34. */
  35. /**
  36. * SalesforceSoapClient
  37. * @package SalesforceSoapClient
  38. */
  39. class SforceBaseClient {
  40. protected $sforce;
  41. protected $sessionId;
  42. protected $location;
  43. protected $version = '20.0';
  44. protected $namespace;
  45. // Header Options
  46. protected $callOptions;
  47. protected $assignmentRuleHeader;
  48. protected $emailHeader;
  49. protected $loginScopeHeader;
  50. protected $mruHeader;
  51. protected $queryHeader;
  52. protected $userTerritoryDeleteHeader;
  53. protected $sessionHeader;
  54. // new headers
  55. protected $allowFieldTruncationHeader;
  56. protected $localeOptions;
  57. protected $packageVersionHeader;
  58. protected function getSoapClient($wsdl, $options) {
  59. return new SoapClient($wsdl, $options);
  60. }
  61. public function getNamespace() {
  62. return $this->namespace;
  63. }
  64. // clientId specifies which application or toolkit is accessing the
  65. // salesforce.com API. For applications that are certified salesforce.com
  66. // solutions, replace this with the value provided by salesforce.com.
  67. // Otherwise, leave this value as 'phpClient/1.0'.
  68. protected $client_id;
  69. public function printDebugInfo() {
  70. echo "PHP Toolkit Version: $this->version\r\n";
  71. echo 'Current PHP version: ' . phpversion();
  72. echo "\r\n";
  73. echo 'SOAP enabled: ';
  74. if (extension_loaded('soap')) {
  75. echo 'True';
  76. } else {
  77. echo 'False';
  78. }
  79. echo "\r\n";
  80. echo 'OpenSSL enabled: ';
  81. if (extension_loaded('openssl')) {
  82. echo 'True';
  83. } else {
  84. echo 'False';
  85. }
  86. }
  87. /**
  88. * Connect method to www.salesforce.com
  89. *
  90. * @param string $wsdl Salesforce.com Partner WSDL
  91. * @param object $proxy (optional) proxy settings with properties host, port,
  92. * login and password
  93. * @param array $soap_options (optional) Additional options to send to the
  94. * SoapClient constructor. @see
  95. * http://php.net/manual/en/soapclient.soapclient.php
  96. */
  97. public function createConnection($wsdl, $proxy=null, $soap_options=array()) {
  98. $phpversion = substr(phpversion(), 0, strpos(phpversion(), '-'));
  99. $soapClientArray = array_merge(array (
  100. 'user_agent' => 'salesforce-toolkit-php/'.$this->version,
  101. 'encoding' => 'utf-8',
  102. 'trace' => 1,
  103. 'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
  104. 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP
  105. ), $soap_options);
  106. // We don't need to parse out any subversion suffix - e.g. "-01" since
  107. // PHP type conversion will ignore it
  108. if (phpversion() < 5.2) {
  109. die("PHP versions older than 5.2 are no longer supported. Please upgrade!");
  110. }
  111. if ($proxy != null) {
  112. $proxySettings = array();
  113. $proxySettings['proxy_host'] = $proxy->host;
  114. $proxySettings['proxy_port'] = $proxy->port; // Use an integer, not a string
  115. $proxySettings['proxy_login'] = $proxy->login;
  116. $proxySettings['proxy_password'] = $proxy->password;
  117. $soapClientArray = array_merge($soapClientArray, $proxySettings);
  118. }
  119. $this->sforce = $this->getSoapClient($wsdl, $soapClientArray);
  120. return $this->sforce;
  121. }
  122. public function setCallOptions($header) {
  123. if ($header != NULL) {
  124. $this->callOptions = new SoapHeader($this->namespace, 'CallOptions', array (
  125. 'client' => $header->client,
  126. 'defaultNamespace' => $header->defaultNamespace
  127. ));
  128. } else {
  129. $this->callOptions = NULL;
  130. }
  131. }
  132. /**
  133. * Login to Salesforce.com and starts a client session.
  134. *
  135. * @param string $username Username
  136. * @param string $password Password
  137. *
  138. * @return LoginResult
  139. */
  140. public function login($username, $password) {
  141. $this->sforce->__setSoapHeaders(NULL);
  142. if ($this->callOptions != NULL) {
  143. $this->sforce->__setSoapHeaders(array($this->callOptions));
  144. }
  145. if ($this->loginScopeHeader != NULL) {
  146. $this->sforce->__setSoapHeaders(array($this->loginScopeHeader));
  147. }
  148. $result = $this->sforce->login(array (
  149. 'username' => $username,
  150. 'password' => $password
  151. ));
  152. $result = $result->result;
  153. $this->_setLoginHeader($result);
  154. return $result;
  155. }
  156. /**
  157. * log outs from the salseforce system`
  158. *
  159. * @return LogoutResult
  160. */
  161. public function logout() {
  162. $this->setHeaders("logout");
  163. $arg = new stdClass();
  164. return $this->sforce->logout();
  165. }
  166. /**
  167. *invalidate Sessions from the salseforce system`
  168. *
  169. * @return invalidateSessionsResult
  170. */
  171. public function invalidateSessions() {
  172. $this->setHeaders("invalidateSessions");
  173. $arg = new stdClass();
  174. $this->logout();
  175. return $this->sforce->invalidateSessions();
  176. }
  177. /**
  178. * Specifies the session ID returned from the login server after a successful
  179. * login.
  180. */
  181. protected function _setLoginHeader($loginResult) {
  182. $this->sessionId = $loginResult->sessionId;
  183. $this->setSessionHeader($this->sessionId);
  184. $serverURL = $loginResult->serverUrl;
  185. $this->setEndPoint($serverURL);
  186. }
  187. /**
  188. * Set the endpoint.
  189. *
  190. * @param string $location Location
  191. */
  192. public function setEndpoint($location) {
  193. $this->location = $location;
  194. $this->sforce->__setLocation($location);
  195. }
  196. private function setHeaders($call=NULL) {
  197. $this->sforce->__setSoapHeaders(NULL);
  198. $header_array = array (
  199. $this->sessionHeader
  200. );
  201. $header = $this->callOptions;
  202. if ($header != NULL) {
  203. array_push($header_array, $header);
  204. }
  205. if ($call == "create" ||
  206. $call == "merge" ||
  207. $call == "update" ||
  208. $call == "upsert"
  209. ) {
  210. $header = $this->assignmentRuleHeader;
  211. if ($header != NULL) {
  212. array_push($header_array, $header);
  213. }
  214. }
  215. if ($call == "login") {
  216. $header = $this->loginScopeHeader;
  217. if ($header != NULL) {
  218. array_push($header_array, $header);
  219. }
  220. }
  221. if ($call == "create" ||
  222. $call == "resetPassword" ||
  223. $call == "update" ||
  224. $call == "upsert"
  225. ) {
  226. $header = $this->emailHeader;
  227. if ($header != NULL) {
  228. array_push($header_array, $header);
  229. }
  230. }
  231. if ($call == "create" ||
  232. $call == "merge" ||
  233. $call == "query" ||
  234. $call == "retrieve" ||
  235. $call == "update" ||
  236. $call == "upsert"
  237. ) {
  238. $header = $this->mruHeader;
  239. if ($header != NULL) {
  240. array_push($header_array, $header);
  241. }
  242. }
  243. if ($call == "delete") {
  244. $header = $this->userTerritoryDeleteHeader;
  245. if ($header != NULL) {
  246. array_push($header_array, $header);
  247. }
  248. }
  249. if ($call == "query" ||
  250. $call == "queryMore" ||
  251. $call == "retrieve") {
  252. $header = $this->queryHeader;
  253. if ($header != NULL) {
  254. array_push($header_array, $header);
  255. }
  256. }
  257. // try to add allowFieldTruncationHeader
  258. $allowFieldTruncationHeaderCalls = array(
  259. 'convertLead', 'create', 'merge',
  260. 'process', 'undelete', 'update',
  261. 'upsert',
  262. );
  263. if (in_array($call, $allowFieldTruncationHeaderCalls)) {
  264. $header = $this->allowFieldTruncationHeader;
  265. if ($header != NULL) {
  266. array_push($header_array, $header);
  267. }
  268. }
  269. // try to add localeOptions
  270. if ($call == 'describeSObject' || $call == 'describeSObjects') {
  271. $header = $this->localeOptions;
  272. if ($header != NULL) {
  273. array_push($header_array, $header);
  274. }
  275. }
  276. // try to add PackageVersionHeader
  277. $packageVersionHeaderCalls = array(
  278. 'convertLead', 'create', 'delete', 'describeGlobal',
  279. 'describeLayout', 'describeSObject', 'describeSObjects',
  280. 'describeSoftphoneLayout', 'describeTabs', 'merge',
  281. 'process', 'query', 'retrieve', 'search', 'undelete',
  282. 'update', 'upsert',
  283. );
  284. if(in_array($call, $packageVersionHeaderCalls)) {
  285. $header = $this->packageVersionHeader;
  286. if ($header != NULL) {
  287. array_push($header_array, $header);
  288. }
  289. }
  290. $this->sforce->__setSoapHeaders($header_array);
  291. }
  292. public function setAssignmentRuleHeader($header) {
  293. if ($header != NULL) {
  294. $this->assignmentRuleHeader = new SoapHeader($this->namespace, 'AssignmentRuleHeader', array (
  295. 'assignmentRuleId' => $header->assignmentRuleId,
  296. 'useDefaultRule' => $header->useDefaultRuleFlag
  297. ));
  298. } else {
  299. $this->assignmentRuleHeader = NULL;
  300. }
  301. }
  302. public function setEmailHeader($header) {
  303. if ($header != NULL) {
  304. $this->emailHeader = new SoapHeader($this->namespace, 'EmailHeader', array (
  305. 'triggerAutoResponseEmail' => $header->triggerAutoResponseEmail,
  306. 'triggerOtherEmail' => $header->triggerOtherEmail,
  307. 'triggerUserEmail' => $header->triggerUserEmail
  308. ));
  309. } else {
  310. $this->emailHeader = NULL;
  311. }
  312. }
  313. public function setLoginScopeHeader($header) {
  314. if ($header != NULL) {
  315. $this->loginScopeHeader = new SoapHeader($this->namespace, 'LoginScopeHeader', array (
  316. 'organizationId' => $header->organizationId,
  317. 'portalId' => $header->portalId
  318. ));
  319. } else {
  320. $this->loginScopeHeader = NULL;
  321. }
  322. //$this->setHeaders('login');
  323. }
  324. public function setMruHeader($header) {
  325. if ($header != NULL) {
  326. $this->mruHeader = new SoapHeader($this->namespace, 'MruHeader', array (
  327. 'updateMru' => $header->updateMruFlag
  328. ));
  329. } else {
  330. $this->mruHeader = NULL;
  331. }
  332. }
  333. public function setSessionHeader($id) {
  334. if ($id != NULL) {
  335. $this->sessionHeader = new SoapHeader($this->namespace, 'SessionHeader', array (
  336. 'sessionId' => $id
  337. ));
  338. $this->sessionId = $id;
  339. } else {
  340. $this->sessionHeader = NULL;
  341. $this->sessionId = NULL;
  342. }
  343. }
  344. public function setUserTerritoryDeleteHeader($header) {
  345. if ($header != NULL) {
  346. $this->userTerritoryDeleteHeader = new SoapHeader($this->namespace, 'UserTerritoryDeleteHeader ', array (
  347. 'transferToUserId' => $header->transferToUserId
  348. ));
  349. } else {
  350. $this->userTerritoryDeleteHeader = NULL;
  351. }
  352. }
  353. public function setQueryOptions($header) {
  354. if ($header != NULL) {
  355. $this->queryHeader = new SoapHeader($this->namespace, 'QueryOptions', array (
  356. 'batchSize' => $header->batchSize
  357. ));
  358. } else {
  359. $this->queryHeader = NULL;
  360. }
  361. }
  362. public function setAllowFieldTruncationHeader($header) {
  363. if ($header != NULL) {
  364. $this->allowFieldTruncationHeader = new SoapHeader($this->namespace, 'AllowFieldTruncationHeader', array (
  365. 'allowFieldTruncation' => $header->allowFieldTruncation
  366. )
  367. );
  368. } else {
  369. $this->allowFieldTruncationHeader = NULL;
  370. }
  371. }
  372. public function setLocaleOptions($header) {
  373. if ($header != NULL) {
  374. $this->localeOptions = new SoapHeader($this->namespace, 'LocaleOptions',
  375. array (
  376. 'language' => $header->language
  377. )
  378. );
  379. } else {
  380. $this->localeOptions = NULL;
  381. }
  382. }
  383. /**
  384. * @param $header
  385. */
  386. public function setPackageVersionHeader($header) {
  387. if ($header != NULL) {
  388. $headerData = array('packageVersions' => array());
  389. foreach ($header->packageVersions as $key => $hdrElem) {
  390. $headerData['packageVersions'][] = array(
  391. 'majorNumber' => $hdrElem->majorNumber,
  392. 'minorNumber' => $hdrElem->minorNumber,
  393. 'namespace' => $hdrElem->namespace,
  394. );
  395. }
  396. $this->packageVersionHeader = new SoapHeader($this->namespace,
  397. 'PackageVersionHeader',
  398. $headerData
  399. );
  400. } else {
  401. $this->packageVersionHeader = NULL;
  402. }
  403. }
  404. public function getSessionId() {
  405. return $this->sessionId;
  406. }
  407. public function getLocation() {
  408. return $this->location;
  409. }
  410. public function getConnection() {
  411. return $this->sforce;
  412. }
  413. public function getFunctions() {
  414. return $this->sforce->__getFunctions();
  415. }
  416. public function getTypes() {
  417. return $this->sforce->__getTypes();
  418. }
  419. public function getLastRequest() {
  420. return $this->sforce->__getLastRequest();
  421. }
  422. public function getLastRequestHeaders() {
  423. return $this->sforce->__getLastRequestHeaders();
  424. }
  425. public function getLastResponse() {
  426. return $this->sforce->__getLastResponse();
  427. }
  428. public function getLastResponseHeaders() {
  429. return $this->sforce->__getLastResponseHeaders();
  430. }
  431. protected function _convertToAny($fields) {
  432. $anyString = '';
  433. foreach ($fields as $key => $value) {
  434. $anyString = $anyString . '<' . $key . '>' . $value . '</' . $key . '>';
  435. }
  436. return $anyString;
  437. }
  438. protected function _create($arg) {
  439. $this->setHeaders("create");
  440. return $this->sforce->create($arg)->result;
  441. }
  442. protected function _merge($arg) {
  443. $this->setHeaders("merge");
  444. return $this->sforce->merge($arg)->result;
  445. }
  446. protected function _process($arg) {
  447. $this->setHeaders();
  448. return $this->sforce->process($arg)->result;
  449. }
  450. protected function _update($arg) {
  451. $this->setHeaders("update");
  452. return $this->sforce->update($arg)->result;
  453. }
  454. protected function _upsert($arg) {
  455. $this->setHeaders("upsert");
  456. return $this->sforce->upsert($arg)->result;
  457. }
  458. public function sendSingleEmail($request) {
  459. if (is_array($request)) {
  460. $messages = array();
  461. foreach ($request as $r) {
  462. $email = new SoapVar($r, SOAP_ENC_OBJECT, 'SingleEmailMessage', $this->namespace);
  463. array_push($messages, $email);
  464. }
  465. $arg = new stdClass();
  466. $arg->messages = $messages;
  467. return $this->_sendEmail($arg);
  468. } else {
  469. $backtrace = debug_backtrace();
  470. die('Please pass in array to this function: '.$backtrace[0]['function']);
  471. }
  472. }
  473. public function sendMassEmail($request) {
  474. if (is_array($request)) {
  475. $messages = array();
  476. foreach ($request as $r) {
  477. $email = new SoapVar($r, SOAP_ENC_OBJECT, 'MassEmailMessage', $this->namespace);
  478. array_push($messages, $email);
  479. }
  480. $arg = new stdClass();
  481. $arg->messages = $messages;
  482. return $this->_sendEmail($arg);
  483. } else {
  484. $backtrace = debug_backtrace();
  485. die('Please pass in array to this function: '.$backtrace[0]['function']);
  486. }
  487. }
  488. protected function _sendEmail($arg) {
  489. $this->setHeaders();
  490. return $this->sforce->sendEmail($arg)->result;
  491. }
  492. /**
  493. * Converts a Lead into an Account, Contact, or (optionally) an Opportunity.
  494. *
  495. * @param array $leadConverts Array of LeadConvert
  496. *
  497. * @return LeadConvertResult
  498. */
  499. public function convertLead($leadConverts) {
  500. $this->setHeaders("convertLead");
  501. $arg = new stdClass();
  502. $arg->leadConverts = $leadConverts;
  503. return $this->sforce->convertLead($arg);
  504. }
  505. /**
  506. * Deletes one or more new individual objects to your organization's data.
  507. *
  508. * @param array $ids Array of fields
  509. * @return DeleteResult
  510. */
  511. public function delete($ids) {
  512. $this->setHeaders("delete");
  513. $arg = new stdClass();
  514. $arg->ids = $ids;
  515. return $this->sforce->delete($arg)->result;
  516. }
  517. /**
  518. * Deletes one or more new individual objects to your organization's data.
  519. *
  520. * @param array $ids Array of fields
  521. * @return DeleteResult
  522. */
  523. public function undelete($ids) {
  524. $this->setHeaders("undelete");
  525. $arg = new stdClass();
  526. $arg->ids = $ids;
  527. return $this->sforce->undelete($arg)->result;
  528. }
  529. /**
  530. * Deletes one or more new individual objects to your organization's data.
  531. *
  532. * @param array $ids Array of fields
  533. * @return DeleteResult
  534. */
  535. public function emptyRecycleBin($ids) {
  536. $this->setHeaders();
  537. $arg = new stdClass();
  538. $arg->ids = $ids;
  539. return $this->sforce->emptyRecycleBin($arg)->result;
  540. }
  541. /**
  542. * Process Submit Request for Approval
  543. *
  544. * @param array $processRequestArray
  545. * @return ProcessResult
  546. */
  547. public function processSubmitRequest($processRequestArray) {
  548. if (is_array($processRequestArray)) {
  549. foreach ($processRequestArray as &$process) {
  550. $process = new SoapVar($process, SOAP_ENC_OBJECT, 'ProcessSubmitRequest', $this->namespace);
  551. }
  552. $arg = new stdClass();
  553. $arg->actions = $processRequestArray;
  554. return $this->_process($arg);
  555. } else {
  556. $backtrace = debug_backtrace();
  557. die('Please pass in array to this function: '.$backtrace[0]['function']);
  558. }
  559. }
  560. /**
  561. * Process Work Item Request for Approval
  562. *
  563. * @param array $processRequestArray
  564. * @return ProcessResult
  565. */
  566. public function processWorkitemRequest($processRequestArray) {
  567. if (is_array($processRequestArray)) {
  568. foreach ($processRequestArray as &$process) {
  569. $process = new SoapVar($process, SOAP_ENC_OBJECT, 'ProcessWorkitemRequest', $this->namespace);
  570. }
  571. $arg = new stdClass();
  572. $arg->actions = $processRequestArray;
  573. return $this->_process($arg);
  574. } else {
  575. $backtrace = debug_backtrace();
  576. die('Please pass in array to this function: '.$backtrace[0]['function']);
  577. }
  578. }
  579. /**
  580. * Retrieves a list of available objects for your organization's data.
  581. *
  582. * @return DescribeGlobalResult
  583. */
  584. public function describeGlobal() {
  585. $this->setHeaders("describeGlobal");
  586. return $this->sforce->describeGlobal()->result;
  587. }
  588. /**
  589. * Use describeLayout to retrieve information about the layout (presentation
  590. * of data to users) for a given object type. The describeLayout call returns
  591. * metadata about a given page layout, including layouts for edit and
  592. * display-only views and record type mappings. Note that field-level security
  593. * and layout editability affects which fields appear in a layout.
  594. *
  595. * @param string Type Object Type
  596. * @return DescribeLayoutResult
  597. */
  598. public function describeLayout($type) {
  599. $this->setHeaders("describeLayout");
  600. $arg = new stdClass();
  601. $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  602. return $this->sforce->describeLayout($arg)->result;
  603. }
  604. /**
  605. * Describes metadata (field list and object properties) for the specified
  606. * object.
  607. *
  608. * @param string $type Object type
  609. * @return DescribsSObjectResult
  610. */
  611. public function describeSObject($type) {
  612. $this->setHeaders("describeSObject");
  613. $arg = new stdClass();
  614. $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  615. return $this->sforce->describeSObject($arg)->result;
  616. }
  617. /**
  618. * An array-based version of describeSObject; describes metadata (field list
  619. * and object properties) for the specified object or array of objects.
  620. *
  621. * @param array $arrayOfTypes Array of object types.
  622. * @return DescribsSObjectResult
  623. */
  624. public function describeSObjects($arrayOfTypes) {
  625. $this->setHeaders("describeSObjects");
  626. return $this->sforce->describeSObjects($arrayOfTypes)->result;
  627. }
  628. /**
  629. * The describeTabs call returns information about the standard apps and
  630. * custom apps, if any, available for the user who sends the call, including
  631. * the list of tabs defined for each app.
  632. *
  633. * @return DescribeTabSetResult
  634. */
  635. public function describeTabs() {
  636. $this->setHeaders("describeTabs");
  637. return $this->sforce->describeTabs()->result;
  638. }
  639. /**
  640. * To enable data categories groups you must enable Answers or Knowledge Articles module in
  641. * admin panel, after adding category group and assign it to Answers or Knowledge Articles
  642. *
  643. * @param string $sObjectType sObject Type
  644. * @return DescribeDataCategoryGroupResult
  645. */
  646. public function describeDataCategoryGroups($sObjectType) {
  647. $this->setHeaders('describeDataCategoryGroups');
  648. $arg = new stdClass();
  649. $arg->sObjectType = new SoapVar($sObjectType, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  650. return $this->sforce->describeDataCategoryGroups($arg)->result;
  651. }
  652. /**
  653. * Retrieves available category groups along with their data category structure for objects specified in the request.
  654. *
  655. * @param DataCategoryGroupSobjectTypePair $pairs
  656. * @param bool $topCategoriesOnly Object Type
  657. * @return DescribeLayoutResult
  658. */
  659. public function describeDataCategoryGroupStructures(array $pairs, $topCategoriesOnly) {
  660. $this->setHeaders('describeDataCategoryGroupStructures');
  661. $arg = new stdClass();
  662. $arg->pairs = $pairs;
  663. $arg->topCategoriesOnly = new SoapVar($topCategoriesOnly, XSD_BOOLEAN, 'boolean', 'http://www.w3.org/2001/XMLSchema');
  664. return $this->sforce->describeDataCategoryGroupStructures($arg)->result;
  665. }
  666. /**
  667. * Retrieves the list of individual objects that have been deleted within the
  668. * given timespan for the specified object.
  669. *
  670. * @param string $type Ojbect type
  671. * @param date $startDate Start date
  672. * @param date $endDate End Date
  673. * @return GetDeletedResult
  674. */
  675. public function getDeleted($type, $startDate, $endDate) {
  676. $this->setHeaders("getDeleted");
  677. $arg = new stdClass();
  678. $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  679. $arg->startDate = $startDate;
  680. $arg->endDate = $endDate;
  681. return $this->sforce->getDeleted($arg)->result;
  682. }
  683. /**
  684. * Retrieves the list of individual objects that have been updated (added or
  685. * changed) within the given timespan for the specified object.
  686. *
  687. * @param string $type Ojbect type
  688. * @param date $startDate Start date
  689. * @param date $endDate End Date
  690. * @return GetUpdatedResult
  691. */
  692. public function getUpdated($type, $startDate, $endDate) {
  693. $this->setHeaders("getUpdated");
  694. $arg = new stdClass();
  695. $arg->sObjectType = new SoapVar($type, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  696. $arg->startDate = $startDate;
  697. $arg->endDate = $endDate;
  698. return $this->sforce->getUpdated($arg)->result;
  699. }
  700. /**
  701. * Executes a query against the specified object and returns data that matches
  702. * the specified criteria.
  703. *
  704. * @param String $query Query String
  705. * @param QueryOptions $queryOptions Batch size limit. OPTIONAL
  706. * @return QueryResult
  707. */
  708. public function query($query) {
  709. $this->setHeaders("query");
  710. $raw = $this->sforce->query(array (
  711. 'queryString' => $query
  712. ))->result;
  713. $QueryResult = new QueryResult($raw);
  714. $QueryResult->setSf($this); // Dependency Injection
  715. return $QueryResult;
  716. }
  717. /**
  718. * Retrieves the next batch of objects from a query.
  719. *
  720. * @param QueryLocator $queryLocator Represents the server-side cursor that tracks the current processing location in the query result set.
  721. * @param QueryOptions $queryOptions Batch size limit. OPTIONAL
  722. * @return QueryResult
  723. */
  724. public function queryMore($queryLocator) {
  725. $this->setHeaders("queryMore");
  726. $arg = new stdClass();
  727. $arg->queryLocator = $queryLocator;
  728. $raw = $this->sforce->queryMore($arg)->result;
  729. $QueryResult = new QueryResult($raw);
  730. $QueryResult->setSf($this); // Dependency Injection
  731. return $QueryResult;
  732. }
  733. /**
  734. * Retrieves data from specified objects, whether or not they have been deleted.
  735. *
  736. * @param String $query Query String
  737. * @param QueryOptions $queryOptions Batch size limit. OPTIONAL
  738. * @return QueryResult
  739. */
  740. public function queryAll($query, $queryOptions = NULL) {
  741. $this->setHeaders("queryAll");
  742. $raw = $this->sforce->queryAll(array (
  743. 'queryString' => $query
  744. ))->result;
  745. $QueryResult = new QueryResult($raw);
  746. $QueryResult->setSf($this); // Dependency Injection
  747. return $QueryResult;
  748. }
  749. /**
  750. * Retrieves one or more objects based on the specified object IDs.
  751. *
  752. * @param string $fieldList One or more fields separated by commas.
  753. * @param string $sObjectType Object from which to retrieve data.
  754. * @param array $ids Array of one or more IDs of the objects to retrieve.
  755. * @return sObject[]
  756. */
  757. public function retrieve($fieldList, $sObjectType, $ids) {
  758. $this->setHeaders("retrieve");
  759. $arg = new stdClass();
  760. $arg->fieldList = $fieldList;
  761. $arg->sObjectType = new SoapVar($sObjectType, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  762. $arg->ids = $ids;
  763. return $this->sforce->retrieve($arg)->result;
  764. }
  765. /**
  766. * Executes a text search in your organization's data.
  767. *
  768. * @param string $searchString Search string that specifies the text expression to search for.
  769. * @return SearchResult
  770. */
  771. public function search($searchString) {
  772. $this->setHeaders("search");
  773. $arg = new stdClass();
  774. $arg->searchString = new SoapVar($searchString, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  775. return new SforceSearchResult($this->sforce->search($arg)->result);
  776. }
  777. /**
  778. * Retrieves the current system timestamp (GMT) from the Web service.
  779. *
  780. * @return timestamp
  781. */
  782. public function getServerTimestamp() {
  783. $this->setHeaders("getServerTimestamp");
  784. return $this->sforce->getServerTimestamp()->result;
  785. }
  786. public function getUserInfo() {
  787. $this->setHeaders("getUserInfo");
  788. return $this->sforce->getUserInfo()->result;
  789. }
  790. /**
  791. * Sets the specified user's password to the specified value.
  792. *
  793. * @param string $userId ID of the User.
  794. * @param string $password New password
  795. */
  796. public function setPassword($userId, $password) {
  797. $this->setHeaders("setPassword");
  798. $arg = new stdClass();
  799. $arg->userId = new SoapVar($userId, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  800. $arg->password = $password;
  801. return $this->sforce->setPassword($arg);
  802. }
  803. /**
  804. * Changes a user's password to a system-generated value.
  805. *
  806. * @param string $userId Id of the User
  807. * @return password
  808. */
  809. public function resetPassword($userId) {
  810. $this->setHeaders("resetPassword");
  811. $arg = new stdClass();
  812. $arg->userId = new SoapVar($userId, XSD_STRING, 'string', 'http://www.w3.org/2001/XMLSchema');
  813. return $this->sforce->resetPassword($arg)->result;
  814. }
  815. }
  816. class SforceSearchResult {
  817. public $searchRecords;
  818. public function __construct($response) {
  819. if($response instanceof SforceSearchResult) {
  820. $this->searchRecords = $response->searchRecords;
  821. } else {
  822. $this->searchRecords = array();
  823. if (isset($response->searchRecords)) {
  824. if (is_array($response->searchRecords)) {
  825. foreach ($response->searchRecords as $record) {
  826. $sobject = new SObject($record->record);
  827. array_push($this->searchRecords, $sobject);
  828. };
  829. } else {
  830. $sobject = new SObject($response->searchRecords->record);
  831. array_push($this->records, $sobject);
  832. }
  833. }
  834. }
  835. }
  836. }
  837. class QueryResult implements Iterator{
  838. public $queryLocator;
  839. public $done;
  840. public $records;
  841. public $size;
  842. public $pointer; // Current iterator location
  843. private $sf; // SOAP Client
  844. public function __construct($response) {
  845. $this->queryLocator = $response->queryLocator;
  846. $this->done = $response->done;
  847. $this->size = $response->size;
  848. $this->pointer = 0;
  849. $this->sf = false;
  850. if($response instanceof QueryResult) {
  851. $this->records = $response->records;
  852. } else {
  853. $this->records = array();
  854. if (isset($response->records)) {
  855. if (is_array($response->records)) {
  856. foreach ($response->records as $record) {
  857. array_push($this->records, $record);
  858. };
  859. } else {
  860. array_push($this->records, $record);
  861. }
  862. }
  863. }
  864. }
  865. public function setSf(SforceBaseClient $sf) { $this->sf = $sf; } // Dependency Injection
  866. // Basic Iterator implementation functions
  867. public function rewind() { $this->pointer = 0; }
  868. public function next() { ++$this->pointer; }
  869. public function key() { return $this->pointer; }
  870. public function current() { return new SObject($this->records[$this->pointer]); }
  871. public function valid() {
  872. while ($this->pointer >= count($this->records)) {
  873. // Pointer is larger than (current) result set; see if we can fetch more
  874. if ($this->done === false) {
  875. if ($this->sf === false) throw new Exception("Dependency not met!");
  876. $response = $this->sf->queryMore($this->queryLocator);
  877. $this->records = array_merge($this->records, $response->records); // Append more results
  878. $this->done = $response->done;
  879. $this->queryLocator = $response->queryLocator;
  880. } else {
  881. return false; // No more records to fetch
  882. }
  883. }
  884. if (isset($this->records[$this->pointer])) return true;
  885. throw new Exception("QueryResult has gaps in the record data?");
  886. }
  887. }
  888. class SObject {
  889. public $type;
  890. public $fields;
  891. // public $sobject;
  892. public function __construct($response=NULL) {
  893. if (!isset($response) && !$response) {
  894. return;
  895. }
  896. foreach ($response as $responseKey => $responseValue) {
  897. if (in_array($responseKey, array('Id', 'type', 'any'))) {
  898. continue;
  899. }
  900. $this->$responseKey = $responseValue;
  901. }
  902. if (isset($response->Id)) {
  903. $this->Id = is_array($response->Id) ? $response->Id[0] : $response->Id;
  904. }
  905. if (isset($response->type)) {
  906. $this->type = $response->type;
  907. }
  908. if (isset($response->any)) {
  909. try {
  910. //$this->fields = $this->convertFields($response->any);
  911. // If ANY is an object, instantiate another SObject
  912. if ($response->any instanceof stdClass) {
  913. if ($this->isSObject($response->any)) {
  914. $anArray = array();
  915. $sobject = new SObject($response->any);
  916. $anArray[] = $sobject;
  917. $this->sobjects = $anArray;
  918. } else {
  919. // this is for parent to child relationships
  920. $this->queryResult = new QueryResult($response->any);
  921. }
  922. } else {
  923. // If ANY is an array
  924. if (is_array($response->any)) {
  925. // Loop through each and perform some action.
  926. $anArray = array();
  927. // Modify the foreach to have $key=>$value
  928. // Added on 28th April 2008
  929. foreach ($response->any as $key=>$item) {
  930. if ($item instanceof stdClass) {
  931. if ($this->isSObject($item)) {
  932. $sobject = new SObject($item);
  933. // make an associative array instead of a numeric one
  934. $anArray[$key] = $sobject;
  935. } else {
  936. // this is for parent to child relationships
  937. //$this->queryResult = new QueryResult($item);
  938. if (!isset($this->queryResult)) {
  939. $this->queryResult = array();
  940. }
  941. array_push($this->queryResult, new QueryResult($item));
  942. }
  943. } else {
  944. //$this->fields = $this->convertFields($item);
  945. if (strpos($item, 'sf:') === false) {
  946. $currentXmlValue = sprintf('<sf:%s>%s</sf:%s>', $key, $item, $key);
  947. } else {
  948. $currentXmlValue = $item;
  949. }
  950. if (!isset($fieldsToConvert)) {
  951. $fieldsToConvert = $currentXmlValue;
  952. } else {
  953. $fieldsToConvert .= $currentXmlValue;
  954. }
  955. }
  956. }
  957. if (isset($fieldsToConvert)) {
  958. // If this line is commented, then the fields becomes a stdclass object and does not have the name variable
  959. // In this case the foreach loop on line 252 runs successfuly
  960. $this->fields = $this->convertFields($fieldsToConvert);
  961. }
  962. if (sizeof($anArray) > 0) {
  963. // To add more variables to the the top level sobject
  964. foreach ($anArray as $key=>$children_sobject) {
  965. $this->fields->$key = $children_sobject;
  966. }
  967. //array_push($this->fields, $anArray);
  968. // Uncommented on 28th April since all the sobjects have now been moved to the fields
  969. //$this->sobjects = $anArray;
  970. }
  971. /*
  972. $this->fields = $this->convertFields($response->any[0]);
  973. if (isset($response->any[1]->records)) {
  974. $anArray = array();
  975. if ($response->any[1]->size == 1) {
  976. $records = array (
  977. $response->any[1]->records
  978. );
  979. } else {
  980. $records = $response->any[1]->records;
  981. }
  982. foreach ($records as $record) {
  983. $sobject = new SObject($record);
  984. array_push($anArray, $sobject);
  985. }
  986. $this->sobjects = $anArray;
  987. } else {
  988. $anArray = array();
  989. $sobject = new SObject($response->any[1]);
  990. array_push($anArray, $sobject);
  991. $this->sobjects = $anArray;
  992. }
  993. */
  994. } else {
  995. $this->fields = $this->convertFields($response->any);
  996. }
  997. }
  998. } catch (Exception $e) {
  999. var_dump('exception: ', $e);
  1000. }
  1001. }
  1002. }
  1003. function __get($name) { return (isset($this->fields->$name))? $this->fields->$name : false; }
  1004. function __isset($name) { return isset($this->fields->$name); }
  1005. /**
  1006. * Parse the "any" string from an sObject. First strip out the sf: and then
  1007. * enclose string with <Object></Object>. Load the string using
  1008. * simplexml_load_string and return an array that can be traversed.
  1009. */
  1010. function convertFields($any) {
  1011. $str = preg_replace('{sf:}', '', $any);
  1012. $array = $this->xml2array('<Object xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'.$str.'</Object>', 2);
  1013. $xml = new stdClass();
  1014. if (!count($array['Object']))
  1015. return $xml;
  1016. foreach ($array['Object'] as $k=>$v) {
  1017. $xml->$k = $v;
  1018. }
  1019. //$new_string = '<Object xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'.$new_string.'</Object>';
  1020. //$new_string = $new_string;
  1021. //$xml = simplexml_load_string($new_string);
  1022. return $xml;
  1023. }
  1024. /**
  1025. *
  1026. * @param string $contents
  1027. * @return array
  1028. */
  1029. function xml2array($contents, $get_attributes=1) {
  1030. if(!$contents) return array();
  1031. if(!function_exists('xml_parser_create')) {
  1032. //print "'xml_parser_create()' function not found!";
  1033. return array('not found');
  1034. }
  1035. //Get the XML parser of PHP - PHP must have this module for the parser to work
  1036. $parser = xml_parser_create();
  1037. xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, 0 );
  1038. xml_parser_set_option( $parser, XML_OPTION_SKIP_WHITE, 1 );
  1039. xml_parse_into_struct( $parser, $contents, $xml_values );
  1040. xml_parser_free( $parser );
  1041. if(!$xml_values) return;//Hmm...
  1042. //Initializations
  1043. $xml_array = array();
  1044. $parents = array();
  1045. $opened_tags = array();
  1046. $arr = array();
  1047. $current = &$xml_array;
  1048. //Go through the tags.
  1049. foreach($xml_values as $data) {
  1050. unset($attributes,$value);//Remove existing values, or there will be trouble
  1051. //This command will extract these variables into the foreach scope
  1052. // tag(string), type(string), level(int), attributes(array).
  1053. extract($data);//We could use the array by itself, but this cooler.
  1054. $result = '';
  1055. if ($get_attributes) {
  1056. switch ($get_attributes) {
  1057. case 1:
  1058. $result = array();
  1059. if(isset($value)) $result['value'] = $value;
  1060. //Set the attributes too.
  1061. if(isset($attributes)) {
  1062. foreach($attributes as $attr => $val) {
  1063. if($get_attributes == 1) $result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
  1064. /** :TODO: should we change the key name to '_attr'? Someone may use the tagname 'attr'. Same goes for 'value' too */
  1065. }
  1066. }
  1067. break;
  1068. case 2:
  1069. $result = array();
  1070. if (isset($value)) {
  1071. $result = $value;
  1072. }
  1073. //Check for nil and ignore other attributes.
  1074. if (isset($attributes) && isset($attributes['xsi:nil']) && !strcasecmp($attributes['xsi:nil'], 'true')) {
  1075. $result = null;
  1076. }
  1077. break;
  1078. }
  1079. } elseif (isset($value)) {
  1080. $result = $value;
  1081. }
  1082. //See tag status and do the needed.
  1083. if($type == "open") {//The starting of the tag '<tag>'
  1084. $parent[$level-1] = &$current;
  1085. if(!is_array($current) or (!in_array($tag, array_keys($current)))) { //Insert New tag
  1086. $current[$tag] = $result;
  1087. $current = &$current[$tag];
  1088. } else { //There was another element with the same tag name
  1089. if(isset($current[$tag][0])) {
  1090. array_push($current[$tag], $result);
  1091. } else {
  1092. $current[$tag] = array($current[$tag],$result);
  1093. }
  1094. $last = count($current[$tag]) - 1;
  1095. $current = &$current[$tag][$last];
  1096. }
  1097. } elseif($type == "complete") { //Tags that ends in 1 line '<tag />'
  1098. //See if the key is already taken.
  1099. if(!isset($current[$tag])) { //New Key
  1100. $current[$tag] = $result;
  1101. } else { //If taken, put all things inside a list(array)
  1102. if((is_array($current[$tag]) and $get_attributes == 0)//If it is already an array...
  1103. or (isset($current[$tag][0]) and is_array($current[$tag][0]) and ($get_attributes == 1 || $get_attributes == 2))) {
  1104. array_push($current[$tag],$result); // ...push the new element into that array.
  1105. } else { //If it is not an array...
  1106. $current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value
  1107. }
  1108. }
  1109. } elseif($type == 'close') { //End of tag '</tag>'
  1110. $current = &$parent[$level-1];
  1111. }
  1112. }
  1113. return($xml_array);
  1114. }
  1115. /*
  1116. * If the stdClass has a done, we know it is a QueryResult
  1117. */
  1118. function isQueryResult($param) {
  1119. return isset($param->done);
  1120. }
  1121. /*
  1122. * If the stdClass has a type, we know it is an SObject
  1123. */
  1124. function isSObject($param) {
  1125. return isset($param->type);
  1126. }
  1127. }