Passed
Push — master ( 25958f...51d800 )
by Aleksei
14:54
created

FileBucket::__construct()   B

Complexity

Conditions 7
Paths 14

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7

Importance

Changes 1
Bugs 1 Features 1
Metric Value
cc 7
eloc 19
nc 14
nop 3
dl 0
loc 26
ccs 17
cts 17
cp 1
crap 7
rs 8.8333
c 1
b 1
f 1
1
<?php
2
3
namespace roxblnfk\SmartStream\Data;
4
5
use InvalidArgumentException;
6
use SplFileInfo;
7
use Yiisoft\Http\Header;
8
9
class FileBucket extends DataBucket
10
{
11
    public const TYPE_OCTET_STREAM = 'application/octet-stream';
12
13
    protected const DISPOSITION_INLINE = 'inline';
14
    protected const DISPOSITION_ATTACHMENT = 'attachment';
15
16
    protected const IS_CONVERTABLE = false;
17
    protected ?string $contentType = null;
18
    protected ?string $contentDisposition = null;
19
    protected ?string $fileName = null;
20
21
    /**
22
     * FileBucket constructor.
23
     * @param string|resource|SplFileInfo $data
24
     * @param null|string $contentType
25
     * @param null|string $fileName
26
     * @throws \Exception
27
     */
28 35
    public function __construct($data, string $contentType = null, string $fileName = null)
29
    {
30
        switch (true) {
31 35
            case $data instanceof SplFileInfo:
32 3
                if (!$data->isFile()) {
33 3
                    throw new InvalidArgumentException('File does not exist or is not a file.');
34 3
                }
35 3
                $fileName = $fileName ?? $data->getFilename();
36 32
                $contentType = $contentType ?? $this->fileContentType($data->getPathname());
37 2
                parent::__construct($data);
38 31
                break;
39 31
            case is_string($data):
40
            case is_resource($data):
41 1
                parent::__construct($data);
42 1
                break;
43
            default:
44
                throw new InvalidArgumentException(
45
                    'The $data parameter must be a resource, a string or an instance of SplFileInfo.'
46 34
                );
47 3
        }
48
49 34
        if ($contentType !== null) {
50 4
            $this->setContentType($contentType);
51
        }
52 34
        if ($fileName !== null) {
53
            $this->setAttachment($fileName);
54 2
        }
55
    }
56 2
57
    public static function createFromPath(string $filePath, string $contentType = null, string $fileName = null): self
58
    {
59 11
        return new static(new SplFileInfo($filePath), $contentType, $fileName);
60
    }
61 11
62
    public function getContentType(): ?string
63 10
    {
64
        return $this->contentType;
65 10
    }
66
    public function getFileName(): ?string
67 3
    {
68
        return $this->fileName;
69 3
    }
70
    public function hasDisposition(): bool
71 6
    {
72
        return $this->contentDisposition !== null;
73 6
    }
74
    public function isAttachment(): bool
75 4
    {
76
        return $this->contentDisposition === self::DISPOSITION_ATTACHMENT;
77 4
    }
78
    public function isInline(): bool
79
    {
80 14
        return $this->contentDisposition === self::DISPOSITION_INLINE;
81
    }
82 14
83 14
    public function withAttachment(string $filename = null): self
84 14
    {
85
        $clone = clone $this;
86 3
        $clone->setAttachment($filename);
87
        return $clone;
88 3
    }
89 3
    public function withContentType(?string $contentType): self
90 3
    {
91
        $clone = clone $this;
92 4
        $clone->setContentType($contentType);
93
        return $clone;
94 4
    }
95 4
    public function withInline(): self
96 4
    {
97
        $clone = clone $this;
98
        $clone->setInline();
99 6
        return $clone;
100
    }
101 6
    public function withoutDisposition(): self
102 6
    {
103 6
        $clone = clone $this;
104 4
        $clone->contentDisposition = null;
105
        $clone->setDispositionHeader();
106 4
        return $clone;
107 4
    }
108 4
109 18
    protected function setContentType(?string $contentType): void
110
    {
111 18
        $this->contentType = $contentType;
112 18
        $this->setHeader(Header::CONTENT_TYPE, $contentType);
113 18
    }
114 18
    protected function setInline(): void
115 20
    {
116
        $this->contentDisposition = self::DISPOSITION_INLINE;
117 20
        $this->setDispositionHeader();
118 14
    }
119 14
    final protected function setAttachment(?string $fileName): void
120 14
    {
121 14
        $this->contentDisposition = self::DISPOSITION_ATTACHMENT;
122 14
        $this->fileName = $fileName;
123
        $this->setDispositionHeader();
124 20
    }
125 20
    final protected function setDispositionHeader(): void
126 20
    {
127 2
        $headerBody = ($this->contentDisposition === self::DISPOSITION_ATTACHMENT && $this->fileName !== null)
128
            ? sprintf(
129 2
                '%s; filename="%s"; filename*=UTF-8\'\'%s',
130 1
                $this->contentDisposition,
131
                preg_replace('/[\x00-\x1F\x7F\"]/', ' ', $this->fileName),
132 1
                rawurlencode($this->fileName)
133
            )
134 1
            : $this->contentDisposition;
135 1
        $this->setHeader(Header::CONTENT_DISPOSITION, $headerBody);
136 1
    }
137 1
    private function fileContentType(?string $filePath): ?string
138 1
    {
139
        if ($filePath === null || !is_file($filePath) || !is_readable($filePath)) {
140
            return null;
141
        }
142
        $result = null;
143
        try {
144
            if (function_exists('finfo_open')) {
145
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
146
                $result = finfo_file($finfo, $filePath);
147
                if (is_string($result)) {
148
                    return $result;
149
                }
150
            }
151
            $result = $result ?? (function_exists('mime_content_type') ? mime_content_type($filePath) : null);
152
            return is_string($result) ? $result : null;
153
        } catch (\Throwable $e) {
154
            return null;
155
        }
156
    }
157
}
158