Attachment   A
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 300
Duplicated Lines 0 %

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 42
eloc 114
c 6
b 1
f 1
dl 0
loc 300
rs 9.0399

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __call() 0 18 4
B findType() 0 29 9
A __set() 0 4 1
A __construct() 0 14 2
A __get() 0 6 2
A getMimeType() 0 2 1
A mask() 0 7 3
A getMessage() 0 2 1
A getAttributes() 0 2 1
B fetch() 0 29 6
A setName() 0 7 4
A getExtension() 0 15 3
A setMask() 0 6 2
A getMask() 0 2 1
A save() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like Attachment 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.

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 Attachment, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
* File:     Attachment.php
4
* Category: -
5
* Author:   M. Goldenbaum
6
* Created:  16.03.18 19:37
7
* Updated:  -
8
*
9
* Description:
10
*  -
11
*/
12
13
namespace Webklex\PHPIMAP;
14
15
use Illuminate\Support\Str;
16
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
17
use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
18
use Webklex\PHPIMAP\Support\Masks\AttachmentMask;
19
20
/**
21
 * Class Attachment
22
 *
23
 * @package Webklex\PHPIMAP
24
 *
25
 * @property integer part_number
26
 * @property integer size
27
 * @property string content
28
 * @property string type
29
 * @property string content_type
30
 * @property string id
31
 * @property string name
32
 * @property string disposition
33
 * @property string img_src
34
 *
35
 * @method integer getPartNumber()
36
 * @method integer setPartNumber(integer $part_number)
37
 * @method string  getContent()
38
 * @method string  setContent(string $content)
39
 * @method string  getType()
40
 * @method string  setType(string $type)
41
 * @method string  getContentType()
42
 * @method string  setContentType(string $content_type)
43
 * @method string  getId()
44
 * @method string  setId(string $id)
45
 * @method string  getSize()
46
 * @method string  setSize(integer $size)
47
 * @method string  getName()
48
 * @method string  getDisposition()
49
 * @method string  setDisposition(string $disposition)
50
 * @method string  setImgSrc(string $img_src)
51
 */
