Passed
Push — master ( 88ceb8...2cbf25 )
by Aleksei
01:45
created

FileBucket::fileContentType()   B

Complexity

Conditions 9
Paths 22

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 13.608

Importance

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