Completed
Branch dev (276354)
by Raffael
15:43
created

Controller::_getNode()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 8.7972
c 0
b 0
f 0
cc 4
eloc 17
nc 4
nop 6
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * balloon
7
 *
8
 * @copyright   Copryright (c) 2012-2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Balloon\App\Api;
13
14
use Balloon\App\Api\v2\Collections as ApiCollection;
15
use Balloon\App\Api\v2\Files as ApiFile;
16
use Balloon\Filesystem;
17
use Balloon\Filesystem\Node\Collection;
18
use Balloon\Filesystem\Node\File;
19
use Balloon\Filesystem\Node\NodeInterface;
20
use Closure;
21
use Generator;
22
use Micro\Http\ExceptionInterface;
23
use Micro\Http\Response;
24
25
abstract class Controller
26
{
27
    /**
28
     * Filesystem.
29
     *
30
     * @var Filesystem
31
     */
32
    protected $fs;
33
34
    /**
35
     * @apiDefine _getNode
36
     *
37
     * @apiParam (GET Parameter) {string} id Either id or p (path) of a node must be given.
38
     * @apiParam (GET Parameter) {string} p Either id or p (path) of a node must be given.
39
     * @apiError (General Error Response) {number} status Status Code
40
     * @apiError (General Error Response) {object[]} data Error body
41
     * @apiError (General Error Response) {string} data.error Exception
42
     * @apiError (General Error Response) {string} data.message Message
43
     * @apiError (General Error Response) {number} data.code Error code
44
     *
45
     * @apiErrorExample {json} Error-Response (Invalid Parameter):
46
     * HTTP/1.1 400 Bad Request
47
     * {
48
     *      "status": 400,
49
     *      "data": {
50
     *          "error": "Balloon\\Exception\\InvalidArgument",
51
     *          "message": "invalid node id specified",
52
     *          "code": 0
53
     *      }
54
     * }
55
     *
56
     * @apiErrorExample {json} Error-Response (Insufficient Access):
57
     * HTTP/1.1 403 Forbidden
58
     * {
59
     *      "status": 403,
60
     *      "data": {
61
     *          "error": "Balloon\\Exception\\Forbidden",
62
     *          "message": "not allowed to read node 51354d073c58891f058b4580",
63
     *          "code": 40
64
     *      }
65
     * }
66
     *
67
     * @apiErrorExample {json} Error-Response (Not found):
68
     * HTTP/1.1 404 Not Found
69
     * {
70
     *      "status": 404,
71
     *      "data": {
72
     *          "error": "Balloon\\Exception\\NotFound",
73
     *          "message": "node 51354d073c58891f058b4580 not found",
74
     *          "code": 49
75
     *      }
76
     * }
77
     *
78
     * @param mixed $id
79
     * @param mixed $p
80
     */
81
82
    /**
83
     * @apiDefine _multiError
84
     *
85
     * @apiErrorExample {json} Error-Response (Multi node error):
86
     * HTTP/1.1 400 Bad Request
87
     * {
88
     *     "status": 400,
89
     *     "data": [
90
     *         {
91
     *              id: "51354d073c58891f058b4580",
92
     *              name: "file.zip",
93
     *              error: "Balloon\\Exception\\Conflict",
94
     *              message: "node already exists",
95
     *              code: 30
96
     *         }
97
     *     ]
98
     * }
99
     */
100
101
    /**
102
     * @apiDefine _writeAction
103
     *
104
     * @apiErrorExample {json} Error-Response (Conflict):
105
     * HTTP/1.1 400 Bad Request
106
     * {
107
     *      "status": 400,
108
     *      "data": {
109
     *          "error": "Balloon\\Exception\\Conflict",
110
     *          "message": "a node called myname does already exists",
111
     *          "code": 17
112
     *      }
113
     * }
114
     */
115
116
    /**
117
     * @apiDefine _conflictNode
118
     * @apiParam (GET Parameter) {number} [conflict=0] Decides how to handle a conflict if a node with the same name already exists at the destination.
119
     * Possible values are:</br>
120
     *  - 0 No action</br>
121
     *  - 1 Automatically rename the node</br>
122
     *  - 2 Overwrite the destination (merge)</br>
123
     */
124
125
    /**
126
     * @apiDefine _getNodes
127
     *
128
     * @apiParam (GET Parameter) {string[]} id Either a single id as string or multiple as an array or a single p (path) as string or multiple paths as array must be given.
129
     * @apiParam (GET Parameter) {string[]} p Either a single id as string or multiple as an array or a single p (path) as string or multiple paths as array must be given.
130
     * @apiError (General Error Response) {number} status Status Code
131
     * @apiError (General Error Response) {object[]} data Error body
132
     * @apiError (General Error Response) {string} data.error Exception
133
     * @apiError (General Error Response) {string} data.message Message
134
     * @apiError (General Error Response) {number} data.code General error messages of type  Balloon\\Exception do not usually have an error code
135
     *
136
     * @apiErrorExample {json} Error-Response (Invalid Parameter):
137
     * HTTP/1.1 400 Bad Request
138
     * {
139
     *      "status": 400,
140
     *      "data": {
141
     *          "error": "Balloon\\Exception\\InvalidArgument",
142
     *          "message": "invalid node id specified",
143
     *          "code": 0
144
     *      }
145
     * }
146
     *
147
     * @apiErrorExample {json} Error-Response (Insufficient Access):
148
     * HTTP/1.1 403 Forbidden
149
     * {
150
     *      "status": 403,
151
     *      "data": {
152
     *          "error": "Balloon\\Exception\\Forbidden",
153
     *          "message": "not allowed to read node 51354d073c58891f058b4580",
154
     *          "code": 40
155
     *      }
156
     * }
157
     *
158
     * @apiErrorExample {json} Error-Response (Not found):
159
     * HTTP/1.1 404 Not Found
160
     * {
161
     *      "status": 404,
162
     *      "data": {
163
     *          "error": "Balloon\\Exception\\NotFound",
164
     *          "message": "node 51354d073c58891f058b4580 not found",
165
     *          "code": 49
166
     *      }
167
     * }
168
     */
169
170
    /**
171
     * Do bulk operations.
172
     *
173
     * @param array|string $id
174
     * @param array|string $p
175
     * @param Closure      $action
176
     */
177
    protected function bulk($id, $p, Closure $action): Response
178
    {
179
        if (is_array($id) || is_array($p)) {
180
            $body = [];
181
            foreach ($this->_getNodes($id, $p) as $node) {
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 177 can also be of type array; however, Balloon\App\Api\Controller::_getNodes() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 177 can also be of type array; however, Balloon\App\Api\Controller::_getNodes() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
182
                try {
183
                    $body[(string) $node->getId()] = $action->call($this, $node);
184
                } catch (\Exception $e) {
185
                    $body[(string) $node->getId()] = [
186
                        'code' => $e instanceof ExceptionInterface ? $e->getStatusCode() : 400,
0 ignored issues
show
Bug introduced by
The class Micro\Http\ExceptionInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
187
                        'data' => [
188
                            'error' => get_class($e),
189
                            'message' => $e->getMessage(),
190
                            'code' => $e->getCode(),
191
                        ],
192
                    ];
193
                }
194
            }
195
196
            return (new Response())->setCode(207)->setBody($body);
197
        }
198
199
        $body = $action->call($this, $this->_getNode($id, $p));
200
        $response = (new Response())->setCode($body['code']);
201
202
        if (isset($body['data'])) {
203
            $response->setBody($body['data']);
204
        }
205
206
        return $response;
207
    }
208
209
    /**
210
     * Get node.
211
     *
212
     * @param string $id
213
     * @param string $path
214
     * @param string $class      Force set node type
215
     * @param bool   $multiple   Allow $id to be an array
216
     * @param bool   $allow_root Allow instance of root collection
217
     * @param bool   $deleted    How to handle deleted node
218
     *
219
     * @return NodeInterface
220
     */
221
    protected function _getNode(
222
        ?string $id = null,
223
        ?string $path = null,
224
        ?string $class = null,
225
        bool $multiple = false,
226
        bool $allow_root = false,
227
        int $deleted = 2
228
    ): NodeInterface {
229
        if (null === $class) {
230
            switch (get_class($this)) {
231
                case ApiFile::class:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
232
                    $class = File::class;
233
234
                break;
235
                case ApiCollection::class:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
236
                    $class = Collection::class;
237
238
                break;
239
            }
240
        }
241
242
        return $this->fs->getNode($id, $path, $class, $multiple, $allow_root, $deleted);
243
    }
244
245
    /**
246
     * Get nodes.
247
     *
248
     * @param string $id
249
     * @param string $path
250
     * @param string $class   Force set node type
251
     * @param bool   $deleted How to handle deleted node
252
     *
253
     * @return Generator
254
     */
255
    protected function _getNodes(
256
        $id = null,
257
        $path = null,
258
        ?string $class = null,
259
        int $deleted = 2
260
    ): Generator {
261
        if (null === $class) {
262
            switch (get_class($this)) {
263
                case ApiFile::class:
264
                    $class = File::class;
265
266
                break;
267
                case ApiCollection::class:
268
                    $class = Collection::class;
269
270
                break;
271
            }
272
        }
273
274
        return $this->fs->getNodes($id, $path, $class, $deleted);
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 256 can also be of type string; however, Balloon\Filesystem::getNodes() does only seem to accept array|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $path defined by parameter $path on line 257 can also be of type string; however, Balloon\Filesystem::getNodes() does only seem to accept array|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
275
    }
276
}
277