Passed
Push — master ( 00f43d...a0d391 )
by Malte
02:56
created

Attachment   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 288
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 111
dl 0
loc 288
rs 7.92
c 0
b 0
f 0
wmc 51

16 Methods

Rating   Name   Duplication   Size   Complexity  
A save() 0 7 4
A setName() 0 5 2
A __set() 0 4 1
A __call() 0 18 4
A mask() 0 7 3
A getMessage() 0 2 1
A getMask() 0 2 1
A __get() 0 6 2
A getMimeType() 0 2 1
C fetch() 0 35 13
A getExtension() 0 2 1
B findType() 0 29 9
A getImgSrc() 0 5 3
A getAttributes() 0 2 1
A __construct() 0 14 3
A setMask() 0 6 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\IMAP;
14
15
use Illuminate\Support\Facades\File;
16
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
17
use Webklex\IMAP\Exceptions\MaskNotFoundException;
18
use Webklex\IMAP\Exceptions\MethodNotFoundException;
19
use Webklex\IMAP\Support\Masks\AttachmentMask;
20
21
/**
22
 * Class Attachment
23
 *
24
 * @package Webklex\IMAP
25
 * 
26
 * @property integer part_number
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  getName()
46
 * @method string  getDisposition()
47
 * @method string  setDisposition(string $disposition)
48
 * @method string  setImgSrc(string $img_src)
49
 */
