Completed
Push — develop ( a05ff5...d1fb89 )
by Vladimir
03:27
created

ApiObject::lazyInject()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5.0729

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 16
ccs 6
cts 7
cp 0.8571
rs 8.8571
cc 5
eloc 6
nc 4
nop 2
crap 5.0729
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 46
    public function __construct ($idOrArray)
102
    {
103 46
        $urlEndPoint = "";
104 46
        $staticClass = explode("\\", get_called_class());
105 46
        $staticClass = end($staticClass);
106
107 46
        if (is_null($idOrArray))
108
        {
109
            throw new \InvalidArgumentException("You may not initialize " . $staticClass . " with null.");
110
        }
111
112 46
        if (!is_array($idOrArray))
113
        {
114 46
            $urlEndPoint = sprintf("%s/%d.json", self::apiEndpoint(), $idOrArray);
115
        }
116
117 46
        if ($this->arrayConstructionOnly && !is_array($idOrArray))
118
        {
119
            throw new \InvalidArgumentException("A " . $staticClass . " cannot be fetched from an ID.");
120
        }
121
122 46
        $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 46
        $this->initializeValues();
125 46
        $this->assignResults();
126 46
    }
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
145
     * @since  0.1.0
146
     * @todo   Remove this in the next major 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...
147
     * @return array
148
     */
149
    final public function getJson ()
150
    {
151
        return $this->jsonSerialize();
152
    }
153
154
    // ================================================================================================================
155
    //   Helper functions
156
    // ================================================================================================================
157
158
    /**
159
     * Assign an associative array from a JSON response and map them to instance variables
160
     *
161
     * @since 0.1.0
162
     */
163 46
    final protected function assignResults ()
164
    {
165 46
        foreach ($this->jsonResponse as $key => $val)
166
        {
167 46
            if (property_exists(get_called_class(), $key))
168
            {
169 46
                $this->$key = $val;
170
            }
171
        }
172 46
    }
173
174
    /**
175
     * Check if the current object has been marked as deleted from DaPulse. If so, throw an exception.
176
     *
177
     * @throws InvalidObjectException
178
     */
179
    final protected function checkInvalid ()
180
    {
181
        if ($this->deletedObject)
182
        {
183
            throw new InvalidObjectException("This object no longer exists on DaPulse", 2);
184
        }
185
    }
186
187
    /**
188
     * Store the value in an array if the value is not null. This function is a shortcut of setting values in an array
189
     * only if they are not null, if not leave them unset; used ideally for PUT requests.
190
     *
191
     * @param array  $array The array that will store all of the POST parameters
192
     * @param string $name  The name of the field
193
     * @param mixed  $value The value to be stored in a given field
194
     */
195
    final protected static function setIfNotNullOrEmpty (&$array, $name, $value)
196
    {
197
        if (!is_null($value) || !empty($value))
198
        {
199
            $array[$name] = $value;
200
        }
201
    }
202
203
    // ================================================================================================================
204
    //   Empty functions
205
    // ================================================================================================================
206
207
    /**
208
     * Overload this function if any class variables need to be initialized to a default value
209
     */
210 39
    protected function initializeValues ()
211
    {
212 39
    }
213
214
    // ================================================================================================================
215
    //   Lazy loading functions
216
    // ================================================================================================================
217
218
    /**
219
     * Inject data into the array that will be mapped into individual instance variables. This function must be called
220
     * **before** lazyCastAll() is called and maps the associative array to objects.
221
     *
222
     * @param array $target An array of associative arrays with data to be converted into objects
223
     * @param array $array  An associative array containing data to be merged with the key being the name of the
224
     *                      instance variable.
225
     *
226
     * @since 0.1.0
227
     *
228
     * @throw \InvalidArgumentException If either parameters are not arrays
229
     */
230 15
    final protected static function lazyInject (&$target, $array)
231
    {
232 15
        if (!is_array($target) || !is_array($array))
233
        {
234
            throw new \InvalidArgumentException("Both the target and array must be arrays");
235
        }
236
237
        // If the first element is an array, let's assume $target hasn't been lazily casted into objects
238 15
        if (is_array($target[0]))
239
        {
240 15
            foreach ($target as &$element)
241
            {
242 15
                $element = array_merge($element, $array);
243
            }
244
        }
245 15
    }
