1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SugarAPI\SDK\EntryPoint\Abstracts; |
4
|
|
|
|
5
|
|
|
|
6
|
|
|
use SugarAPI\SDK\EntryPoint\Interfaces\EPInterface; |
7
|
|
|
use SugarAPI\SDK\Exception\EntryPointException; |
8
|
|
|
use SugarAPI\SDK\Request\POST; |
9
|
|
|
use SugarAPI\SDK\Response\JSON as JSONResponse; |
10
|
|
|
|
11
|
|
|
abstract class AbstractEntryPoint implements EPInterface { |
12
|
|
|
|
13
|
|
|
protected $_AUTH_REQUIRED = true; |
14
|
|
|
protected $_MODULE; |
15
|
|
|
protected $_URL; |
16
|
|
|
protected $_REQUIRED_DATA; |
17
|
|
|
|
18
|
|
|
protected $url; |
19
|
|
|
protected $Module; |
20
|
|
|
protected $Options = array(); |
21
|
|
|
protected $Data; |
22
|
|
|
protected $Request; |
23
|
|
|
protected $Response; |
24
|
|
|
|
25
|
|
|
public function __construct($url,$options = array()){ |
26
|
|
|
$this->url = $url; |
27
|
|
|
$this->Module = $this->_MODULE; |
28
|
|
|
|
29
|
|
|
if (!empty($options)) { |
30
|
|
|
if (empty($this->Module)) { |
31
|
|
|
if (strpos($this->_URL, '$module') !== FALSE) { |
32
|
|
|
$this->module($options[0]); |
33
|
|
|
array_shift($options); |
34
|
|
|
} |
35
|
|
|
} |
36
|
|
|
$this->options($options); |
37
|
|
|
} |
38
|
|
|
$this->setupRequest(); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @inheritdoc |
43
|
|
|
*/ |
44
|
|
|
public function module($module){ |
45
|
|
|
$this->Module = $module; |
46
|
|
|
return $this; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @inheritdoc |
51
|
|
|
*/ |
52
|
|
|
public function options(array $options){ |
53
|
|
|
$this->Options = $options; |
54
|
|
|
return $this; |
|
|
|
|
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @inheritdoc |
59
|
|
|
*/ |
60
|
|
|
public function data(array $data){ |
61
|
|
|
$this->Data = $data; |
62
|
|
|
$this->Request->setBody($this->Data); |
63
|
|
|
return $this; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @inheritdoc |
68
|
|
|
*/ |
69
|
|
|
public function execute(){ |
70
|
|
|
if ($this->verifyURL() && $this->validateData()) { |
71
|
|
|
$this->configureURL(); |
72
|
|
|
$this->Request->setURL($this->url); |
73
|
|
|
$this->Request->send(); |
74
|
|
|
$this->setupResponse(); |
75
|
|
|
//Trying to manage memory by closing Curl Resource |
76
|
|
|
$this->Request->close(); |
77
|
|
|
} |
78
|
|
|
return $this; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @inheritdoc |
84
|
|
|
*/ |
85
|
|
|
public function authRequired() { |
86
|
|
|
return $this->_AUTH_REQUIRED; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @inheritdoc |
91
|
|
|
*/ |
92
|
|
|
public function getModule() { |
93
|
|
|
return $this->Module; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* |
98
|
|
|
*/ |
99
|
|
|
public function getData(){ |
100
|
|
|
return $this->Data; |
|
|
|
|
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @inheritdoc |
105
|
|
|
*/ |
106
|
|
|
public function getURL(){ |
107
|
|
|
return $this->url; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @inheritdoc |
112
|
|
|
*/ |
113
|
|
|
public function getResponse(){ |
114
|
|
|
return $this->Response; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* @inheritdoc |
119
|
|
|
*/ |
120
|
|
|
public function getRequest(){ |
121
|
|
|
return $this->Request; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Configures the URL, by updating any variable placeholders in the URL property on the EntryPoint |
126
|
|
|
* - Replaces $module with $this->Module |
127
|
|
|
* - Replcaes all other variables starting with $, with options in the order they were given |
128
|
|
|
*/ |
129
|
|
|
protected function configureURL(){ |
130
|
|
|
$url = $this->_URL; |
131
|
|
|
if (strpos($this->_URL,"$")!==FALSE) { |
132
|
|
|
if (count($this->Options) > 0 || !empty($this->Module)) { |
133
|
|
|
$urlParts = explode("/", $this->_URL); |
134
|
|
|
$o = 0; |
135
|
|
|
foreach ($urlParts as $key => $part) { |
136
|
|
|
if (strpos($part, '$module') !== FALSE) { |
137
|
|
|
if (isset($this->Module)) { |
138
|
|
|
$urlParts[$key] = $this->Module; |
139
|
|
|
continue; |
140
|
|
|
} else { |
141
|
|
|
if (isset($this->Options[$o])) { |
142
|
|
|
$this->Module = $this->Options[$o]; |
143
|
|
|
array_shift($this->Options); |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
if (strpos($part, "$") !== FALSE) { |
148
|
|
|
if (isset($this->Options[$o])) { |
149
|
|
|
$urlParts[$key] = $this->Options[$o]; |
150
|
|
|
$o++; |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
$url = implode($urlParts,"/"); |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
$this->url = $this->url.$url; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Setup the Request Object property, setup on initial Construct of EntryPoint |
162
|
|
|
*/ |
163
|
|
|
protected function setupRequest(){ |
164
|
|
|
$this->Request = new POST(); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Setup the Response Object Property, not called until after Request Execution |
169
|
|
|
*/ |
170
|
|
|
protected function setupResponse(){ |
171
|
|
|
$this->Response = new JSONResponse($this->Request->getResponse(),$this->Request->getCurlObject()); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Verify URL variables have been removed, and that valid number of options were passed. |
176
|
|
|
* @return bool |
177
|
|
|
* @throws EntryPointExecutionFailure |
178
|
|
|
*/ |
179
|
|
|
protected function verifyURL(){ |
180
|
|
|
$urlVarCount = substr_count($this->_URL,"$"); |
181
|
|
|
$optionCount = 0; |
182
|
|
|
if (!empty($this->Module)){ |
183
|
|
|
$optionCount++; |
184
|
|
|
} |
185
|
|
|
$optionCount += count($this->Options); |
186
|
|
|
if ($urlVarCount!==$optionCount){ |
187
|
|
|
if (empty($this->Module) && strpos($this->_URL,'$module')){ |
188
|
|
|
throw new EntryPointException('Module is required for EntryPoint '.get_called_class()); |
189
|
|
|
}else{ |
190
|
|
|
throw new EntryPointException('EntryPoint URL ('.$this->_URL.') requires more parameters than passed.'); |
191
|
|
|
} |
192
|
|
|
}else{ |
193
|
|
|
return true; |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* @return bool |
199
|
|
|
*/ |
200
|
|
|
protected function validateData(){ |
201
|
|
|
if (empty($this->_REQUIRED_DATA)||count($this->_REQUIRED_DATA)==0){ |
202
|
|
|
return true; |
203
|
|
|
}else{ |
204
|
|
|
$errors = array(); |
205
|
|
|
foreach($this->_REQUIRED_DATA as $property){ |
206
|
|
|
if (isset($this->Data[$property]) || $this->Data[$property]!==null){ |
207
|
|
|
continue; |
208
|
|
|
}else{ |
209
|
|
|
$errors[] = $property; |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
if (count($errors)>0){ |
213
|
|
|
throw new EntryPointException('EntryPoint requires specific properties in Request data. Missing the following '.implode($errors,",")); |
214
|
|
|
}else{ |
215
|
|
|
return true; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
} |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.