52
class Attachment {
53
54
    /**
55
     * @var Message $oMessage
56
     */
57
    protected $oMessage;
58
59
    /**
60
     * Used config
61
     *
62
     * @var array $config
63
     */
64
    protected $config = [];
65
66
    /** @var Part $part */
67
    protected $part;
68
69
    /**
70
     * Attribute holder
71
     *
72
     * @var array $attributes
73
     */
74
    protected $attributes = [
75
        'content' => null,
76
        'type' => null,
77
        'part_number' => 0,
78
        'content_type' => null,
79
        'id' => null,
80
        'name' => null,
81
        'disposition' => null,
82
        'img_src' => null,
83
        'size' => null,
84
    ];
85
86
    /**
87
     * Default mask
88
     *
89
     * @var string $mask
90
     */
91
    protected $mask = AttachmentMask::class;
92
93
    /**
94
     * Attachment constructor.
95
     * @param Message   $oMessage
96
     * @param Part      $part
97
     */
98
    public function __construct(Message $oMessage, Part $part) {
99
        $this->config = ClientManager::get('options');
100
101
        $this->oMessage = $oMessage;
102
        $this->part = $part;
103
        $this->part_number = $part->part_number;
104
105
        $default_mask = $this->oMessage->getClient()->getDefaultAttachmentMask();
106
        if($default_mask != null) {
107
            $this->mask = $default_mask;
108
        }
109
110
        $this->findType();
111
        $this->fetch();
112
    }
113
114
    /**
115
     * Call dynamic attribute setter and getter methods
116
     * @param string $method
117
     * @param array $arguments
118
     *
119
     * @return mixed
120
     * @throws MethodNotFoundException
121
     */
122
    public function __call(string $method, array $arguments) {
123
        if(strtolower(substr($method, 0, 3)) === 'get') {
124
            $name = Str::snake(substr($method, 3));
125
126
            if(isset($this->attributes[$name])) {
127
                return $this->attributes[$name];
128
            }
129
130
            return null;
131
        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
132
            $name = Str::snake(substr($method, 3));
133
134
            $this->attributes[$name] = array_pop($arguments);
135
136
            return $this->attributes[$name];
137
        }
138
139
        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
140
    }
141
142
    /**
143
     * Magic setter
144
     * @param $name
145
     * @param $value
146
     *
147
     * @return mixed
148
     */
149
    public function __set($name, $value) {
150
        $this->attributes[$name] = $value;
151
152
        return $this->attributes[$name];
153
    }
154
155
    /**
156
     * magic getter
157
     * @param $name
158
     *
159
     * @return mixed|null
160
     */
161
    public function __get($name) {
162
        if(isset($this->attributes[$name])) {
163
            return $this->attributes[$name];
164
        }
165
166
        return null;
167
    }
168
169
    /**
170
     * Determine the structure type
171
     */
172
    protected function findType() {
173
        switch ($this->part->type) {
174
            case IMAP::ATTACHMENT_TYPE_MESSAGE:
175
                $this->type = 'message';
176
                break;
177
            case IMAP::ATTACHMENT_TYPE_APPLICATION:
178
                $this->type = 'application';
179
                break;
180
            case IMAP::ATTACHMENT_TYPE_AUDIO:
181
                $this->type = 'audio';
182
                break;
183
            case IMAP::ATTACHMENT_TYPE_IMAGE:
184
                $this->type = 'image';
185
                break;
186
            case IMAP::ATTACHMENT_TYPE_VIDEO:
187
                $this->type = 'video';
188
                break;
189
            case IMAP::ATTACHMENT_TYPE_MODEL:
190
                $this->type = 'model';
191
                break;
192
            case IMAP::ATTACHMENT_TYPE_TEXT:
193
                $this->type = 'text';
194
                break;
195
            case IMAP::ATTACHMENT_TYPE_MULTIPART:
196
                $this->type = 'multipart';
197
                break;
198
            default:
199
                $this->type = 'other';
200
                break;
201
        }
202
    }
203
204
    /**
205
     * Fetch the given attachment
206
     */
207
    protected function fetch() {
208
209
        $content = $this->part->content;
210
211
        $this->content_type = $this->part->content_type;
212
        $this->content = $this->oMessage->decodeString($content, $this->part->encoding);
213
214
        if (($id = $this->part->id) !== null) {
0 ignored issues
show
introduced by
The condition $id = $this->part->id !== null is always true.
Loading history...
215
            $this->id = str_replace(['<', '>'], '', $id);
216
        }else{
217
            $this->id = hash("sha256", (string)microtime(true));
218
        }
219
220
        $this->size = $this->part->bytes;
221
        $this->disposition = $this->part->disposition;
222
223
        if (($filename = $this->part->filename) !== null) {
0 ignored issues
show
introduced by
The condition $filename = $this->part->filename !== null is always true.
Loading history...
224
            $this->setName($filename);
225
        } elseif (($name = $this->part->name) !== null) {
226
            $this->setName($name);
227
        }else {
228
            $this->setName("undefined");
229
        }
230
231
        if (IMAP::ATTACHMENT_TYPE_MESSAGE == $this->part->type) {
232
            if ($this->part->ifdescription) {
233
                $this->setName($this->part->description);
234
            } else {
235
                $this->setName($this->part->subtype);
236
            }
237
        }
238
    }
239
240
    /**
241
     * Save the attachment content to your filesystem
242
     * @param string $path
243
     * @param string|null $filename
244
     *
245
     * @return boolean
246
     */
247
    public function save(string $path, $filename = null): bool {
248
        $filename = $filename ?: $this->getName();
249
250
        return file_put_contents($path.$filename, $this->getContent()) !== false;
251
    }
252
253
    /**
254
     * Set the attachment name and try to decode it
255
     * @param $name
256
     */
257
    public function setName($name) {
258
        $decoder = $this->config['decoder']['attachment'];
259
        if ($name !== null) {
260
            if($decoder === 'utf-8' && extension_loaded('imap')) {
261
                $this->name = \imap_utf8($name);
262
            }else{
263
                $this->name = mb_decode_mimeheader($name);
264
            }
265
        }
266
    }
267
268
    /**
269
     * Get the attachment mime type
270
     *
271
     * @return string|null
272
     */
273
    public function getMimeType(){
274
        return (new \finfo())->buffer($this->getContent(), FILEINFO_MIME_TYPE);
275
    }
276
277
    /**
278
     * Try to guess the attachment file extension
279
     *
280
     * @return string|null
281
     */
282
    public function getExtension(){
283
        $guesser = "\Symfony\Component\Mime\MimeTypes";
284
        if (class_exists($guesser) !== false) {
285
            /** @var Symfony\Component\Mime\MimeTypes $guesser */
286
            $extensions = $guesser::getDefault()->getExtensions($this->getMimeType());
287
            return $extensions[0] ?? null;
288
        }
289
290
        $deprecated_guesser = "\Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser";
291
        if (class_exists($deprecated_guesser) !== false){
292
            /** @var \Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser $deprecated_guesser */
293
            return $deprecated_guesser::getInstance()->guess($this->getMimeType());
294
        }
295
296
        return null;
297
    }
298
299
    /**
300
     * Get all attributes
301
     *
302
     * @return array
303
     */
304
    public function getAttributes(): array {
305
        return $this->attributes;
306
    }
307
308
    /**
309
     * @return Message
310
     */
311
    public function getMessage(): Message {
312
        return $this->oMessage;
313
    }
314
315
    /**
316
     * Set the default mask
317
     * @param $mask
318
     *
319
     * @return $this
320
     */
321
    public function setMask($mask): Attachment {
322
        if(class_exists($mask)){
323
            $this->mask = $mask;
324
        }
325
326
        return $this;
327
    }
328
329
    /**
330
     * Get the used default mask
331
     *
332
     * @return string
333
     */
334
    public function getMask(): string {
335
        return $this->mask;
336
    }
337
338
    /**
339
     * Get a masked instance by providing a mask name
340
     * @param string|null $mask
341
     *
342
     * @return mixed
343
     * @throws MaskNotFoundException
344
     */
345
    public function mask($mask = null){
346
        $mask = $mask !== null ? $mask : $this->mask;
347
        if(class_exists($mask)){
348
            return new $mask($this);
349
        }
350
351
        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
352
    }
353
}