50
class Attachment {
51
52
    /** @var Message $oMessage */
53
    protected $oMessage;
54
55
    /** @var array $config */
56
    protected $config = [];
57
58
    /** @var object $structure */
59
    protected $structure;
60
    
61
    /** @var array $attributes */
62
    protected $attributes = [
63
        'part_number' => 1,
64
        'content' => null,
65
        'type' => null,
66
        'content_type' => null,
67
        'id' => null,
68
        'name' => null,
69
        'disposition' => null,
70
        'img_src' => null,
71
    ];
72
73
    /**
74
     * Default mask
75
     * @var string $mask
76
     */
77
    protected $mask = AttachmentMask::class;
78
79
    /**
80
     * Attachment constructor.
81
     *
82
     * @param Message   $oMessage
83
     * @param object    $structure
84
     * @param integer   $part_number
85
     *
86
     * @throws Exceptions\ConnectionFailedException
87
     */
88
    public function __construct(Message $oMessage, $structure, $part_number = 1) {
89
        $this->config = config('imap.options');
90
91
        $this->oMessage = $oMessage;
92
        $this->structure = $structure;
93
        $this->part_number = ($part_number) ? $part_number : $this->part_number;
94
95
        $default_mask = $this->oMessage->getClient()->getDefaultAttachmentMask();
96
        if($default_mask != null) {
97
            $this->mask = $default_mask;
98
        }
99
100
        $this->findType();
101
        $this->fetch();
102
    }
103
104
    /**
105
     * Call dynamic attribute setter and getter methods
106
     * @param string $method
107
     * @param array $arguments
108
     *
109
     * @return mixed
110
     * @throws MethodNotFoundException
111
     */
112
    public function __call($method, $arguments) {
113
        if(strtolower(substr($method, 0, 3)) === 'get') {
114
            $name = snake_case(substr($method, 3));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

114
            $name = /** @scrutinizer ignore-deprecated */ snake_case(substr($method, 3));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
115
116
            if(isset($this->attributes[$name])) {
117
                return $this->attributes[$name];
118
            }
119
120
            return null;
121
        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
122
            $name = snake_case(substr($method, 3));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

122
            $name = /** @scrutinizer ignore-deprecated */ snake_case(substr($method, 3));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
123
124
            $this->attributes[$name] = array_pop($arguments);
125
126
            return $this->attributes[$name];
127
        }
128
129
        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
130
    }
131
132
    /**
133
     * @param $name
134
     * @param $value
135
     *
136
     * @return mixed
137
     */
138
    public function __set($name, $value) {
139
        $this->attributes[$name] = $value;
140
141
        return $this->attributes[$name];
142
    }
143
144
    /**
145
     * @param $name
146
     *
147
     * @return mixed|null
148
     */
149
    public function __get($name) {
150
        if(isset($this->attributes[$name])) {
151
            return $this->attributes[$name];
152
        }
153
154
        return null;
155
    }
156
157
    /**
158
     * Determine the structure type
159
     */
160
    protected function findType() {
161
        switch ($this->structure->type) {
162
            case IMAP::ATTACHMENT_TYPE_MESSAGE:
163
                $this->type = 'message';
164
                break;
165
            case IMAP::ATTACHMENT_TYPE_APPLICATION:
166
                $this->type = 'application';
167
                break;
168
            case IMAP::ATTACHMENT_TYPE_AUDIO:
169
                $this->type = 'audio';
170
                break;
171
            case IMAP::ATTACHMENT_TYPE_IMAGE:
172
                $this->type = 'image';
173
                break;
174
            case IMAP::ATTACHMENT_TYPE_VIDEO:
175
                $this->type = 'video';
176
                break;
177
            case IMAP::ATTACHMENT_TYPE_MODEL:
178
                $this->type = 'model';
179
                break;
180
            case IMAP::ATTACHMENT_TYPE_TEXT:
181
                $this->type = 'text';
182
                break;
183
            case IMAP::ATTACHMENT_TYPE_MULTIPART:
184
                $this->type = 'multipart';
185
                break;
186
            default:
187
                $this->type = 'other';
188
                break;
189
        }
190
    }
191
192
    /**
193
     * Fetch the given attachment
194
     *
195
     * @throws Exceptions\ConnectionFailedException
196
     */
197
    protected function fetch() {
198
199
        $content = imap_fetchbody($this->oMessage->getClient()->getConnection(), $this->oMessage->getUid(), $this->part_number, $this->oMessage->getFetchOptions() | FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->oMessage->getClient()->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchbody() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

199
        $content = imap_fetchbody(/** @scrutinizer ignore-type */ $this->oMessage->getClient()->getConnection(), $this->oMessage->getUid(), $this->part_number, $this->oMessage->getFetchOptions() | FT_UID);
Loading history...
200
201
        $this->content_type = $this->type.'/'.strtolower($this->structure->subtype);
202
        $this->content = $this->oMessage->decodeString($content, $this->structure->encoding);
203
204
        if (property_exists($this->structure, 'id')) {
205
            $this->id = str_replace(['<', '>'], '', $this->structure->id);
206
        }
207
208
        if (property_exists($this->structure, 'dparameters')) {
209
            foreach ($this->structure->dparameters as $parameter) {
210
                if (strtolower($parameter->attribute) == "filename") {
211
                    $this->setName($parameter->value);
212
                    $this->disposition = property_exists($this->structure, 'disposition') ? $this->structure->disposition : null;
213
                    break;
214
                }
215
            }
216
        }
217
218
        if (IMAP::ATTACHMENT_TYPE_MESSAGE == $this->structure->type) {
219
            if ($this->structure->ifdescription) {
220
                $this->setName($this->structure->description);
221
            } else {
222
                $this->setName($this->structure->subtype);
223
            }
224
        }
225
226
        if (!$this->name && property_exists($this->structure, 'parameters')) {
227
            foreach ($this->structure->parameters as $parameter) {
228
                if (strtolower($parameter->attribute) == "name") {
229
                    $this->setName($parameter->value);
230
                    $this->disposition = property_exists($this->structure, 'disposition') ? $this->structure->disposition : null;
231
                    break;
232
                }
233
            }
234
        }
235
    }
236
237
    /**
238
     * Save the attachment content to your filesystem
239
     *
240
     * @param string|null $path
241
     * @param string|null $filename
242
     *
243
     * @return boolean
244
     */
245
    public function save($path = null, $filename = null) {
246
        $path = $path ?: storage_path();
247
        $filename = $filename ?: $this->getName();
248
249
        $path = substr($path, -1) == DIRECTORY_SEPARATOR ? $path : $path.DIRECTORY_SEPARATOR;
250
251
        return File::put($path.$filename, $this->getContent()) !== false;
252
    }
253
254
    /**
255
     * @param $name
256
     */
257
    public function setName($name) {
258
        if($this->config['decoder']['message']['subject'] === 'utf-8') {
259
            $this->name = imap_utf8($name);
260
        }else{
261
            $this->name = mb_decode_mimeheader($name);
262
        }
263
    }
264
265
    /**
266
     * @return null|string
267
     *
268
     * @deprecated 1.4.0:2.0.0 No longer needed. Use AttachmentMask::getImageSrc() instead
269
     */
270
    public function getImgSrc() {
271
        if ($this->type == 'image' && $this->img_src == null) {
272
            $this->img_src = 'data:'.$this->content_type.';base64,'.base64_encode($this->content);
273
        }
274
        return $this->img_src;
275
    }
276
277
    /**
278
     * @return string|null
279
     */
280
    public function getMimeType(){
281
        return (new \finfo())->buffer($this->getContent(), FILEINFO_MIME_TYPE);
282
    }
283
284
    /**
285
     * @return string|null
286
     */
287
    public function getExtension(){
288
        return ExtensionGuesser::getInstance()->guess($this->getMimeType());
289
    }
290
291
    /**
292
     * @return array
293
     */
294
    public function getAttributes(){
295
        return $this->attributes;
296
    }
297
298
    /**
299
     * @return Message
300
     */
301
    public function getMessage(){
302
        return $this->oMessage;
303
    }
304
305
    /**
306
     * @param $mask
307
     * @return $this
308
     */
309
    public function setMask($mask){
310
        if(class_exists($mask)){
311
            $this->mask = $mask;
312
        }
313
314
        return $this;
315
    }
316
317
    /**
318
     * @return string
319
     */
320
    public function getMask(){
321
        return $this->mask;
322
    }
323
324
    /**
325
     * Get a masked instance by providing a mask name
326
     * @param string|null $mask
327
     *
328
     * @return mixed
329
     * @throws MaskNotFoundException
330
     */
331
    public function mask($mask = null){
332
        $mask = $mask !== null ? $mask : $this->mask;
333
        if(class_exists($mask)){
334
            return new $mask($this);
335
        }
336
337
        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
338
    }
339
}