Passed
Pull Request — master (#899)
by Yassine
05:08
created

GraphNode::map()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
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 56
    public function __construct(array $data = [])
48
    {
49 56
        $this->fields = $this->castFields($data);
50 56
    }
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 30
    public function getField($name, $default = null)
61
    {
62 30
        if (isset($this->fields[$name])) {
63 27
            return $this->fields[$name];
64
        }
65
66 3
        return $default;
67
    }
68
69
    /**
70
     * Returns a list of all fields set on the object.
71
     *
72
     * @return array
73
     */
74 1
    public function getFieldNames()
75
    {
76 1
        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 10
        return array_map(function ($value) {
97 9
            if ($value instanceof GraphNode || $value instanceof GraphEdge) {
98 1
                return $value->asArray();
99
            }
100
101 9
            return $value;
102 10
        }, $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 2
    public function asJson($options = 0)
125
    {
126 2
        return json_encode($this->uncastFields(), $options);
127
    }
128
129
    /**
130
     * Count the number of fields in the collection.
131
     *
132
     * @return int
133
     */
134 1
    public function count()
135
    {
136 1
        return count($this->fields);
137
    }
138
139
    /**
140
     * Get an iterator for the fields.
141
     *
142
     * @return \ArrayIterator
143
     */
144 1
    public function getIterator()
145
    {
146 1
        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 5
    public function offsetGet($key)
169
    {
170 5
        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 1
    public function offsetSet($key, $value)
182
    {
183 1
        if (is_null($key)) {
184
            $this->fields[] = $value;
185
        } else {
186 1
            $this->fields[$key] = $value;
187
        }
188 1
    }
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 56
    public function castFields(array $data)
223
    {
224 56
        $fields = [];
225
226 56
        foreach ($data as $k => $v) {
227 52
            if ($this->shouldCastAsDateTime($k)
228 6
                && (is_numeric($v)
229 52
                    || $this->isIso8601DateString($v))
230
            ) {
231 6
                $fields[$k] = $this->castToDateTime($v);
232 49
            } elseif ($k === 'birthday') {
233 3
                $fields[$k] = $this->castToBirthday($v);
234
            } else {
235 52
                $fields[$k] = $v;
236
            }
237
        }
238
239 56
        return $fields;
240
    }
241
242
    /**
243
     * Uncasts any auto-casted datatypes.
244
     * Basically the reverse of castFields().
245
     *
246
     * @return array
247
     */
248 3
    public function uncastFields()
249
    {
250 3
        $fields = $this->asArray();
251
252 3
        return array_map(function ($v) {
253 3
            if ($v instanceof \DateTime) {
254 2
                return $v->format(\DateTime::ISO8601);
255
            }
256
257 3
            return $v;
258 3
        }, $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 6
    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 6
            . '([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/';
284
285 6
        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 52
    public function shouldCastAsDateTime($key)
296
    {
297 52
        return in_array($key, [
298 52
            '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 52
        ], 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 8
    public function castToDateTime($value)
318
    {
319 8
        if (is_int($value)) {
320 2
            $dt = new \DateTime();
321 2
            $dt->setTimestamp($value);
322
        } else {
323 6
            $dt = new \DateTime($value);
324
        }
325
326 8
        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 3
    public function castToBirthday($value)
337
    {
338 3
        return new Birthday($value);
339
    }
340
341
    /**
342
     * Getter for $graphNodeMap.
343
     *
344
     * @return array
345
     */
346 19
    public static function getNodeMap()
347
    {
348 19
        return static::$graphNodeMap;
349
    }
350
}
351