Completed
Push — master ( 23f646...5fafaa )
by Arina
24s queued 11s
created

JsonResponse::error()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 4
1
<?php
2
3
namespace ArinaSystems\JsonResponse;
4
5
use Exception;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Facades\Response;
8
9
class JsonResponse
10
{
11
    /**
12
     * @var \ArinaSystems\JsonResponse\Status
13
     */
14
    protected $status;
15
16
    /**
17
     * @var \ArinaSystems\JsonResponse\Option
18
     */
19
    protected $options;
20
21
    /**
22
     * @var \ArinaSystems\JsonResponse\Attribute
23
     */
24
    protected $attributes;
25
26
    /**
27
     * Create a new instance.
28
     *
29
     * @param  \ArinaSystems\JsonResponse\Option|array|string $options
30
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
31
     */
32
    public function __construct(Option $options, Attribute $attributes, Status $status)
33
    {
34
        $this->options = $options;
35
        $this->status = $status;
36
        $this->attributes = $attributes;
37
    }
38
39
    /**
40
     * Return json response.
41
     *
42
     * @param  string|array                    $status
43
     * @param  array                           $attributes
44
     * @param  array                           $options
45
     * @return \Illuminate\Http\JsonResponse
46
     */
47
    public function json($status = null, array $attributes = [], array $options = [])
48
    {
49
        if (is_array($status)) {
50
            extract($status);
51
        }
52
53
        if ($options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
54
            $this->options($options);
55
        }
56
57
        if ($attributes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attributes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
58
            $this->attributes($attributes);
59
        }
60
61
        if (!is_null($status) && is_string($status)) {
62
            $this->status($status);
63
        }
64
65
        $is_error = $this->attributes('exception') ? true : false;
66
67
        return Response::json(
68
            $this->build($is_error),
69
            $this->attributes('http_code', 200),
70
            $this->attributes('headers', []),
71
            $this->options('encoding_options')
72
        );
73
    }
74
75
    /**
76
     * Return json response with error.
77
     *
78
     * @param  \Throwable                      $exception
79
     * @param  string|array                    $status
80
     * @param  array                           $attributes
81
     * @param  array                           $options
82
     * @return \Illuminate\Http\JsonResponse
83
     */
84
    public function error($exception, $status = null, array $attributes = [], array $options = [])
85
    {
86
        $this->setAttributes('exception', $exception);
87
88
        return $this->json($status, $attributes, $options);
89
    }
90
91
    /**
92
     * Retrieve/Set json response status.
93
     *
94
     * @param  string                                   $status
95
     * @return \ArinaSystems\JsonResponse\Status|self
96
     */
97
    public function status(string $status = null)
98
    {
99
        if (is_string($status)) {
100
            throw_if(!$this->status->has($status), new Exception("The \"{$status}\" response state does not exist"));
0 ignored issues
show
Documentation introduced by
$status is of type string, but the function expects a array.

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...
101
            return $this->status->call($status, $this);
102
        }
103
104
        return $this->status;
105
    }
106
107
    /**
108
     * Retrieve/Set json response options.
109
     *
110
     * @param  string|array|null                         $key
111
     * @param  mixed|null                                $default
112
     * @return \ArinaSystems\JsonResponse\Option|mixed
113
     */
114
    public function options($key = null, $default = null)
115
    {
116
        if (is_array($key)) {
117
            return $this->setOptions($key);
118
        }
119
120
        if (!is_null($key)) {
121
            return $this->options->get($key, $default);
122
        }
123
124
        return $this->options ?? $default;
125
    }
126
127
    /**
128
     * Retrieve/Set json response attributes.
129
     *
130
     * @param  string|array|null                            $key
131
     * @param  mixed|null                                   $default
132
     * @return \ArinaSystems\JsonResponse\Attribute|mixed
133
     */
134
    public function attributes($key = null, $default = null)
135
    {
136
        if (is_array($key)) {
137
            return $this->setAttributes($key);
138
        }
139
140
        if (is_string($key)) {
141
            return $this->attributes->get($key, $default);
142
        }
143
144
        return $this->attributes ?? $default;
145
    }
146
147
    /**
148
     * Set json response options.
149
     *
150
     * @param  \ArinaSystems\JsonResponse\Option|array|string $options
151
     * @param  null|mixed                                     $value
152
     * @return self
153
     */
154
    public function setOptions($options, $value = null)
155
    {
156
        if (is_a($options, Option::class)) {
157
            $this->options = $options;
0 ignored issues
show
Documentation Bug introduced by
It seems like $options can also be of type array or string. However, the property $options is declared as type object<ArinaSystems\JsonResponse\Option>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
158
        }
159
160
        $this->options->set($options, $value);
161
162
        return $this;
163
    }
164
165
    /**
166
     * Set json response attributes.
167
     *
168
     * @param  \ArinaSystems\JsonResponse\Attribute|array|string $attributes
169
     * @param  null|mixed                                        $value
170
     * @return self
171
     */
172
    public function setAttributes($attributes, $value = null)
173
    {
174
        if (is_a($attributes, Attribute::class)) {
175
            $this->attributes = $attributes;
0 ignored issues
show
Documentation Bug introduced by
It seems like $attributes can also be of type array or string. However, the property $attributes is declared as type object<ArinaSystems\JsonResponse\Attribute>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
176
        }
177
178
        $this->attributes->set($attributes, $value);
179
        return $this;
180
    }
181
182
    /**
183
     * Get the json response data array.
184
     *
185
     * @param  bool    $error
186
     * @return array
187
     */
188
    protected function build(bool $error = false): array
189
    {
190
        $structure = $this->structure($error);
191
192
        if (!$this->options('debug')) {
193
            unset($structure['debug']);
194
        }
195
196
        $response = array_intersect_key($this->attributes->all('value'), $structure);
197
198
        return $response;
199
    }
200
201
    /**
202
     * Get response data structure.
203
     *
204
     * @param  bool    $error
205
     * @return array
206
     */
207
    public function structure(bool $error = false): array
208
    {
209
        $structure = Arr::where($this->attributes->all('on-' . ($error ? 'error' : 'response')), function ($onStructure) {
210
            return boolval($onStructure);
211
        });
212
213
        $structure = array_keys($structure);
214
215
        return array_flip($structure);
216
    }
217
218
    /**
219
     * Get the current instance of json response builder.
220
     *
221
     * @return ArinaSystems\JsonResponse\JsonResponse
222
     */
223
    public function instance(): self
224
    {
225
        return $this;
226
    }
227
}
228