Completed
Pull Request — master (#899)
by Yassine
60:59
created

GraphNode::castItems()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 4
nop 1
dl 0
loc 18
ccs 11
cts 11
cp 1
crap 6
rs 8.8571
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A GraphNode::getField() 0 7 2
1
<?php
2
/**
3
 * Copyright 2017 Facebook, Inc.
4
 *
5
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
6
 * use, copy, modify, and distribute this software in source code or binary
7
 * form for use in connection with the web services and APIs provided by
8
 * Facebook.
9
 *
10
 * As with any software that integrates with the Facebook platform, your use
11
 * of this software is subject to the Facebook Developer Principles and
12
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
13
 * shall be included in all copies or substantial portions of the software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * DEALINGS IN THE SOFTWARE.
22
 */
23
namespace Facebook\GraphNode;
24
25
/**
26
 * @package Facebook
27
 */
28
class GraphNode implements \ArrayAccess, \Countable, \IteratorAggregate
29
{
30
    /**
31
     * @var array maps object key names to Graph object types
32
     */
33
    protected static $graphNodeMap = [];
34
35
    /**
36
     * The fields contained in the node.
37
     *
38
     * @var array
39
     */
40
    protected $fields = [];
41
42
    /**
43
     * Init this Graph object.
44
     *
45
     * @param array $data
46
     */
47
    public function __construct(array $data = [])
48
    {
49
        $this->fields = $this->castFields($data);
50
    }
51
52
    /**
53
     * Gets the value of a field from the Graph node.
54
     *
55
     * @param string $name    the field to retrieve
56
     * @param mixed  $default the default to return if the field doesn't exist
57
     *
58
     * @return mixed
59
     */
60
    public function getField($name, $default = null)
61
    {
62
        if (isset($this->fields[$name])) {
63
            return $this->fields[$name];
64
        }
65
66
        return $default;
67
    }
68
69
    /**
70
     * Returns a list of all fields set on the object.
71
     *
72
     * @return array
73
     */
74
    public function getFieldNames()
75
    {
76
        return array_keys($this->fields);
77
    }
78
79
    /**
80
     * Get all of the fields in the node.
81
     *
82
     * @return array
83
     */
84
    public function all()
85
    {
86
        return $this->fields;
87
    }
88
89
    /**
90
     * Get all fields as a plain array.
91
     *
92
     * @return array
93
     */
94 View Code Duplication
    public function asArray()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
95
    {
96
        return array_map(function ($value) {
97
            if ($value instanceof GraphNode || $value instanceof GraphEdge) {
98
                return $value->asArray();
99
            }
100
101
            return $value;
102
        }, $this->fields);
103
    }
104
105
    /**
106
     * Run a map over each field.
107
     *
108
     * @param \Closure $callback
109
     *
110
     * @return static
111
     */
112
    public function map(\Closure $callback)
113
    {
114
        return new static(array_map($callback, $this->fields, array_keys($this->fields)));
115
    }
116
117
    /**
118
     * Get all fields as JSON.
119
     *
120
     * @param int $options
121
     *
122
     * @return string
123
     */
124
    public function asJson($options = 0)
125
    {
126
        return json_encode($this->uncastFields(), $options);
127
    }
128
129
    /**
130
     * Count the number of fields in the collection.
131
     *
132
     * @return int
133
     */
134
    public function count()
135
    {
136
        return count($this->fields);
137
    }
138
139
    /**
140
     * Get an iterator for the fields.
141
     *
142
     * @return \ArrayIterator
143
     */
144
    public function getIterator()
145
    {
146
        return new \ArrayIterator($this->fields);
147
    }
148
149
    /**
150
     * Determine if an item exists at an offset.
151
     *
152
     * @param mixed $key
153
     *
154
     * @return bool
155
     */
156
    public function offsetExists($key)
157
    {
158
        return array_key_exists($key, $this->fields);
159
    }
160
161
    /**
162
     * Get an item at a given offset.
163
     *
164
     * @param mixed $key
165
     *
166
     * @return mixed
167
     */
168
    public function offsetGet($key)
169
    {
170
        return $this->fields[$key];
171
    }
172
173
    /**
174
     * Set the item at a given offset.
175
     *
176
     * @param mixed $key
177
     * @param mixed $value
178
     *
179
     * @return void
180
     */
181
    public function offsetSet($key, $value)
182
    {
183
        if (is_null($key)) {
184
            $this->fields[] = $value;
185
        } else {
186
            $this->fields[$key] = $value;
187
        }
188
    }
189
190
    /**
191
     * Unset the item at a given offset.
192
     *
193
     * @param string $key
194
     *
195
     * @return void
196
     */
197
    public function offsetUnset($key)
198
    {
199
        unset($this->fields[$key]);
200
    }
201
202
    /**
203
     * Convert the collection to its string representation.
204
     *
205
     * @return string
206
     */
207
    public function __toString()
208
    {
209
        return $this->asJson();
210
    }
211
212
    /**
213
     * Iterates over an array and detects the types each node
214
     * should be cast to and returns all the fields as an array.
215
     *
216
     * @TODO Add auto-casting to AccessToken entities.
217
     *
218
     * @param array $data the array to iterate over
219
     *
220
     * @return array
221
     */
222
    public function castFields(array $data)
223
    {
224
        $fields = [];
225
226
        foreach ($data as $k => $v) {
227
            if ($this->shouldCastAsDateTime($k)
228
                && (is_numeric($v)
229
                    || $this->isIso8601DateString($v))
230
            ) {
231
                $fields[$k] = $this->castToDateTime($v);
232
            } elseif ($k === 'birthday') {
233
                $fields[$k] = $this->castToBirthday($v);
234
            } else {
235
                $fields[$k] = $v;
236
            }
237
        }
238
239
        return $fields;
240
    }
241
242
    /**
243
     * Uncasts any auto-casted datatypes.
244
     * Basically the reverse of castFields().
245
     *
246
     * @return array
247
     */
248
    public function uncastFields()
249
    {
250
        $fields = $this->asArray();
251
252
        return array_map(function ($v) {
253
            if ($v instanceof \DateTime) {
254
                return $v->format(\DateTime::ISO8601);
255
            }
256
257
            return $v;
258
        }, $fields);
259
    }
260
261
    /**
262
     * Detects an ISO 8601 formatted string.
263
     *
264
     * @param string $string
265
     *
266
     * @return bool
267
     *
268
     * @see https://developers.facebook.com/docs/graph-api/using-graph-api/#readmodifiers
269
     * @see http://www.cl.cam.ac.uk/~mgk25/iso-time.html
270
     * @see http://en.wikipedia.org/wiki/ISO_8601
271
     */
272
    public function isIso8601DateString($string)
273
    {
274
        // This insane regex was yoinked from here:
275
        // http://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
276
        // ...and I'm all like:
277
        // http://thecodinglove.com/post/95378251969/when-code-works-and-i-dont-know-why
278
        $crazyInsaneRegexThatSomehowDetectsIso8601 = '/^([\+-]?\d{4}(?!\d{2}\b))'
279
            . '((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?'
280
            . '|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d'
281
            . '|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])'
282
            . '((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d'
283
            . '([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/';
284
285
        return preg_match($crazyInsaneRegexThatSomehowDetectsIso8601, $string) === 1;
286
    }
287
288
    /**
289
     * Determines if a value from Graph should be cast to DateTime.
290
     *
291
     * @param string $key
292
     *
293
     * @return bool
294
     */
295
    public function shouldCastAsDateTime($key)
296
    {
297
        return in_array($key, [
298
            'created_time',
299
            'updated_time',
300
            'start_time',
301
            'stop_time',
302
            'end_time',
303
            'backdated_time',
304
            'issued_at',
305
            'expires_at',
306
            'publish_time'
307
        ], true);
308
    }
309
310
    /**
311
     * Casts a date value from Graph to DateTime.
312
     *
313
     * @param int|string $value
314
     *
315
     * @return \DateTime
316
     */
317
    public function castToDateTime($value)
318
    {
319
        if (is_int($value)) {
320
            $dt = new \DateTime();
321
            $dt->setTimestamp($value);
322
        } else {
323
            $dt = new \DateTime($value);
324
        }
325
326
        return $dt;
327
    }
328
329
    /**
330
     * Casts a birthday value from Graph to Birthday.
331
     *
332
     * @param string $value
333
     *
334
     * @return Birthday
335
     */
336
    public function castToBirthday($value)
337
    {
338
        return new Birthday($value);
339
    }
340
341
    /**
342
     * Getter for $graphNodeMap.
343
     *
344
     * @return array
345
     */
346
    public static function getNodeMap()
347
    {
348
        return static::$graphNodeMap;
349
    }
350
}
351