246
247
    /**
248
     * Convert the specified array into an array of object types if needed
249
     *
250
     * @param  string $objectType The class name of the Objects the items should be
251
     * @param  array  $array      The array to check
252
     *
253
     * @since  0.2.0
254
     */
255 18
    final protected static function lazyCastAll (&$array, $objectType)
256
    {
257 18
        if (self::lazyCastNeededOnArray($objectType, $array))
258
        {
259 17
            $array = self::castArrayToObjectArray($objectType, $array);
260
        }
261 18
    }
262
263
    /**
264
     * Convert the specified item into the specified object if needed
265
     *
266
     * @param mixed  $target     The item to check
267
     * @param string $objectType The class name of the Objects the items should be
268
     *
269
     * @since 0.2.0
270
     */
271 4
    final protected static function lazyCast (&$target, $objectType)
272
    {
273 4
        if (self::lazyCastNeeded($target, $objectType))
274
        {
275 4
            $object = ($objectType[0] == "\\") ? $objectType : self::OBJ_NAMESPACE . $objectType;
276 4
            $target = new $object($target);
277
        }
278 4
    }
279
280
    /**
281
     * Check whether it is required for an array of JSON data to be converted into an array of the specified objects
282
     *
283
     * @param  string $objectType The class name of the Objects the items should be
284
     * @param  array  $array      The array to check
285
     *
286
     * @since  0.2.0
287
     *
288
     * @return bool True if the array needs to converted into an array of objects
289
     */
290 18
    final protected static function lazyCastNeededOnArray ($objectType, $array)
291
    {
292 18
        if (is_array($array) && count($array) == 0) { return false; }
293
294 17
        $firstItem = $array[0];
295
296 17
        return self::lazyCastNeeded($firstItem, $objectType);
297
    }
298
299
    /**
300
     * Check if an individual item needs to be lazily converted into an object
301
     *
302
     * @param  mixed  $target     The item to check
303
     * @param  string $objectType The class name of the Objects the items should be
304
     *
305
     * @since  0.2.0
306
     *
307
     * @return bool
308
     */
309 21
    final protected static function lazyCastNeeded ($target, $objectType)
310
    {
311 21
        $objectDefinition = ($objectType[0] === "\\") ? $objectType : self::OBJ_NAMESPACE . $objectType;
312
313 21
        return !($target instanceof $objectDefinition);
314
    }
315
316
    /**
317
     * Sends a GET request for a JSON array and casts the response into an array of objects
318
     *
319
     * @param  string $url       The API endpoint to call to get the JSON response from
320
     * @param  string $className The class name of the Object type to cast to
321
     * @param  array  $params    An associative array of URL parameters that will be passed to the specific call. For
322
     *                           example, limiting the number of results or the pagination of results. **Warning** The
323
     *                           API key does NOT need to be passed here
324
     *
325
     * @since  0.2.0
326
     *
327
     * @return array
328
     */
329 13
    final protected static function fetchAndCastToObjectArray ($url, $className, $params = array())
330
    {
331 13
        $objects = self::sendGet($url, $params);
332
333 13
        return self::castArrayToObjectArray($className, $objects);
334
    }
335
336
    /**
337
     * Convert an array of associative arrays into a specific object
338
     *
339
     * @param  string $className The class name of the Object type
340
     * @param  array  $objects   An associative array to be converted into an object
341
     *
342
     * @since  0.2.0
343
     *
344
     * @return array An array of the specified objects
345
     */
346 28
    final protected static function castArrayToObjectArray ($className, $objects)
347
    {
348 28
        $class = self::OBJ_NAMESPACE . $className;
349 28
        $array = array();
350
351 28
        foreach ($objects as $post)
352
        {
353 28
            $array[] = new $class($post);
354
        }
355
356 28
        return $array;
357
    }
358
359
    // ================================================================================================================
360
    //   URL jobs functions
