Completed
Push — master ( 48bc46...861ddb )
by Jonathan
04:46 queued 34s
created

SforceBaseClient::setHeaders()   F

Complexity

Conditions 34
Paths > 20000

Size

Total Lines 109

Duplication

Lines 20
Ratio 18.35 %

Importance

Changes 0
Metric Value
cc 34
nc 39366
nop 1
dl 20
loc 109
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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