Completed
Push — develop ( afc6a9...c6c8f7 )
by Vladimir
02:59
created

ApiObject::_markInvalid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 1
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * This file contains the ApiObject class
5
 *
6
 * @copyright 2015 Vladimir Jimenez
7
 * @license   https://github.com/allejo/PhpPulse/blob/master/LICENSE.md MIT
8
 */
9
10
namespace allejo\DaPulse\Objects;
11
12
use allejo\DaPulse\Exceptions\InvalidObjectException;
13
use allejo\DaPulse\Utilities\UrlQuery;
14
15
/**
16
 * The base class for all DaPulse API objects
17
 *
18
 * @internal
19
 * @package allejo\DaPulse\Objects
20
 * @since   0.1.0
21
 */
22
abstract class ApiObject implements \JsonSerializable
23
{
24
    /**
25
     * The namespace used for all main PhpPulse objects. This is value is prepended before PhpPulse objects when being
26
     * checked with `instanceof`.
27
     *
28
     * @internal
29
     */
30
    const OBJ_NAMESPACE = "\\allejo\\DaPulse\\";
31
32
    /**
33
     * The default API protocol used for URL calls
34
     *
35
     * @internal
36
     */
37
    const API_PROTOCOL = "https";
38
39
    /**
40
     * The API end point for URL calls
41
     *
42
     * @internal
43
     */
44
    const API_ENDPOINT = "api.dapulse.com";
45
46
    /**
47
     * The API version used for URL calls
48
     *
49
     * @internal
50
     */
51
    const API_VERSION = "v1";
52
53
    /**
54
     * The suffix that is appended to the URL to access functionality for certain objects
55
     *
56
     * @internal
57
     */
58
    const API_PREFIX = "";
59
60
    /**
61
     * The API key used to make the URL calls
62
     *
63
     * @var string
64
     */
65
    protected static $apiKey;
66
67
    /**
68
     * When set to true, the object can only be constructed from an associative array of data. It will not attempt
69
     * to fetch the data with an API call; this is intended for objects are not directly accessible via the API.
70
     *
71
     * @var bool
72
     */
73
    protected $arrayConstructionOnly = false;
74
75
    /**
76
     * Set to true if the object has been deleted via an API call but the instance still exists. This variable will
77
     * prevent further API calls to a nonexistent object.
78
     *
79
     * @var bool
80
     */
81
    protected $deletedObject = false;
82
83
    /**
84
     * An associative array representing the original JSON response from DaPulse
85
     *
86
     * @var array
87
     */
88
    protected $jsonResponse;
89
90
    /**
91
     * Create an object from an API call
92
     *
93
     * @param int|array $idOrArray Either the numerical ID of an object or an associative array representing a JSON
94
     *                             response from an API call
95
     *
96
     * @throw \InvalidArgumentException The specified object cannot be created directly from an API call but instead
97
     *                                  requires an associative array of information gathered from other API calls.
98
     *
99
     * @since 0.1.0
100
     */
101 70
    public function __construct ($idOrArray)
102
    {
103 70
        $urlEndPoint = "";
104 70
        $staticClass = explode("\\", get_called_class());
105 70
        $staticClass = end($staticClass);
106
107 70
        if (is_null($idOrArray))
108
        {
109
            throw new \InvalidArgumentException("You may not initialize " . $staticClass . " with null.");
110
        }
111
112 70
        if (!is_array($idOrArray))
113
        {
114 70
            $urlEndPoint = sprintf("%s/%d.json", self::apiEndpoint(), $idOrArray);
115
        }
116
117 70
        if ($this->arrayConstructionOnly && !is_array($idOrArray))
118
        {
119
            throw new \InvalidArgumentException("A " . $staticClass . " cannot be fetched from an ID.");
120
        }
121
122 70
        $this->jsonResponse = (is_array($idOrArray)) ? $idOrArray : $this::sendGet($urlEndPoint);
0 ignored issues
show
Documentation Bug introduced by
It seems like is_array($idOrArray) ? $...::sendGet($urlEndPoint) of type * is incompatible with the declared type array of property $jsonResponse.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
123
124 70
        $this->initializeValues();
125 70
        $this->assignResults();
126 70
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    public function jsonSerialize()
132
    {
133
        return $this->jsonResponse;
134
    }
135
136
    // ================================================================================================================
137
    //   Getter functions
138
    // ================================================================================================================
139
140
    /**
141
     * Access the JSON response from DaPulse directly
142
     *
143
     * @api
144
     * @deprecated 0.3.0 Feed this object to json_encode() to get the JSON representation of this object instead or call
145
     *                   `jsonSerialize()`
146
     * @since  0.1.0
147
     * @todo   Remove at 0.4.0 or next breaking release
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
148
     * @return array
149
     */
150
    final public function getJson ()
151
    {
152
        return $this->jsonSerialize();
153
    }
154
155
    // ================================================================================================================
156
    //   Helper functions
157
    // ================================================================================================================
158
159
    /**
160
     * Assign an associative array from a JSON response and map them to instance variables
161
     *
162
     * @since 0.1.0
163
     */
164 70
    final protected function assignResults ()
165
    {
166 70
        foreach ($this->jsonResponse as $key => $val)
167
        {
168 70
            if (property_exists(get_called_class(), $key))
169
            {
170 70
                $this->$key = $val;
171
            }
172
        }
173 70
    }
174
175
    /**
176
     * Check if the current object has been marked as deleted from DaPulse. If so, throw an exception.
177
     *
178
     * @throws InvalidObjectException
179
     */
180
    final protected function checkInvalid ()
181
    {
182
        if ($this->deletedObject)
183
        {
184
            throw new InvalidObjectException("This object no longer exists on DaPulse", 2);
185
        }
186
    }
187
188
    /**
189
     * Mark an object as deleted
190
     *
191
     * @internal
192
     */
193
    final public function _markInvalid ()
194
    {
195
        $this->deletedObject = true;
196
    }
197
198
    /**
199
     * Store the value in an array if the value is not null. This function is a shortcut of setting values in an array
200
     * only if they are not null, if not leave them unset; used ideally for PUT requests.
201
     *
202
     * @param array  $array The array that will store all of the POST parameters
203
     * @param string $name  The name of the field
204
     * @param mixed  $value The value to be stored in a given field
205
     */
206
    final protected static function setIfNotNullOrEmpty (&$array, $name, $value)
207
    {
208
        if (!is_null($value) || !empty($value))
209
        {
210
            $array[$name] = $value;
211 63
        }
212
    }
213 63
214
    // ================================================================================================================
215
    //   Empty functions
216
    // ================================================================================================================
217
218
    /**
219
     * Overload this function if any class variables need to be initialized to a default value
220
     */
221
    protected function initializeValues ()
222
    {
223
    }
224
225
    // ================================================================================================================
226
    //   Lazy loading functions
227
    // ================================================================================================================
228
229
    /**
230
     * Inject data into the array that will be mapped into individual instance variables. This function must be called
231 15
     * **before** lazyCastAll() is called and maps the associative array to objects.
232
     *
233 15
     * @param array $target An array of associative arrays with data to be converted into objects
234
     * @param array $array  An associative array containing data to be merged with the key being the name of the
235
     *                      instance variable.
236
     *
237
     * @since 0.1.0
238
     *
239 15
     * @throw \InvalidArgumentException If either parameters are not arrays
240
     */
241 15
    final protected static function lazyInject (&$target, $array)
242
    {
243 15
        if (!is_array($target) || !is_array($array))
244
        {
245
            throw new \InvalidArgumentException("Both the target and array must be arrays");
246 15
        }
247
248
        // If the first element is an array, let's assume $target hasn't been lazily casted into objects
249
        if (is_array($target[0]))
250
        {
251
            foreach ($target as &$element)
252
            {
253
                $element = array_merge($element, $array);
254
            }
255
        }
256 18
    }
257
258 18
    /**
259
     * Convert the specified array into an array of object types if needed
260 17
     *
261
     * @param  string $objectType The class name of the Objects the items should be
262 18
     * @param  array  $array      The array to check
263
     *
264
     * @since  0.2.0
265
     */
266
    final protected static function lazyCastAll (&$array, $objectType)
267
    {
268
        if (self::lazyCastNeededOnArray($objectType, $array))
269
        {
270
            $array = self::castArrayToObjectArray($objectType, $array);
271
        }
272 4
    }
273
274 4
    /**
275
     * Convert the specified item into the specified object if needed
276 4
     *
277 4
     * @param mixed  $target     The item to check
278
     * @param string $objectType The class name of the Objects the items should be
279 4
     *
280
     * @since 0.2.0
281
     */
282
    final protected static function lazyCast (&$target, $objectType)
283
    {
284
        if (self::lazyCastNeeded($target, $objectType))
285
        {
286
            $object = ($objectType[0] == "\\") ? $objectType : self::OBJ_NAMESPACE . $objectType;
287
            $target = new $object($target);
288
        }
289
    }
290
291 18
    /**
292
     * Check whether it is required for an array of JSON data to be converted into an array of the specified objects
293 18
     *
294
     * @param  string $objectType The class name of the Objects the items should be
295 17
     * @param  array  $array      The array to check
296
     *
297 17
     * @since  0.2.0
298
     *
299
     * @return bool True if the array needs to converted into an array of objects
300
     */
301
    final protected static function lazyCastNeededOnArray ($objectType, $array)
302
    {
303
        if (is_array($array) && count($array) == 0) { return false; }
304
305
        $firstItem = $array[0];
306
307
        return self::lazyCastNeeded($firstItem, $objectType);
308
    }
309
310 21
    /**
311
     * Check if an individual item needs to be lazily converted into an object
312 21
     *
313
     * @param  mixed  $target     The item to check
314 21
     * @param  string $objectType The class name of the Objects the items should be
315
     *
316
     * @since  0.2.0
317
     *
318
     * @return bool
319
     */
320
    final protected static function lazyCastNeeded ($target, $objectType)
321
    {
322
        $objectDefinition = ($objectType[0] === "\\") ? $objectType : self::OBJ_NAMESPACE . $objectType;
323
324
        return !($target instanceof $objectDefinition);
325
    }
326
327
    /**
328
     * Sends a GET request for a JSON array and casts the response into an array of objects
329
     *
330 20
     * @param  string $url       The API endpoint to call to get the JSON response from
331
     * @param  string $className The class name of the Object type to cast to
332 20
     * @param  array  $params    An associative array of URL parameters that will be passed to the specific call. For
333
     *                           example, limiting the number of results or the pagination of results. **Warning** The
334 20
     *                           API key does NOT need to be passed here
335
     *
336
     * @since  0.2.0
337
     *
338
     * @return array
339
     */
340
    final protected static function fetchAndCastToObjectArray ($url, $className, $params = array())
341
    {
342
        $objects = self::sendGet($url, $params);
343
344
        return self::castArrayToObjectArray($className, $objects);
345
    }
346
347 35
    /**
348
     * Convert an array of associative arrays into a specific object
349 35
     *
350 35
     * @param  string $className The class name of the Object type
351
     * @param  array  $objects   An associative array to be converted into an object
352 35
     *
353
     * @since  0.2.0
354 35
     *
355
     * @return array An array of the specified objects
356
     */
357 35
    final protected static function castArrayToObjectArray ($className, $objects)
358
    {
359
        $class = self::OBJ_NAMESPACE . $className;
360
        $array = array();
361
362
        foreach ($objects as $post)
363
        {
364
            $array[] = new $class($post);
365
        }
366
367
        return $array;
368
    }
369
370
    // ================================================================================================================
371
    //   URL jobs functions
372
    // ================================================================================================================
373
374
    /**
375
     * Send a GET request to fetch the data from the specified URL
376 70
     *
377
     * @param  string $url    The API endpoint to call
378 70
     * @param  array  $params An associative array of URL parameters that will be passed to the specific call. For
379
     *                        example, limiting the number of results or the pagination of results. **Warning** The API
380 70
     *                        key does NOT need to be passed here
381
     *
382 70
     * @since  0.1.0
383
     *
384
     * @return mixed          An associative array of the JSON response from DaPulse
385
     */
386
    final protected static function sendGet ($url, $params = array())
387
    {
388
        $params["api_key"] = self::$apiKey;
389
390
        $urlQuery = new UrlQuery($url, $params);
391
392
        return $urlQuery->sendGet();
393
    }
394
395
    /**
396 3
     * Send a POST request to a specified URL
397
     *
398 3
     * @param  string $url
399
     * @param  array  $postParams
400
     * @param  array  $getParams
401
     *
402
     * @since  0.1.0
403
     *
404
     * @return mixed
405
     */
406
    final protected static function sendPost ($url, $postParams, $getParams = array())
407
    {
408
        return self::sendRequest("POST", $url, $postParams, $getParams);
409
    }
410
411
    /**
412 7
     * Send a PUT request to a specified URL
413
     *
414 7
     * @param  string $url
415
     * @param  array  $postParams
416
     * @param  array  $getParams
417
     *
418
     * @since  0.1.0
419
     *
420
     * @return mixed
421
     */
422
    final protected static function sendPut ($url, $postParams, $getParams = array())
423
    {
424
        return self::sendRequest("PUT", $url, $postParams, $getParams);
425
    }
426
427
    /**
428
     * Send a DELETE request to a specified URL
429
     *
430
     * @param  string $url
431
     * @param  array  $getParams
432
     *
433
     * @since  0.1.0
434
     *
435
     * @return mixed
436
     */
437
    final protected static function sendDelete ($url, $getParams = array())
438
    {
439
        return self::sendRequest("DELETE", $url, NULL, $getParams);
0 ignored issues
show
Documentation introduced by
NULL is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
440
    }
441
442
    /**
443
     * Send the appropriate URL request
444 10
     *
445
     * @param  string $type
446 10
     * @param  string $url
447
     * @param  array  $postParams
448 10
     * @param  array  $getParams
449
     *
450
     * @since  0.1.0
451
     *
452 10
     * @return mixed
453 3
     */
454
    private static function sendRequest ($type, $url, $postParams, $getParams)
455 7
    {
456 7
        $getParams["api_key"] = self::$apiKey;
457
458
        $urlQuery = new UrlQuery($url, $getParams);
459
460
        switch ($type)
461
        {
462
            case "POST":
463
                return $urlQuery->sendPost($postParams);
464
465
            case "PUT":
466
                return $urlQuery->sendPut($postParams);
467
468
            case "DELETE":
469
                return $urlQuery->sendDelete();
470
471
            default:
472
                throw new \InvalidArgumentException();
473
        }
474
    }
475
476
    // ================================================================================================================
477
    //   API key functions
478
    // ================================================================================================================
479
480 70
    /**
481
     * Get the base URL to use in all of the API calls
482 70
     *
483
     * @param  string|null $apiPrefix If the API end point is different from the class's constant, this value will be
484 70
     *                                used as the suffix for the API endpoint
485
     *
486
     * @since  0.1.0
487
     *
488
     * @return string The base URL to call
489
     */
490
    final protected static function apiEndpoint ($apiPrefix = NULL)
491
    {
492
        $apiSection = isset($apiPrefix) ? $apiPrefix : static::API_PREFIX;
493
494 70
        return sprintf("%s://%s/%s/%s", self::API_PROTOCOL, self::API_ENDPOINT, self::API_VERSION, $apiSection);
495
    }
496 70
497 70
    /**
498
     * Set the API for all calls to the API
499
     *
500
     * @param string $apiKey The API key used to access the DaPulse API
501
     *
502
     * @since 0.1.0
503
     */
504
    final public static function setApiKey ($apiKey)
505
    {
506
        self::$apiKey = $apiKey;
507
    }
508
}
509