361
    // ================================================================================================================
362
363
    /**
364
     * Send a GET request to fetch the data from the specified URL
365
     *
366
     * @param  string $url    The API endpoint to call
367
     * @param  array  $params An associative array of URL parameters that will be passed to the specific call. For
368
     *                        example, limiting the number of results or the pagination of results. **Warning** The API
369
     *                        key does NOT need to be passed here
370
     *
371
     * @since  0.1.0
372
     *
373
     * @return mixed          An associative array of the JSON response from DaPulse
374
     */
375 46
    final protected static function sendGet ($url, $params = array())
376
    {
377 46
        $params["api_key"] = self::$apiKey;
378
379 46
        $urlQuery = new UrlQuery($url, $params);
380
381 46
        return $urlQuery->sendGet();
382
    }
383
384
    /**
385
     * Send a POST request to a specified URL
386
     *
387
     * @param  string $url
388
     * @param  array  $postParams
389
     * @param  array  $getParams
390
     *
391
     * @since  0.1.0
392
     *
393
     * @return mixed
394
     */
395
    final protected static function sendPost ($url, $postParams, $getParams = array())
396
    {
397
        return self::sendRequest("POST", $url, $postParams, $getParams);
398
    }
399
400
    /**
401
     * Send a PUT request to a specified URL
402
     *
403
     * @param  string $url
404
     * @param  array  $postParams
405
     * @param  array  $getParams
406
     *
407
     * @since  0.1.0
408
     *
409
     * @return mixed
410
     */
411 7
    final protected static function sendPut ($url, $postParams, $getParams = array())
412
    {
413 7
        return self::sendRequest("PUT", $url, $postParams, $getParams);
414
    }
415
416
    /**
417
     * Send a DELETE request to a specified URL
418
     *
419
     * @param  string $url
420
     * @param  array  $getParams
421
     *
422
     * @since  0.1.0
423
     *
424
     * @return mixed
425
     */
426
    final protected static function sendDelete ($url, $getParams = array())
427
    {
428
        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...
429
    }
430
431
    /**
432
     * Send the appropriate URL request
433
     *
434
     * @param  string $type
435
     * @param  string $url
436
     * @param  array  $postParams
437
     * @param  array  $getParams
438
     *
439
     * @since  0.1.0
440
     *
441
     * @return mixed
442
     */
443 7
    private static function sendRequest ($type, $url, $postParams, $getParams)
444
    {
445 7
        $getParams["api_key"] = self::$apiKey;
446
447 7
        $urlQuery = new UrlQuery($url, $getParams);
448
449
        switch ($type)
450
        {
451 7
            case "POST":
452
                return $urlQuery->sendPost($postParams);
453
454 7
            case "PUT":
455 7
                return $urlQuery->sendPut($postParams);
456
457
            case "DELETE":
458
                return $urlQuery->sendDelete();
459
460
            default:
461
                throw new \InvalidArgumentException();
462
        }
463
    }
464
465
    // ================================================================================================================
466
    //   API key functions
467
    // ================================================================================================================
468
469
    /**
470
     * Get the base URL to use in all of the API calls
471
     *
472
     * @param  string|null $apiPrefix If the API end point is different from the class's constant, this value will be
473
     *                                used as the suffix for the API endpoint
474
     *
475
     * @since  0.1.0
476
     *
477
     * @return string The base URL to call
478
     */
479 46
    final protected static function apiEndpoint ($apiPrefix = NULL)
480
    {
481 46
        $apiSection = isset($apiPrefix) ? $apiPrefix : static::API_PREFIX;
482
483 46
        return sprintf("%s://%s/%s/%s", self::API_PROTOCOL, self::API_ENDPOINT, self::API_VERSION, $apiSection);
484
    }
485
486
    /**
487
     * Set the API for all calls to the API
488
     *
489
     * @param string $apiKey The API key used to access the DaPulse API
490
     *
491
     * @since 0.1.0
492
     */
493 46
    final public static function setApiKey ($apiKey)
494
    {
495 46
        self::$apiKey = $apiKey;
496 46
    }
497
}
498