Completed
Branch dev (bc6e47)
by Raffael
02:23
created

AttributeDecorator   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 308
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 42
cbo 6
dl 0
loc 308
rs 8.295
c 0
b 0
f 0
lcom 1

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A decorate() 0 22 3
A addDecorator() 0 6 1
C prepare() 0 40 16
B getAttributes() 0 45 5
B getTimeAttributes() 0 25 3
C getTypeAttributes() 0 47 9
A translateAttributes() 0 16 4

How to fix   Complexity   

Complex Class

Complex classes like AttributeDecorator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AttributeDecorator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Balloon
7
 *
8
 * @author      Raffael Sahli <[email protected]>
9
 * @copyright   Copryright (c) 2012-2017 gyselroth GmbH (https://gyselroth.com)
10
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
11
 */
12
13
namespace Balloon\Filesystem\Node;
14
15
use Balloon\Filesystem;
16
use Balloon\Filesystem\Acl;
17
use Balloon\Helper;
18
use Balloon\Server;
19
use Closure;
20
21
class AttributeDecorator
22
{
23
    /**
24
     * Server.
25
     *
26
     * @var Server
27
     */
28
    protected $server;
29
30
    /**
31
     * Filesystem.
32
     *
33
     * @var Filesystem
34
     */
35
    protected $fs;
36
37
    /**
38
     * Acl.
39
     *
40
     * @var Acl
41
     */
42
    protected $acl;
43
44
    /**
45
     * Custom attributes.
46
     *
47
     * @var array
48
     */
49
    protected $custom = [];
50
51
    /**
52
     * Init.
53
     *
54
     * @param Server $server
55
     * @param Acl    $acl
56
     */
57
    public function __construct(Server $server, Acl $acl)
58
    {
59
        $this->server = $server;
60
        $this->fs = $server->getFilesystem();
61
        $this->acl = $acl;
62
    }
63
64
    /**
65
     * Decorate attributes.
66
     *
67
     * @param NodeInterface $node
68
     * @param array         $attributes
69
     *
70
     * @return array
71
     */
72
    public function decorate(NodeInterface $node, ?array $attributes): array
73
    {
74
        if (null === $attributes) {
75
            $attributes = [];
76
        }
77
78
        $requested = $this->prepare($node, $attributes);
79
        $attributes = $node->getAttributes();
80
81
        $attrs = array_merge(
82
            $this->getAttributes($node, $attributes),
83
            $this->getTimeAttributes($node, $attributes),
84
            $this->getTypeAttributes($node, $attributes),
85
            $this->custom
86
        );
87
88
        if (0 === count($requested['attributes'])) {
89
            return $this->translateAttributes($node, $attrs, $requested);
90
        }
91
92
        return $this->translateAttributes($node, array_intersect_key($attrs, array_flip($requested['attributes'])), $requested);
93
    }
94
95
    /**
96
     * Add decorator.
97
     *
98
     * @param string  $attribute
99
     * @param Closure $decorator
100
     *
101
     * @return AttributeDecorator
102
     */
103
    public function addDecorator(string $attribute, Closure $decorator): self
104
    {
105
        $this->custom[$attribute] = $decorator;
106
107
        return $this;
108
    }
109
110
    /**
111
     * Prepare requested attributes.
112
     *
113
     * @param NodeInterface $node
114
     * @param array         $attributes
115
     *
116
     * @return array
117
     */
118
    protected function prepare(NodeInterface $node, ?array $attributes): array
119
    {
120
        $clean = [
121
            'attributes' => [],
122
            'meta' => [],
123
            'share' => [],
124
            'parent' => [],
125
            'owner' => [],
126
            'shareowner' => [],
127
        ];
128
129
        foreach ($attributes as $key => $attr) {
130
            $keys = explode('.', $attr);
131
            $prefix = array_shift($keys);
132
133
            if ('file' === $prefix && ($node instanceof Collection)) {
134
                continue;
135
            }
136
            if ('collection' === $prefix && ($node instanceof File)) {
137
                continue;
138
            }
139
            if (('file' === $prefix || 'collection' === $prefix) && count($keys) > 1) {
140
                $prefix = array_shift($keys);
141
            }
142
143
            if (('file' === $prefix || 'collection' === $prefix) && 1 === count($keys)) {
144
                $clean['attributes'][] = $keys[0];
145
            } elseif (0 === count($keys)) {
146
                $clean['attributes'][] = $attr;
147
            } elseif ('meta' === $prefix && 1 === count($keys)) {
148
                $clean['attributes'][] = 'meta';
149
                $clean['meta'][] = $keys[0];
150
            } elseif (isset($clean[$prefix])) {
151
                $clean['attributes'][] = $prefix;
152
                $clean[$prefix][] = $attr;
153
            }
154
        }
155
156
        return $clean;
157
    }
158
159
    /**
160
     * Get Attributes.
161
     *
162
     * @param NodeInterface
163
     * @param array $attributes
164
     *
165
     * @return array
166
     */
167
    protected function getAttributes(NodeInterface $node, array $attributes): array
168
    {
169
        $acl = $this->acl;
170
        $server = $this->server;
171
172
        return [
173
            'id' => (string) $attributes['id'],
174
            'name' => (string) $attributes['name'],
175
            'mime' => (string) $attributes['mime'],
176
            'readonly' => (bool) $attributes['readonly'],
177
            'directory' => $node instanceof Collection,
178
            'meta' => function ($node, $requested) {
0 ignored issues
show
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
179
                return (object) $node->getMetaAttribute([]);
180
            },
181
            'size' => function ($node, $requested) {
0 ignored issues
show
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
182
                return $node->getSize();
183
            },
184
            'path' => function ($node, $requested) {
0 ignored issues
show
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
185
                try {
186
                    return $node->getPath();
187
                } catch (\Exception $e) {
188
                    return null;
189
                }
190
            },
191
            'parent' => function ($node, $requested) {
192
                $parent = $node->getParent();
193
194
                if (null === $parent || $parent->isRoot()) {
195
                    return null;
196
                }
197
198
                return $this->decorate($node->getParent(), $requested['parent']);
199
            },
200
            'access' => function ($node, $requested) use ($acl) {
0 ignored issues
show
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
201
                return $acl->getAclPrivilege($node);
202
            },
203
            'owner' => function ($node, $requested) use ($server) {
204
                try {
205
                    return $server->getUserById($node->getOwner())->getAttribute($requested['owner']);
206
                } catch (\Exception $e) {
207
                    return null;
208
                }
209
            },
210
        ];
211
    }
212
213
    /**
214
     * Get Attributes.
215
     *
216
     * @param NodeInterface
217
     * @param array $attributes
218
     *
219
     * @return array
220
     */
221
    protected function getTimeAttributes(NodeInterface $node, array $attributes): array
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
222
    {
223
        return [
224
            'created' => function ($node, $requested) use ($attributes) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
225
                return Helper::DateTimeToUnix($attributes['created']);
226
            },
227
            'changed' => function ($node, $requested) use ($attributes) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
228
                return Helper::DateTimeToUnix($attributes['changed']);
229
            },
230
            'deleted' => function ($node, $requested) use ($attributes) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
231
                if (false === $attributes['deleted']) {
232
                    return false;
233
                }
234
235
                return Helper::DateTimeToUnix($attributes['deleted']);
236
            },
237
            'destroy' => function ($node, $requested) use ($attributes) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
238
                if (false === $attributes['destroy']) {
239
                    return false;
240
                }
241
242
                return Helper::DateTimeToUnix($attributes['destroy']);
243
            },
