Completed
Push — master ( e014ef...46142e )
by Toby
63:09
created

Document::getIncluded()   D

Complexity

Conditions 9
Paths 10

Size

Total Lines 40
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 19.125

Importance

Changes 6
Bugs 1 Features 1
Metric Value
c 6
b 1
f 1
dl 0
loc 40
ccs 11
cts 22
cp 0.5
rs 4.909
cc 9
eloc 20
nc 10
nop 2
crap 19.125
1
<?php
2
3
/*
4
 * This file is part of JSON-API.
5
 *
6
 * (c) Toby Zerner <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Tobscure\JsonApi;
13
14
use JsonSerializable;
15
16
class Document implements JsonSerializable
17
{
18
    use LinksTrait;
19
    use MetaTrait;
20
21
    /**
22
     * The included array.
23
     *
24
     * @var array
25
     */
26
    protected $included = [];
27
28
    /**
29
     * The errors array.
30
     *
31
     * @var array
32
     */
33
    protected $errors;
34
35
    /**
36
     * The jsonapi array.
37
     *
38
     * @var array
39
     */
40
    protected $jsonapi;
41
42
    /**
43
     * The data object.
44
     *
45
     * @var ElementInterface
46
     */
47
    protected $data;
48
49
    /**
50
     * @param ElementInterface $data
0 ignored issues
show
Documentation introduced by
Should the type for parameter $data not be null|ElementInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
51
     */
52 6
    public function __construct(ElementInterface $data = null)
53
    {
54 6
        $this->data = $data;
55 6
    }
56
57
    /**
58
     * Get included resources.
59
     *
60
     * @param ElementInterface $element
61
     * @param bool $includeParent
62
     * @return Resource[]
63
     */
64 3
    protected function getIncluded(ElementInterface $element, $includeParent = false)
65
    {
66 3
        $included = [];
67
68 3
        foreach ($element->getResources() as $resource) {
69 3
            if ($resource->isIdentifier()) {
70
                continue;
71
            }
72
73 3
            if ($includeParent) {
74
                $included = $this->mergeResource($included, $resource);
75
            } else {
76
                $type = $resource->getType();
77 3
                $id = $resource->getId();
78
            }
79
80
            foreach ($resource->getUnfilteredRelationships() as $relationship) {
81
                $includedElement = $relationship->getData();
82
83 3
                foreach ($this->getIncluded($includedElement, true) as $child) {
84 3
                    // If this resource is the same as the top-level "data"
85
                    // resource, then we don't want it to show up again in the
86 3
                    // "included" array.
87
                    if (! $includeParent && $child->getType() === $type && $child->getId() === $id) {
0 ignored issues
show
Bug introduced by
The variable $type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The method getType cannot be called on $child (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method getId cannot be called on $child (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
88
                        continue;
89
                    }
90 3
91
                    $included = $this->mergeResource($included, $child);
0 ignored issues
show
Documentation introduced by
$child is of type resource, but the function expects a object<Tobscure\JsonApi\Resource>.

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...
92 3
                }
93
            }
94
        }
95
96
        $flattened = [];
97
98
        array_walk_recursive($included, function ($a) use (&$flattened) {
99
            $flattened[] = $a;
100
        });
101
102
        return $flattened;
103
    }
104
105
    /**
106
     * @param Resource[] $resources
107
     * @param Resource $newResource
108
     * @return Resource[]
109
     */
110
    protected function mergeResource(array $resources, Resource $newResource)
111
    {
112
        $type = $newResource->getType();
113
        $id = $newResource->getId();
114
115
        if (isset($resources[$type][$id])) {
116
            $resources[$type][$id]->merge($newResource);
117
        } else {
118
            $resources[$type][$id] = $newResource;
119
        }
120
121
        return $resources;
122
    }
123
124
    /**
125
     * Set the data object.
126
     *
127
     * @param ElementInterface $element
128
     * @return $this
129
     */
130
    public function setData(ElementInterface $element)
131
    {
132
        $this->data = $element;
133
134
        return $this;
135
    }
136
137
    /**
138
     * Set the errors array.
139
     *
140
     * @param array $errors
141
     * @return $this
142
     */
143
    public function setErrors($errors)
144
    {
145
        $this->errors = $errors;
146
147
        return $this;
148
    }
149
150
    /**
151
     * Set the jsonapi array.
152
     *
153
     * @param array $jsonapi
154
     * @return $this
155
     */
156
    public function setJsonapi($jsonapi)
157
    {
158 6
        $this->jsonapi = $jsonapi;
159
160 6
        return $this;
161
    }
162 6
163
    /**
164
     * Map everything to arrays.
165
     *
166 6
     * @return array
167 3
     */
168
    public function toArray()
169 3
    {
170
        $document = [];
171 3
172
        if (! empty($this->links)) {
173
            $document['links'] = $this->links;
174
        }
175
176 3
        if (! empty($this->data)) {
177
            $document['data'] = $this->data->toArray();
178 6
179
            $resources = $this->getIncluded($this->data);
180
181
            if (count($resources)) {
182 6
                $document['included'] = array_map(function (Resource $resource) {
183
                    return $resource->toArray();
184
                }, $resources);
185
            }
186 6
        }
187
188
        if (! empty($this->meta)) {
189
            $document['meta'] = $this->meta;
190 6
        }
191
192
        if (! empty($this->errors)) {
193
            $document['errors'] = $this->errors;
194
        }
195
196
        if (! empty($this->jsonapi)) {
197
            $document['jsonapi'] = $this->jsonapi;
198 3
        }
199
200 3
        return $document;
201
    }
202
203
    /**
204
     * Map to string.
205
     *
206
     * @return string
207
     */
208
    public function __toString()
209
    {
210
        return json_encode($this->toArray());
211
    }
212
213
    /**
214
     * Serialize for JSON usage.
215
     *
216
     * @return array
217
     */
218
    public function jsonSerialize()
219
    {
220
        return $this->toArray();
221
    }
222
}
223