Browse code

Added README.md appinfo/info.xml appinfo/signature.json lib/Controller/AuthorApiController.php and the providers directory

DoubleBastionAdmin authored on 20/08/2022 16:33:00
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,523 @@
1
+<?php
2
+
3
+namespace Telnyx;
4
+
5
+/**
6
+ * Class TelnyxObject
7
+ *
8
+ * @package Telnyx
9
+ */
10
+class TelnyxObject implements \ArrayAccess, \Countable, \JsonSerializable
11
+{
12
+    protected $_opts;
13
+    protected $_originalValues;
14
+    protected $_values;
15
+    protected $_unsavedValues;
16
+    protected $_transientValues;
17
+    protected $_retrieveOptions;
18
+    protected $_lastResponse;
19
+
20
+    /**
21
+     * @return Util\Set Attributes that should not be sent to the API because
22
+     *    they're not updatable (e.g. ID).
23
+     */
24
+    public static function getPermanentAttributes()
25
+    {
26
+        static $permanentAttributes = null;
27
+        if ($permanentAttributes === null) {
28
+            $permanentAttributes = new Util\Set([
29
+                'id',
30
+            ]);
31
+        }
32
+        return $permanentAttributes;
33
+    }
34
+
35
+    /**
36
+     * Additive objects are subobjects in the API that don't have the same
37
+     * semantics as most subobjects, which are fully replaced when they're set.
38
+     * This is best illustrated by example. The `source` parameter sent when
39
+     * updating a subscription is *not* additive; if we set it:
40
+     *
41
+     *     source[object]=card&source[number]=123
42
+     *
43
+     * We expect the old `source` object to have been overwritten completely. If
44
+     * the previous source had an `address_state` key associated with it and we
45
+     * didn't send one this time, that value of `address_state` is gone.
46
+     *
47
+     * By contrast, additive objects are those that will have new data added to
48
+     * them while keeping any existing data in place. The only known case of its
49
+     * use is for `metadata`, but it could in theory be more general. As an
50
+     * example, say we have a `metadata` object that looks like this on the
51
+     * server side:
52
+     *
53
+     *     metadata = ["old" => "old_value"]
54
+     *
55
+     * If we update the object with `metadata[new]=new_value`, the server side
56
+     * object now has *both* fields:
57
+     *
58
+     *     metadata = ["old" => "old_value", "new" => "new_value"]
59
+     *
60
+     * This is okay in itself because usually users will want to treat it as
61
+     * additive:
62
+     *
63
+     *     $obj->metadata["new"] = "new_value";
64
+     *     $obj->save();
65
+     *
66
+     * However, in other cases, they may want to replace the entire existing
67
+     * contents:
68
+     *
69
+     *     $obj->metadata = ["new" => "new_value"];
70
+     *     $obj->save();
71
+     *
72
+     * This is where things get a little bit tricky because in order to clear
73
+     * any old keys that may have existed, we actually have to send an explicit
74
+     * empty string to the server. So the operation above would have to send
75
+     * this form to get the intended behavior:
76
+     *
77
+     *     metadata[old]=&metadata[new]=new_value
78
+     *
79
+     * This method allows us to track which parameters are considered additive,
80
+     * and lets us behave correctly where appropriate when serializing
81
+     * parameters to be sent.
82
+     *
83
+     * @return Util\Set Set of additive parameters
84
+     */
85
+    public static function getAdditiveParams()
86
+    {
87
+        static $additiveParams = null;
88
+        if ($additiveParams === null) {
89
+            // Set `metadata` as additive so that when it's set directly we remember
90
+            // to clear keys that may have been previously set by sending empty
91
+            // values for them.
92
+            //
93
+            // It's possible that not every object has `metadata`, but having this
94
+            // option set when there is no `metadata` field is not harmful.
95
+            $additiveParams = new Util\Set([
96
+                'metadata',
97
+            ]);
98
+        }
99
+        return $additiveParams;
100
+    }
101
+
102
+    public function __construct($id = null, $opts = null)
103
+    {
104
+        list($id, $this->_retrieveOptions) = Util\Util::normalizeId($id);
105
+        $this->_opts = Util\RequestOptions::parse($opts);
106
+        $this->_originalValues = [];
107
+        $this->_values = [];
108
+        $this->_unsavedValues = new Util\Set();
109
+        $this->_transientValues = new Util\Set();
110
+        if ($id !== null) {
111
+            $this->_values['id'] = $id;
112
+        }
113
+    }
114
+
115
+    // Standard accessor magic methods
116
+    public function __set($k, $v)
117
+    {
118
+        if (static::getPermanentAttributes()->includes($k)) {
119
+            throw new \InvalidArgumentException(
120
+                "Cannot set $k on this object. HINT: you can't set: " .
121
+                join(', ', static::getPermanentAttributes()->toArray())
122
+            );
123
+        }
124
+
125
+        if ($v === "") {
126
+            throw new \InvalidArgumentException(
127
+                'You cannot set \''.$k.'\'to an empty string. '
128
+                .'We interpret empty strings as NULL in requests. '
129
+                .'You may set obj->'.$k.' = NULL to delete the property'
130
+            );
131
+        }
132
+
133
+        $this->_values[$k] = Util\Util::convertToTelnyxObject($v, $this->_opts);
134
+        $this->dirtyValue($this->_values[$k]);
135
+        $this->_unsavedValues->add($k);
136
+    }
137
+
138
+    public function __isset($k)
139
+    {
140
+        return isset($this->_values[$k]);
141
+    }
142
+
143
+    public function __unset($k)
144
+    {
145
+        unset($this->_values[$k]);
146
+        $this->_transientValues->add($k);
147
+        $this->_unsavedValues->discard($k);
148
+    }
149
+
150
+    public function &__get($k)
151
+    {
152
+        // function should return a reference, using $nullval to return a reference to null
153
+        $nullval = null;
154
+        if (!empty($this->_values) && array_key_exists($k, $this->_values)) {
155
+            return $this->_values[$k];
156
+        } elseif (!empty($this->_transientValues) && $this->_transientValues->includes($k)) {
157
+            $class = get_class($this);
158
+            $attrs = join(', ', array_keys($this->_values));
159
+            $message = "Telnyx Notice: Undefined property of $class instance: $k. "
160
+                    . "HINT: The $k attribute was set in the past, however. "
161
+                    . "It was then wiped when refreshing the object "
162
+                    . "with the result returned by Telnyx's API, "
163
+                    . "probably as a result of a save(). The attributes currently "
164
+                    . "available on this object are: $attrs";
165
+            Telnyx::getLogger()->error($message);
166
+            return $nullval;
167
+        } else {
168
+            $class = get_class($this);
169
+            Telnyx::getLogger()->error("Telnyx Notice: Undefined property of $class instance: $k");
170
+            return $nullval;
171
+        }
172
+    }
173
+
174
+    // Magic method for var_dump output. Only works with PHP >= 5.6
175
+    public function __debugInfo()
176
+    {
177
+        return $this->_values;
178
+    }
179
+
180
+    // ArrayAccess methods
181
+    public function offsetSet($k, $v)
182
+    {
183
+        $this->$k = $v;
184
+    }
185
+
186
+    public function offsetExists($k)
187
+    {
188
+        return array_key_exists($k, $this->_values);
189
+    }
190
+
191
+    public function offsetUnset($k)
192
+    {
193
+        unset($this->$k);
194
+    }
195
+
196
+    public function offsetGet($k)
197
+    {
198
+        return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
199
+    }
200
+
201
+    // Countable method
202
+    public function count()
203
+    {
204
+        return count($this->_values);
205
+    }
206
+
207
+    public function keys()
208
+    {
209
+        return array_keys($this->_values);
210
+    }
211
+
212
+    public function values()
213
+    {
214
+        return array_values($this->_values);
215
+    }
216
+
217
+    /**
218
+     * This unfortunately needs to be public to be used in Util\Util
219
+     *
220
+     * @param array $values
221
+     * @param null|string|array|Util\RequestOptions $opts
222
+     *
223
+     * @return static The object constructed from the given values.
224
+     */
225
+    public static function constructFrom($values, $opts = null)
226
+    {
227
+        $obj = new static(isset($values['id']) ? $values['id'] : null);
228
+        $obj->refreshFrom($values, $opts);
229
+        return $obj;
230
+    }
231
+
232
+    /**
233
+     * Refreshes this object using the provided values.
234
+     *
235
+     * @param array $values
236
+     * @param null|string|array|Util\RequestOptions $opts
237
+     * @param boolean $partial Defaults to false.
238
+     */
239
+    public function refreshFrom($values, $opts, $partial = false)
240
+    {
241
+        $this->_opts = Util\RequestOptions::parse($opts);
242
+
243
+        $this->_originalValues = self::deepCopy($values);
244
+
245
+        if ($values instanceof TelnyxObject) {
246
+            $values = $values->__toArray(true);
247
+        }
248
+
249
+        // Wipe old state before setting new.  This is useful for e.g. updating a
250
+        // customer, where there is no persistent card parameter.  Mark those values
251
+        // which don't persist as transient
252
+        if ($partial) {
253
+            $removed = new Util\Set();
254
+        } else {
255
+            $removed = new Util\Set(array_diff(array_keys($this->_values), array_keys($values)));
256
+        }
257
+
258
+        foreach ($removed->toArray() as $k) {
259
+            unset($this->$k);
260
+        }
261
+
262
+        $this->updateAttributes($values, $opts, false);
263
+        foreach ($values as $k => $v) {
264
+            $this->_transientValues->discard($k);
265
+            $this->_unsavedValues->discard($k);
266
+        }
267
+    }
268
+
269
+    /**
270
+     * Mass assigns attributes on the model.
271
+     *
272
+     * @param array $values
273
+     * @param null|string|array|Util\RequestOptions $opts
274
+     * @param boolean $dirty Defaults to true.
275
+     */
276
+    public function updateAttributes($values, $opts = null, $dirty = true)
277
+    {
278
+        foreach ($values as $k => $v) {
279
+            // Special-case metadata to always be cast as a TelnyxObject
280
+            // This is necessary in case metadata is empty, as PHP arrays do
281
+            // not differentiate between lists and hashes, and we consider
282
+            // empty arrays to be lists.
283
+            if (($k === "metadata") && (is_array($v))) {
284
+                $this->_values[$k] = TelnyxObject::constructFrom($v, $opts);
285
+            } else {
286
+                $this->_values[$k] = Util\Util::convertToTelnyxObject($v, $opts);
287
+            }
288
+            if ($dirty) {
289
+                $this->dirtyValue($this->_values[$k]);
290
+            }
291
+            $this->_unsavedValues->add($k);
292
+        }
293
+    }
294
+
295
+    /**
296
+     * @return array A recursive mapping of attributes to values for this object,
297
+     *    including the proper value for deleted attributes.
298
+     */
299
+    public function serializeParameters($force = false)
300
+    {
301
+        $updateParams = [];
302
+
303
+        foreach ($this->_values as $k => $v) {
304
+            // There are a few reasons that we may want to add in a parameter for
305
+            // update:
306
+            //
307
+            //   1. The `$force` option has been set.
308
+            //   2. We know that it was modified.
309
+            //   3. Its value is a TelnyxObject. A TelnyxObject may contain modified
310
+            //      values within in that its parent TelnyxObject doesn't know about.
311
+            //
312
+            $original = array_key_exists($k, $this->_originalValues) ? $this->_originalValues[$k] : null;
313
+            $unsaved = $this->_unsavedValues->includes($k);
314
+            if ($force || $unsaved || $v instanceof TelnyxObject) {
315
+                $updateParams[$k] = $this->serializeParamsValue(
316
+                    $this->_values[$k],
317
+                    $original,
318
+                    $unsaved,
319
+                    $force,
320
+                    $k
321
+                );
322
+            }
323
+        }
324
+
325
+        // a `null` that makes it out of `serializeParamsValue` signals an empty
326
+        // value that we shouldn't appear in the serialized form of the object
327
+        $updateParams = array_filter(
328
+            $updateParams,
329
+            function ($v) {
330
+                return $v !== null;
331
+            }
332
+        );
333
+
334
+        return $updateParams;
335
+    }
336
+
337
+
338
+    public function serializeParamsValue($value, $original, $unsaved, $force, $key = null)
339
+    {
340
+        // The logic here is that essentially any object embedded in another
341
+        // object that had a `type` is actually an API resource of a different
342
+        // type that's been included in the response. These other resources must
343
+        // be updated from their proper endpoints, and therefore they are not
344
+        // included when serializing even if they've been modified.
345
+        //
346
+        // There are _some_ known exceptions though.
347
+        //
348
+        // For example, if the value is unsaved (meaning the user has set it), and
349
+        // it looks like the API resource is persisted with an ID, then we include
350
+        // the object so that parameters are serialized with a reference to its
351
+        // ID.
352
+        //
353
+        // Another example is that on save API calls it's sometimes desirable to
354
+        // update a customer's default source by setting a new card (or other)
355
+        // object with `->source=` and then saving the customer. The
356
+        // `saveWithParent` flag to override the default behavior allows us to
357
+        // handle these exceptions.
358
+        //
359
+        // We throw an error if a property was set explicitly but we can't do
360
+        // anything with it because the integration is probably not working as the
361
+        // user intended it to.
362
+        if ($value === null) {
363
+            return "";
364
+        } elseif (($value instanceof APIResource) && (!$value->saveWithParent)) {
365
+            if (!$unsaved) {
366
+                return null;
367
+            } elseif (isset($value->id)) {
368
+                return $value;
369
+            } else {
370
+                throw new \InvalidArgumentException(
371
+                    "Cannot save property `$key` containing an API resource of type " .
372
+                    get_class($value) . ". It doesn't appear to be persisted and is " .
373
+                    "not marked as `saveWithParent`."
374
+                );
375
+            }
376
+        } elseif (is_array($value)) {
377
+            if (Util\Util::isList($value)) {
378
+                // Sequential array, i.e. a list
379
+                $update = [];
380
+                foreach ($value as $v) {
381
+                    array_push($update, $this->serializeParamsValue($v, null, true, $force));
382
+                }
383
+                // This prevents an array that's unchanged from being resent.
384
+                if ($update !== $this->serializeParamsValue($original, null, true, $force, $key)) {
385
+                    return $update;
386
+                }
387
+            } else {
388
+                // Associative array, i.e. a map
389
+                return Util\Util::convertToTelnyxObject($value, $this->_opts)->serializeParameters();
390
+            }
391
+        } elseif ($value instanceof TelnyxObject) {
392
+            $update = $value->serializeParameters($force);
393
+            if ($original && $unsaved && $key && static::getAdditiveParams()->includes($key)) {
394
+                $update = array_merge(self::emptyValues($original), $update);
395
+            }
396
+            return $update;
397
+        } else {
398
+            return $value;
399
+        }
400
+    }
401
+
402
+    public function jsonSerialize()
403
+    {
404
+        return $this->__toArray(true);
405
+    }
406
+
407
+    public function __toJSON()
408
+    {
409
+        return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
410
+    }
411
+
412
+    public function __toString()
413
+    {
414
+        $class = get_class($this);
415
+        return $class . ' JSON: ' . $this->__toJSON();
416
+    }
417
+
418
+    public function __toArray($recursive = false)
419
+    {
420
+        if ($recursive) {
421
+            return Util\Util::convertTelnyxObjectToArray($this->_values);
422
+        } else {
423
+            return $this->_values;
424
+        }
425
+    }
426
+
427
+    /**
428
+     * Sets all keys within the TelnyxObject as unsaved so that they will be
429
+     * included with an update when `serializeParameters` is called. This
430
+     * method is also recursive, so any TelnyxObjects contained as values or
431
+     * which are values in a tenant array are also marked as dirty.
432
+     */
433
+    public function dirty()
434
+    {
435
+        $this->_unsavedValues = new Util\Set(array_keys($this->_values));
436
+        foreach ($this->_values as $k => $v) {
437
+            $this->dirtyValue($v);
438
+        }
439
+    }
440
+
441
+    protected function dirtyValue($value)
442
+    {
443
+        if (is_array($value)) {
444
+            foreach ($value as $v) {
445
+                $this->dirtyValue($v);
446
+            }
447
+        } elseif ($value instanceof TelnyxObject) {
448
+            $value->dirty();
449
+        }
450
+    }
451
+
452
+    /**
453
+     * Produces a deep copy of the given object including support for arrays
454
+     * and TelnyxObjects.
455
+     */
456
+    protected static function deepCopy($obj)
457
+    {
458
+        if (is_array($obj)) {
459
+            $copy = [];
460
+            foreach ($obj as $k => $v) {
461
+                $copy[$k] = self::deepCopy($v);
462
+            }
463
+            return $copy;
464
+        } elseif ($obj instanceof TelnyxObject) {
465
+            return $obj::constructFrom(
466
+                self::deepCopy($obj->_values),
467
+                clone $obj->_opts
468
+            );
469
+        } else {
470
+            return $obj;
471
+        }
472
+    }
473
+
474
+    /**
475
+     * Returns a hash of empty values for all the values that are in the given
476
+     * TelnyxObject.
477
+     */
478
+    public static function emptyValues($obj)
479
+    {
480
+        if (is_array($obj)) {
481
+            $values = $obj;
482
+        } elseif ($obj instanceof TelnyxObject) {
483
+            $values = $obj->_values;
484
+        } else {
485
+            throw new \InvalidArgumentException(
486
+                "empty_values got got unexpected object type: " . get_class($obj)
487
+            );
488
+        }
489
+        $update = array_fill_keys(array_keys($values), "");
490
+        return $update;
491
+    }
492
+
493
+    /**
494
+     * @return object The last response from the Telnyx API
495
+     */
496
+    public function getLastResponse()
497
+    {
498
+        return $this->_lastResponse;
499
+    }
500
+
501
+    /**
502
+     * Sets the last response from the Telnyx API
503
+     *
504
+     * @param ApiResponse $resp
505
+     * @return void
506
+     */
507
+    public function setLastResponse($resp)
508
+    {
509
+        $this->_lastResponse = $resp;
510
+    }
511
+
512
+    /**
513
+     * Indicates whether or not the resource has been deleted on the server.
514
+     * Note that some, but not all, resources can indicate whether they have
515
+     * been deleted.
516
+     *
517
+     * @return bool Whether the resource is deleted.
518
+     */
519
+    public function isDeleted()
520
+    {
521
+        return isset($this->_values['deleted']) ? $this->_values['deleted'] : false;
522
+    }
523
+}