244
        ];
245
    }
246
247
    /**
248
     * Get Attributes.
249
     *
250
     * @param NodeInterface
251
     * @param array $attributes
252
     *
253
     * @return array
254
     */
255
    protected function getTypeAttributes(NodeInterface $node, array $attributes): array
256
    {
257
        $server = $this->server;
258
        $fs = $this->fs;
259
260
        if ($node instanceof File) {
261
            return [
262
                'version' => $attributes['version'],
263
                'hash' => $attributes['hash'],
264
            ];
265
        }
266
267
        return [
268
            'shared' => $node->isShared(),
269
            'reference' => $node->isReference(),
270
            'filter' => function ($node, $requested) use ($attributes) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
271
                if (null === $attributes['filter']) {
272
                    return null;
273
                }
274
275
                return json_decode($attributes['filter']);
276
            },
277
            'share' => function ($node, $requested) {
278
                if ($node->isShare() || !$node->isSpecial()) {
279
                    return null;
280
                }
281
282
                try {
283
                    return $this->decorate($node->getShareNode(), $requested['share']);
284
                } catch (\Exception $e) {
285
                    return null;
286
                }
287
            },
288
            'shareowner' => function ($node, $requested) use ($server, $fs) {
289
                if ($node->isShare() || !$node->isSpecial()) {
290
                    return null;
291
                }
292
293
                try {
294
                    return $server->getUserById($fs->findRawNode($node->getShareId())['owner'])
295
                        ->getAttribute($requested['shareowner']);
296
                } catch (\Exception $e) {
297
                    return null;
298
                }
299
            },
300
        ];
301
    }
302
303
    /**
304
     * Execute closures.
305
     *
306
     * @param NodeInterface
307
     * @param array $attributes
308
     * @param array $requested
309
     *
310
     * @return array
311
     */
312
    protected function translateAttributes(NodeInterface $node, array $attributes, array $requested): array
313
    {
314
        foreach ($attributes as $key => &$value) {
315
            if ($value instanceof Closure) {
316
                $result = $value($node, $requested);
317
318
                if (null === $result) {
319
                    unset($attributes[$key]);
320
                } else {
321
                    $value = $result;
322
                }
323
            }
324
        }
325
326
        return $attributes;
327
    }
328
}
329