Completed
Pull Request — master (#1)
by Arina
01:38
created

JsonResponse   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 25
lcom 1
cbo 5
dl 0
loc 217
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
B json() 0 25 6
A error() 0 6 1
A status() 0 9 2
A options() 0 12 3
A attributes() 0 12 3
A setOptions() 0 10 2
A setAttributes() 0 9 2
A build() 0 12 2
A structure() 0 10 2
A instance() 0 4 1
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
        return Response::json(
66
            $this->build($this->attributes('exception', false)),
67
            $this->attributes('http_code', 200),
68
            $this->attributes('headers', []),
69
            $this->options('encoding_options')
70
        );
71
    }
72
73
    /**
74
     * Return json response with error.
75
     *
76
     * @param  \Throwable                      $exception
77
     * @param  string|array                    $status
78
     * @param  array                           $attributes
79
     * @param  array                           $options
80
     * @return \Illuminate\Http\JsonResponse
81
     */
82
    public function error($exception, $status = null, array $attributes = [], array $options = [])
83
    {
84
        $this->setAttributes('exception', $exception);
85
86
        return $this->json($status, $attributes, $options);
87
    }
88
89
    /**
90
     * Retrieve/Set json response status.
91
     *
92
     * @param  string                                   $status
93
     * @return \ArinaSystems\JsonResponse\Status|self
94
     */
95
    public function status(string $status = null)
96
    {
97
        if (is_string($status)) {
98
            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...
99
            return $this->status->call($status, $this);
100
        }
101
102
        return $this->status;
103
    }
104
105
    /**
106
     * Retrieve/Set json response options.
107
     *
108
     * @param  string|array|null                         $key
109
     * @param  mixed|null                                $default
110
     * @return \ArinaSystems\JsonResponse\Option|mixed
111
     */
112
    public function options($key = null, $default = null)
113
    {
114
        if (is_array($key)) {
115
            return $this->setOptions($key);
116
        }
117
118
        if (!is_null($key)) {
119
            return $this->options->get($key, $default);
120
        }
121
122
        return $this->options ?? $default;
123
    }
124
125
    /**
126
     * Retrieve/Set json response attributes.
127
     *
128
     * @param  string|array|null                            $key
129
     * @param  mixed|null                                   $default
130
     * @return \ArinaSystems\JsonResponse\Attribute|mixed
131
     */
132
    public function attributes($key = null, $default = null)
133
    {
134
        if (is_array($key)) {
135
            return $this->setAttributes($key);
136
        }
137
138
        if (is_string($key)) {
139
            return $this->attributes->get($key, $default);
140
        }
141
142
        return $this->attributes ?? $default;
143
    }
144
145
    /**
146
     * Set json response options.
147
     *
148
     * @param  \ArinaSystems\JsonResponse\Option|array|string $options
149
     * @param  null|mixed                                     $value
150
     * @return self
151
     */
152
    public function setOptions($options, $value = null)
153
    {
154
        if (is_a($options, Option::class)) {
155
            $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...
156
        }
157
158
        $this->options->set($options, $value);
159
160
        return $this;
161
    }
162
163
    /**
164
     * Set json response attributes.
165
     *
166
     * @param  \ArinaSystems\JsonResponse\Attribute|array|string $attributes
167
     * @param  null|mixed                                        $value
168
     * @return self
169
     */
170
    public function setAttributes($attributes, $value = null)
171
    {
172
        if (is_a($attributes, Attribute::class)) {
173
            $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...
174
        }
175
176
        $this->attributes->set($attributes, $value);
177
        return $this;
178
    }
179
180
    /**
181
     * Get the json response data array.
182
     *
183
     * @param  bool    $error
184
     * @return array
185
     */
186
    protected function build(bool $error = false): array
187
    {
188
        $structure = $this->structure($error);
189
190
        if (!$this->options('debug')) {
191
            unset($structure['debug']);
192
        }
193
194
        $response = array_intersect_key($this->attributes->all('value'), $structure);
195
196
        return $response;
197
    }
198
199
    /**
200
     * Get response data structure.
201
     *
202
     * @param  bool    $error
203
     * @return array
204
     */
205
    public function structure(bool $error = false): array
206
    {
207
        $structure = Arr::where($this->attributes->all('on-' . ($error ? 'error' : 'response')), function ($onStructure) {
208
            return boolval($onStructure);
209
        });
210
211
        $structure = array_keys($structure);
212
213
        return array_flip($structure);
214
    }
215
216
    /**
217
     * Get the current instance of json response builder.
218
     *
219
     * @return ArinaSystems\JsonResponse\JsonResponse
220
     */
221
    public function instance(): self
222
    {
223
        return $this;
224
    }
225
}
226