This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file contains the SodaDataset class |
||
5 | * |
||
6 | * @copyright 2015 Vladimir Jimenez |
||
7 | * @license https://github.com/allejo/PhpSoda/blob/master/LICENSE.md MIT |
||
8 | */ |
||
9 | |||
10 | namespace allejo\Socrata; |
||
11 | |||
12 | use allejo\Socrata\Converters\Converter; |
||
13 | use allejo\Socrata\Exceptions\InvalidResourceException; |
||
14 | use allejo\Socrata\Utilities\StringUtilities; |
||
15 | use allejo\Socrata\Utilities\UrlQuery; |
||
16 | |||
17 | /** |
||
18 | * An object provided to interact with a Socrata dataset directly. Provides functionality for fetching the dataset, an |
||
19 | * individual row, or updating/replacing a dataset. |
||
20 | * |
||
21 | * @package allejo\Socrata |
||
22 | * @since 0.1.0 |
||
23 | */ |
||
24 | class SodaDataset |
||
25 | { |
||
26 | /** |
||
27 | * The client with all the authentication and configuration set |
||
28 | * |
||
29 | * @var SodaClient |
||
30 | */ |
||
31 | private $sodaClient; |
||
32 | |||
33 | /** |
||
34 | * The object used to make URL jobs for common requests |
||
35 | * |
||
36 | * @var UrlQuery |
||
37 | */ |
||
38 | private $urlQuery; |
||
39 | |||
40 | /** |
||
41 | * The 4x4 resource ID of a dataset |
||
42 | * |
||
43 | * @var string |
||
44 | */ |
||
45 | private $resourceId; |
||
46 | |||
47 | /** |
||
48 | * The API version of the dataset being worked with |
||
49 | * |
||
50 | * @var int |
||
51 | */ |
||
52 | private $apiVersion; |
||
53 | |||
54 | /** |
||
55 | * The API's cached metadata |
||
56 | * |
||
57 | * @var array |
||
58 | */ |
||
59 | private $metadata; |
||
60 | |||
61 | /** |
||
62 | * Create an object for interacting with a Socrata dataset |
||
63 | * |
||
64 | * @param SodaClient $sodaClient The SodaClient with all of the authentication information and settings for access |
||
65 | * @param string $resourceID The 4x4 resource ID of the dataset that will be referenced |
||
66 | * |
||
67 | * @throws InvalidResourceException If the given resource ID does not match the pattern of a resource ID |
||
68 | * |
||
69 | * @since 0.1.0 |
||
70 | */ |
||
71 | public function __construct ($sodaClient, $resourceID) |
||
72 | { |
||
73 | StringUtilities::validateResourceID($resourceID); |
||
74 | |||
75 | if (!($sodaClient instanceof SodaClient)) |
||
76 | { |
||
77 | throw new \InvalidArgumentException("The first variable is expected to be a SodaClient object"); |
||
78 | } |
||
79 | |||
80 | $this->apiVersion = 0; |
||
81 | $this->sodaClient = $sodaClient; |
||
82 | $this->resourceId = $resourceID; |
||
83 | $this->urlQuery = new UrlQuery($this->buildResourceUrl(), $this->sodaClient->getToken(), $this->sodaClient->getEmail(), $this->sodaClient->getPassword()); |
||
84 | |||
85 | $this->urlQuery->setOAuth2Token($this->sodaClient->getOAuth2Token()); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * Get the API version this dataset is using |
||
90 | * |
||
91 | * @since 0.1.0 |
||
92 | * |
||
93 | * @return double The API version number |
||
94 | */ |
||
95 | public function getApiVersion () |
||
96 | { |
||
97 | // If we don't have the API version set, send a dummy query with limit 0 since we only care about the headers |
||
98 | if ($this->apiVersion == 0) |
||
99 | { |
||
100 | $soql = new SoqlQuery(); |
||
101 | $soql->limit(0); |
||
102 | |||
103 | // When we fetch a dataset, the API version is stored |
||
104 | $this->getDataset($soql); |
||
105 | } |
||
106 | |||
107 | return $this->apiVersion; |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Get the metadata of a dataset |
||
112 | * |
||
113 | * @param bool $forceFetch Set to true if the cached metadata for the dataset is outdata or needs to be refreshed |
||
114 | * |
||
115 | * @see SodaClient::enableAssociativeArrays() |
||
116 | * @see SodaClient::disableAssociativeArrays() |
||
117 | * |
||
118 | * @since 0.1.0 |
||
119 | * |
||
120 | * @return array The metadata as a PHP array. The array will contain associative arrays or stdClass objects from |
||
121 | * the decoded JSON received from the data set. |
||
122 | */ |
||
123 | public function getMetadata ($forceFetch = false) |
||
124 | { |
||
125 | if (empty($this->metadata) || $forceFetch) |
||
126 | { |
||
127 | $metadataUrlQuery = new UrlQuery($this->buildViewUrl(), $this->sodaClient->getToken(), $this->sodaClient->getEmail(), $this->sodaClient->getPassword()); |
||
128 | $metadataUrlQuery->setOAuth2Token($this->sodaClient->getOAuth2Token()); |
||
129 | |||
130 | $this->metadata = $metadataUrlQuery->sendGet("", $this->sodaClient->associativeArrayEnabled()); |
||
131 | } |
||
132 | |||
133 | return $this->metadata; |
||
134 | } |
||
135 | |||
136 | /** |
||
137 | * Fetch a dataset based on a resource ID. |
||
138 | * |
||
139 | * @param string|SoqlQuery $filterOrSoqlQuery A simple filter or a SoqlQuery to filter the results |
||
140 | * |
||
141 | * @see SodaClient::enableAssociativeArrays() |
||
142 | * @see SodaClient::disableAssociativeArrays() |
||
143 | * |
||
144 | * @since 0.1.0 |
||
145 | * |
||
146 | * @return array The data set as a PHP array. The array will contain associative arrays or stdClass objects from |
||
147 | * the decoded JSON received from the data set. |
||
148 | */ |
||
149 | public function getDataset ($filterOrSoqlQuery = "") |
||
150 | { |
||
151 | $headers = array(); |
||
152 | |||
153 | if (!($filterOrSoqlQuery instanceof SoqlQuery) && StringUtilities::isNullOrEmpty($filterOrSoqlQuery)) |
||
154 | { |
||
155 | $filterOrSoqlQuery = new SoqlQuery(); |
||
156 | } |
||
157 | |||
158 | $dataset = $this->urlQuery->sendGet($filterOrSoqlQuery, $this->sodaClient->associativeArrayEnabled(), $headers); |
||
159 | |||
160 | $this->setApiVersion($headers); |
||
161 | |||
162 | return $dataset; |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * Delete an individual row based on their row identifier. For deleting more than a single row, use an upsert |
||
167 | * instead. |
||
168 | * |
||
169 | * @param int|string $rowID The row identifier of the row to fetch; if no identifier is set for the dataset, the |
||
170 | * internal row identifier should be used |
||
171 | * |
||
172 | * @link http://dev.socrata.com/publishers/direct-row-manipulation.html#deleting-a-row Deleting a Row |
||
173 | * |
||
174 | * @see SodaClient::enableAssociativeArrays() |
||
175 | * @see SodaClient::disableAssociativeArrays() |
||
176 | * @see upsert() |
||
177 | * |
||
178 | * @since 0.1.2 |
||
179 | * |
||
180 | * @return mixed An object with information about the deletion. The array will contain associative arrays or |
||
181 | * stdClass objects from the decoded JSON received from the data set. |
||
182 | */ |
||
183 | public function deleteRow ($rowID) |
||
184 | { |
||
185 | return $this->individualRow($rowID, "delete"); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Fetch an individual row from a dataset. |
||
190 | * |
||
191 | * @param int|string $rowID The row identifier of the row to fetch; if no identifier is set for the dataset, the |
||
192 | * internal row identifier should be used |
||
193 | * |
||
194 | * @link http://dev.socrata.com/publishers/direct-row-manipulation.html#retrieving-an-individual-row Retrieving |
||
195 | * An Individual Row |
||
196 | * |
||
197 | * @see SodaClient::enableAssociativeArrays() |
||
198 | * @see SodaClient::disableAssociativeArrays() |
||
199 | * |
||
200 | * @since 0.1.2 |
||
201 | * |
||
202 | * @return array The data set as a PHP array. The array will contain associative arrays or stdClass objects from |
||
203 | * the decoded JSON received from the data set. |
||
204 | */ |
||
205 | public function getRow ($rowID) |
||
206 | { |
||
207 | return $this->individualRow($rowID, "get"); |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * Replace the entire dataset with the new payload provided |
||
212 | * |
||
213 | * Data will always be transmitted as JSON to Socrata even though different forms are accepted. In order to pass |
||
214 | * other forms of data, you must use a Converter class that has a `toJson()` method, such as the CsvConverter. |
||
215 | * |
||
216 | * @param array|Converter|JSON $payload The data that will be upserted to the Socrata dataset as a PHP array, an |
||
217 | * instance of a Converter child class, or a JSON string |
||
218 | * |
||
219 | * @link http://dev.socrata.com/publishers/replace.html Replacing a dataset with Replace |
||
220 | * |
||
221 | * @see Converter |
||
222 | * @see CsvConverter |
||
223 | * |
||
224 | * @since 0.1.0 |
||
225 | * |
||
226 | * @return mixed |
||
227 | */ |
||
228 | public function replace ($payload) |
||
229 | { |
||
230 | $upsertData = $this->handleJson($payload); |
||
231 | |||
232 | return $this->urlQuery->sendPut($upsertData, $this->sodaClient->associativeArrayEnabled()); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Create, update, and delete rows in a single operation, using their row identifiers. |
||
237 | * |
||
238 | * Data will always be transmitted as JSON to Socrata even though different forms are accepted. In order to pass |
||
239 | * other forms of data, you must use a Converter class that has a `toJson()` method, such as the CsvConverter. |
||
240 | * |
||
241 | * @param array|Converter|JSON $payload The data that will be upserted to the Socrata dataset as a PHP array, an |
||
242 | * instance of a Converter child class, or a JSON string |
||
243 | * |
||
244 | * @link http://dev.socrata.com/publishers/upsert.html Updating Rows in Bulk with Upsert |
||
245 | * |
||
246 | * @see Converter |
||
247 | * @see CsvConverter |
||
248 | * |
||
249 | * @since 0.1.0 |
||
250 | * |
||
251 | * @return mixed |
||
252 | */ |
||
253 | public function upsert ($payload) |
||
254 | { |
||
255 | $upsertData = $this->handleJson($payload); |
||
256 | |||
257 | return $this->urlQuery->sendPost($upsertData, $this->sodaClient->associativeArrayEnabled()); |
||
258 | } |
||
259 | |||
260 | /** |
||
261 | * Build the API URL that will be used to access the dataset |
||
262 | * |
||
263 | * @return string The apt API URL |
||
264 | */ |
||
265 | private function buildResourceUrl () |
||
266 | { |
||
267 | return $this->buildApiUrl("resource"); |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * Build the API URL that will be used to access the metadata for the dataset |
||
272 | * |
||
273 | * @return string The apt API URL |
||
274 | */ |
||
275 | private function buildViewUrl () |
||
276 | { |
||
277 | return $this->buildApiUrl("views"); |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * Build the URL that will be used to access the API for the respective action |
||
282 | * |
||
283 | * @param string $location The location of where to get information from |
||
284 | * @param string|null $identifier The part of the URL that will end with .json. This will either be the resource |
||
285 | * ID or it will be a row ID prepended with the resource ID |
||
286 | * |
||
287 | * @return string The API URL |
||
288 | */ |
||
289 | private function buildApiUrl ($location, $identifier = NULL) |
||
290 | { |
||
291 | if ($identifier === NULL) |
||
292 | { |
||
293 | $identifier = $this->resourceId; |
||
294 | } |
||
295 | |||
296 | return sprintf("%s://%s/%s/%s.json", UrlQuery::DEFAULT_PROTOCOL, $this->sodaClient->getDomain(), $location, $identifier); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Handle different forms of data to be returned in JSON format so it can be sent to Socrata. |
||
301 | * |
||
302 | * Data will always be transmitted as JSON to Socrata even though different forms are accepted. |
||
303 | * |
||
304 | * @param array|Converter|JSON $payload The data that will be upserted to the Socrata dataset as a PHP array, an |
||
305 | * instance of a Converter child class, or a JSON string |
||
306 | * |
||
307 | * @return string A JSON encoded string available to be used for UrlQuery requsts |
||
0 ignored issues
–
show
|
|||
308 | */ |
||
309 | private function handleJson ($payload) |
||
310 | { |
||
311 | $uploadData = $payload; |
||
312 | |||
313 | if (is_array($payload)) |
||
314 | { |
||
315 | $uploadData = json_encode($payload); |
||
316 | } |
||
317 | else if ($payload instanceof Converter) |
||
318 | { |
||
319 | $uploadData = $payload->toJson(); |
||
320 | } |
||
321 | else if (!StringUtilities::isJson($payload)) |
||
322 | { |
||
323 | throw new \InvalidArgumentException("The given data is not valid JSON"); |
||
324 | } |
||
325 | |||
326 | return $uploadData; |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * Interact with an individual row. Either to retrieve it or to delete it; both actions use the same API endpoint |
||
331 | * with the exception of what type of request is sent. |
||
332 | * |
||
333 | * @param string $rowID The 4x4 resource ID of the dataset to work with |
||
334 | * @param string $method Either `get` or `delete` |
||
335 | * |
||
336 | * @return mixed |
||
337 | */ |
||
338 | private function individualRow ($rowID, $method) |
||
339 | { |
||
340 | $headers = array(); |
||
341 | |||
342 | // For a single row, the format is the `resourceID/rowID.json`, so we'll use that as the "location" of the Api URL |
||
343 | $apiEndPoint = $this->buildApiUrl("resource", $this->resourceId . "/" . $rowID); |
||
344 | |||
345 | $urlQuery = new UrlQuery($apiEndPoint, $this->sodaClient->getToken(), $this->sodaClient->getEmail(), $this->sodaClient->getPassword()); |
||
346 | $urlQuery->setOAuth2Token($this->sodaClient->getOAuth2Token()); |
||
347 | |||
348 | $result = $this->sendIndividualRequest($urlQuery, $method, $this->sodaClient->associativeArrayEnabled(), $headers); |
||
349 | |||
350 | $this->setApiVersion($headers); |
||
351 | |||
352 | return $result; |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * Send the appropriate request header based on the method that's required |
||
357 | * |
||
358 | * @param UrlQuery $urlQuery The object for the API endpoint |
||
359 | * @param string $method Either `get` or `delete` |
||
360 | * @param bool $associativeArrays Whether or not to return the information as an associative array |
||
361 | * @param array $headers An array with the cURL headers received |
||
362 | * |
||
363 | * @return mixed |
||
364 | */ |
||
365 | private function sendIndividualRequest ($urlQuery, $method, $associativeArrays, &$headers) |
||
366 | { |
||
367 | if ($method === "get") |
||
368 | { |
||
369 | return $urlQuery->sendGet("", $associativeArrays, $headers); |
||
370 | } |
||
371 | else if ($method === "delete") |
||
372 | { |
||
373 | return $urlQuery->sendDelete($associativeArrays, $headers); |
||
374 | } |
||
375 | |||
376 | throw new \InvalidArgumentException("Invalid request method"); |
||
377 | } |
||
378 | |||
379 | /** |
||
380 | * Determine and save the API version if it does not exist for easy access later |
||
381 | * |
||
382 | * @param array $headers An array with the cURL headers received |
||
383 | */ |
||
384 | private function setApiVersion ($headers) |
||
385 | { |
||
386 | // Only set the API version number if it hasn't been set yet |
||
387 | if ($this->apiVersion == 0) |
||
388 | { |
||
389 | $this->apiVersion = $this->parseApiVersion($headers); |
||
390 | } |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * Determine the version number of the API this dataset is using |
||
395 | * |
||
396 | * @param array $responseHeaders An array with the cURL headers received |
||
397 | * |
||
398 | * @return double The Socrata API version number this dataset uses |
||
399 | */ |
||
400 | private function parseApiVersion ($responseHeaders) |
||
401 | { |
||
402 | // A header that's unique to the legacy API |
||
403 | if (array_key_exists('X-SODA2-Legacy-Types', $responseHeaders) && $responseHeaders['X-SODA2-Legacy-Types']) |
||
404 | { |
||
405 | return 1; |
||
406 | } |
||
407 | |||
408 | // A header that's unique to the new API |
||
409 | if (array_key_exists('X-SODA2-Truth-Last-Modified', $responseHeaders)) |
||
410 | { |
||
411 | if (empty($this->metadata)) |
||
412 | { |
||
413 | $this->getMetadata(); |
||
414 | } |
||
415 | |||
416 | if ($this->metadata['newBackend']) |
||
417 | { |
||
418 | return 2.1; |
||
419 | } |
||
420 | |||
421 | return 2; |
||
422 | } |
||
423 | |||
424 | return 0; |
||
425 | } |
||
426 | } |
||
427 |
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.If the return type contains the type array, this check recommends the use of a more specific type like
String[]
orarray<String>
.