BaseApiController::renderError()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 2
cts 2
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 5
crap 1
1
<?php
2
3
namespace TreeHouse\BaseApiBundle\Controller;
4
5
use JMS\Serializer\SerializationContext;
6
use JMS\Serializer\SerializerInterface;
7
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
8
use Symfony\Component\HttpFoundation\JsonResponse;
9
use Symfony\Component\HttpFoundation\ParameterBag;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\HttpFoundation\Response;
12
use Symfony\Component\Security\Core\User\UserInterface;
13
use Symfony\Component\Validator\Validator\ValidatorInterface;
14
use TreeHouse\BaseApiBundle\Security\SecurityContext;
15
use TreeHouse\BaseApiBundle\Exception\ValidationException;
16
17
abstract class BaseApiController extends Controller
18
{
19
    /**
20
     * @param int $statusCode
21
     *
22
     * @return JsonResponse
23
     */
24 10
    protected function createResponse($statusCode = Response::HTTP_OK)
25 1
    {
26 10
        $response = new JsonResponse();
27 10
        $response->setStatusCode($statusCode);
28 10
        $response->headers->set('Access-Control-Allow-Origin', $this->container->getParameter('tree_house.api.allowed_origins'));
29
30 10
        return $response;
31
    }
32
33
    /**
34
     * Renders an successful Api call.
35
     *
36
     * @see renderResponse()
37
     *
38
     * @param Request              $request
39
     * @param mixed                $result   The result of the call.
40
     * @param int                  $code     The response code.
41
     * @param array                $groups   JMS\Serializer groups
42
     * @param array                $metadata Extra metadata to put in the response
43
     * @param SerializationContext $context  The context to use for serializing the result data
44
     *
45
     * @return JsonResponse
46
     */
47 2
    protected function renderOk(Request $request, $result, $code = 200, array $groups = [], array $metadata = [], SerializationContext $context = null)
48 1
    {
49 2
        $data = array();
50
51 2
        if (!empty($metadata)) {
52 2
            $data['metadata'] = $metadata;
53 1
        }
54
55 2
        if (null !== $result) {
56 2
            $data['result'] = $result;
57 1
        }
58
59 2
        return $this->renderResponse($request, $data, true, $code, $groups, $context);
60
    }
61
62
    /**
63
     * Renders an Api error.
64
     *
65
     * @see renderResponse()
66
     *
67
     * @param Request              $request
68
     * @param int                  $code    The response code
69
     * @param string|array         $error   The error
70
     * @param array                $groups  JMS\Serializer groups
71
     * @param SerializationContext $context The context to use for serializing the result data
72
     *
73
     * @return JsonResponse
74
     */
75 4
    protected function renderError(
76
        Request $request,
77
        $code = 400,
78
        $error,
79
        array $groups = [],
80
        SerializationContext $context = null
81
    ) {
82 4
        return $this->renderResponse($request, ['error' => $error], false, $code, $groups, $context);
83
    }
84
85
    /**
86
     * Renders a JSON response in a generic structure:
87
     *
88
     * <code>
89
     * {
90
     *    "ok": true,
91
     *    "result": {
92
     *      [...]
93
     *    }
94
     * }
95
     * </code>
96
     *
97
     * Or in case of an error:
98
     *
99
     * <code>
100
     * {
101
     *    "ok": false,
102
     *    "error": "message"
103
     * }
104
     * </code>
105
     *
106
     * @param Request              $request
107
     * @param array                $result     The result of the call.
108
     * @param bool                 $ok         Whether the call was successful or not.
109
     * @param int                  $statusCode The response code.
110
     * @param array                $groups     JMS\Serializer groups
111
     * @param SerializationContext $context    The context to use for serializing the result data
112
     *
113
     * @return JsonResponse
114
     */
115 6
    protected function renderResponse(
116
        Request $request,
117
        array $result = [],
118
        $ok,
119
        $statusCode,
120
        array $groups = [],
121
        SerializationContext $context = null
122
    ) {
123 6
        $response = $this->createResponse($statusCode);
124
125 6
        $data = array_merge(
126 6
            ['ok' => $ok],
127
            $result
128 3
        );
129
130 6
        if ($context === null) {
131 6
            $context = SerializationContext::create();
132 3
        }
133
134 6
        if ($groups) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $groups 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...
135
            $context->setGroups($groups);
136
        }
137
138 6
        $context->setSerializeNull(true);
139
140
        // the json response needs to have data set as an array, rather than setting the content directly.
141
        // this is because other options use that to overwrite the content (like jsonp).
142
        // so unfortunately we have to double-convert the data, since the serializer won't convert to
143
        // arrays just yet :(
144 6
        $json = $this->getSerializer()->serialize($data, 'json', $context);
145 6
        $response->setData(json_decode($json, true));
146
147
        // handle JSON-P requests
148 6
        $callback = $request->query->get('callback', '');
149 6
        if (!empty($callback)) {
150
            try {
151 4
                $response->setCallback($callback);
152 3
            } catch (\InvalidArgumentException $e) {
153
                // remove the callback from the query parameters, and render an error
154 2
                $request->query->remove('callback');
155
156 2
                return $this->renderError($request, Response::HTTP_BAD_REQUEST, $e->getMessage(), $groups, $context);
157
            }
158 1
        }
159
160 6
        return $response;
161
    }
162
163
    /**
164
     * @param Request $request
165
     * @param string  $serializeType
166
     *
167
     * @return ParameterBag|object
168
     */
169 4
    protected function getRequestData(Request $request, $serializeType = null)
170
    {
171 4
        $data = null;
0 ignored issues
show
Unused Code introduced by
$data is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
172
173 4
        switch ($request->getMethod()) {
174 4
            case 'GET':
175 4
                $data = $request->query;
176 4
                break;
177
178 2
            case 'POST':
179 2
            case 'PUT':
180 2
            case 'DELETE':
181 2
                $data = $request->getContent();
182 2
                break;
183
184 1
            default:
185 2
                $data = $request->query;
186 2
                break;
187 2
        }
188
189 4
        if ($serializeType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $serializeType of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
190 2
            $data = $this->getSerializer()->deserialize($data, $serializeType, 'json');
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->getSerializer()->...$serializeType, 'json') on line 190 can also be of type object<Symfony\Component...oundation\ParameterBag> or resource; however, JMS\Serializer\SerializerInterface::deserialize() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
191 1
        }
192
193 4
        return $data;
194
    }
195
196
    /**
197
     * Validates an API request
198
     *
199
     * @param object $request
200
     *
201
     * @throws ValidationException
202
     */
203 4
    protected function validate($request)
204
    {
205 4
        $violations = $this->getValidator()->validate($request);
206
207 4
        if (count($violations) > 0) {
208 2
            throw ValidationException::create($violations);
209
        }
210 2
    }
211
212
    /**
213
     * @return UserInterface
214
     */
215 4
    protected function getApiUser()
216
    {
217 4
        if ($token = $this->getSecurityContext()->getToken()) {
218 2
            return $token->getUser();
219
        }
220
221 2
        return null;
222
    }
223
224
    /**
225
     * @return SecurityContext
226
     */
227 4
    protected function getSecurityContext()
228
    {
229 4
        return $this->get('tree_house.api.security.security_context');
230
    }
231
232
    /**
233
     * @return SerializerInterface
234
     */
235 8
    protected function getSerializer()
236
    {
237 8
        return $this->get('jms_serializer');
238
    }
239
240
    /**
241
     * @return ValidatorInterface
242
     */
243 4
    protected function getValidator()
244
    {
245 4
        return $this->get('validator');
246
    }
247
}
248