Passed
Pull Request — master (#899)
by Yassine
33:24
created

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