Completed
Push — master ( 11e5ad...663d1c )
by Toby
02:33
created

Document::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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 3
                $type = $resource->getType();
77 3
                $id = $resource->getId();
78
            }
79
80 3
            foreach ($resource->getUnfilteredRelationships() as $relationship) {
81
                $includedElement = $relationship->getData();
82
83
                if (! $includedElement instanceof ElementInterface) {
84
                    continue;
85
                }
86
87
                foreach ($this->getIncluded($includedElement, true) as $child) {
88
                    // If this resource is the same as the top-level "data"
89
                    // resource, then we don't want it to show up again in the
90
                    // "included" array.
91
                    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...
92
                        continue;
93
                    }
94
95
                    $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...
96
                }
97 3
            }
98 3
        }
99
100 3
        $flattened = [];
101
102
        array_walk_recursive($included, function ($a) use (&$flattened) {
103
            $flattened[] = $a;
104 3
        });
105
106 3
        return $flattened;
107
    }
108
109
    /**
110
     * @param Resource[] $resources
111
     * @param Resource $newResource
112
     * @return Resource[]
113
     */
114
    protected function mergeResource(array $resources, Resource $newResource)
115
    {
116
        $type = $newResource->getType();
117
        $id = $newResource->getId();
118
119
        if (isset($resources[$type][$id])) {
120
            $resources[$type][$id]->merge($newResource);
121
        } else {
122
            $resources[$type][$id] = $newResource;
123
        }
124
125
        return $resources;
126
    }
127
128
    /**
129
     * Set the data object.
130
     *
131
     * @param ElementInterface $element
132
     * @return $this
133
     */
134
    public function setData(ElementInterface $element)
135
    {
136
        $this->data = $element;
137
138
        return $this;
139
    }
140
141
    /**
142
     * Set the errors array.
143
     *
144
     * @param array $errors
145
     * @return $this
146
     */
147
    public function setErrors($errors)
148
    {
149
        $this->errors = $errors;
150
151
        return $this;
152
    }
153
154
    /**
155
     * Set the jsonapi array.
156
     *
157
     * @param array $jsonapi
158
     * @return $this
159
     */
160
    public function setJsonapi($jsonapi)
161
    {
162
        $this->jsonapi = $jsonapi;
163
164
        return $this;
165
    }
166
167
    /**
168
     * Map everything to arrays.
169
     *
170
     * @return array
171
     */
172 6
    public function toArray()
173
    {
174 6
        $document = [];
175
176 6
        if (! empty($this->links)) {
177
            $document['links'] = $this->links;
178
        }
179
180 6
        if (! empty($this->data)) {
181 3
            $document['data'] = $this->data->toArray();
182
183 3
            $resources = $this->getIncluded($this->data);
184
185 3
            if (count($resources)) {
186
                $document['included'] = array_map(function (Resource $resource) {
187
                    return $resource->toArray();
188
                }, $resources);
189
            }
190 3
        }
191
192 6
        if (! empty($this->meta)) {
193
            $document['meta'] = $this->meta;
194
        }
195
196 6
        if (! empty($this->errors)) {
197
            $document['errors'] = $this->errors;
198
        }
199
200 6
        if (! empty($this->jsonapi)) {
201
            $document['jsonapi'] = $this->jsonapi;
202
        }
203
204 6
        return $document;
205
    }
206
207
    /**
208
     * Map to string.
209
     *
210
     * @return string
211
     */
212 3
    public function __toString()
213
    {
214 3
        return json_encode($this->toArray());
215
    }
216
217
    /**
218
     * Serialize for JSON usage.
219
     *
220
     * @return array
221
     */
222
    public function jsonSerialize()
223
    {
224
        return $this->toArray();
225
    }
226
}
227