• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

68publishers / file-storage / 8243028639

12 Mar 2024 03:11AM UTC coverage: 92.53% (-4.5%) from 97.015%
8243028639

push

github

tg666
Resources changes - StreamResource

- renamed method `ResourceFactoryInterface::createResourceFromLocalFile()` to `ResourceFactoryInterface::createResourceFromFile()`
- removed resource class `SimpleResource`
- added resource class `StreamResource`
- added methods `ResourceInterface::getMimeType()` and `ResourceInterface::getFilesize()`
- ResourceFactory can now create a resource from an url
- fixed tests

53 of 83 new or added lines in 5 files covered. (63.86%)

2 existing lines in 2 files now uncovered.

607 of 656 relevant lines covered (92.53%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.34
/src/Resource/ResourceFactory.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace SixtyEightPublishers\FileStorage\Resource;
6

7
use League\Flysystem\FilesystemException as LeagueFilesystemException;
8
use League\Flysystem\FilesystemReader;
9
use League\Flysystem\UnableToRetrieveMetadata;
10
use SixtyEightPublishers\FileStorage\Exception\FileNotFoundException;
11
use SixtyEightPublishers\FileStorage\Exception\FilesystemException;
12
use SixtyEightPublishers\FileStorage\PathInfoInterface;
13
use function error_clear_last;
14
use function error_get_last;
15
use function file_exists;
16
use function filter_var;
17
use function fopen;
18
use function is_file;
19
use function sprintf;
20
use function str_starts_with;
21
use function stream_context_create;
22
use function stream_get_meta_data;
23
use function strlen;
24
use function strtolower;
25
use function trim;
26

27
final class ResourceFactory implements ResourceFactoryInterface
28
{
29
    public function __construct(
1✔
30
        private readonly FilesystemReader $filesystemReader,
31
    ) {}
1✔
32

33
    /**
34
     * @throws FileNotFoundException
35
     * @throws LeagueFilesystemException
36
     * @throws FilesystemException
37
     */
38
    public function createResource(PathInfoInterface $pathInfo): ResourceInterface
1✔
39
    {
40
        $path = $pathInfo->getPath();
1✔
41

42
        if (false === $this->filesystemReader->fileExists($path)) {
1✔
43
            throw new FileNotFoundException($path);
1✔
44
        }
45

46
        try {
47
            $source = $this->filesystemReader->readStream($path);
1✔
48
        } catch (LeagueFilesystemException $e) {
1✔
49
            throw new FilesystemException(
1✔
50
                message: sprintf(
1✔
51
                    'Can not read stream from file "%s".',
1✔
52
                    $path,
1✔
53
                ),
54
                previous: $e,
55
            );
56
        }
57

58
        return new StreamResource(
1✔
59
            pathInfo: $pathInfo,
1✔
60
            source: $source,
61
            mimeType: function () use ($path): ?string {
1✔
62
                try {
63
                    return $this->filesystemReader->mimeType($path);
64
                } catch (LeagueFilesystemException|UnableToRetrieveMetadata $e) {
65
                    return null;
66
                }
67
            },
1✔
68
            filesize: function () use ($path): ?int {
1✔
69
                try {
70
                    return $this->filesystemReader->fileSize($path);
71
                } catch (LeagueFilesystemException|UnableToRetrieveMetadata $e) {
72
                    return null;
73
                }
74
            },
1✔
75
        );
76
    }
77

78
    public function createResourceFromFile(PathInfoInterface $pathInfo, string $filename): ResourceInterface
1✔
79
    {
80
        return match (true) {
81
            (bool) filter_var($filename, FILTER_VALIDATE_URL) => $this->getResourceFromUrl(
1✔
NEW
82
                pathInfo: $pathInfo,
×
83
                url: $filename,
84
            ),
85
            file_exists($filename) && is_file($filename) => $this->getResourceFromLocalFile(
1✔
86
                pathInfo: $pathInfo,
1✔
87
                filename: $filename,
88
            ),
89
            default => throw new FileNotFoundException($filename),
1✔
90
        };
91
    }
92

93
    /**
94
     * @throws FilesystemException
95
     */
96
    private function getResourceFromUrl(PathInfoInterface $pathInfo, string $url): ResourceInterface
97
    {
UNCOV
98
        error_clear_last();
×
99

NEW
100
        $context = stream_context_create(
×
101
            options: [
102
                'http' => [
NEW
103
                    'method' => 'GET',
×
104
                    'protocol_version' => 1.1,
105
                    'header' => "Accept-language: en\r\n" . "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36\r\n",
106
                ],
107
            ],
108
        );
109

NEW
110
        $source = @fopen(
×
NEW
111
            filename: $url,
×
NEW
112
            mode: 'rb',
×
NEW
113
            context: $context,
×
114
        );
115

NEW
116
        if (false === $source) {
×
NEW
117
            throw new FilesystemException(
×
NEW
118
                message: sprintf(
×
NEW
119
                    'Can not read stream from url "%s". %s',
×
NEW
120
                    $url,
×
NEW
121
                    error_get_last()['message'] ?? '',
×
122
                ),
123
            );
124
        }
125

NEW
126
        $headers = stream_get_meta_data($source)['wrapper_data'] ?? [];
×
127

NEW
128
        return new StreamResource(
×
NEW
129
            pathInfo: $pathInfo,
×
130
            source: $source,
NEW
131
            mimeType: function () use ($headers): ?string {
×
132
                return $this->getHeaderValue(
133
                    headers: $headers,
134
                    name: 'Content-Type',
135
                );
NEW
136
            },
×
NEW
137
            filesize: function () use ($headers): ?int {
×
138
                $filesize = $this->getHeaderValue(
139
                    headers: $headers,
140
                    name: 'Content-Length',
141
                );
142

143
                return null !== $filesize ? (int) $filesize : null;
NEW
144
            },
×
145
        );
146
    }
147

148
    /**
149
     * @throws FilesystemException
150
     */
151
    private function getResourceFromLocalFile(PathInfoInterface $pathInfo, string $filename): ResourceInterface
1✔
152
    {
153
        error_clear_last();
1✔
154

155
        $source = @fopen(
1✔
156
            filename: $filename,
1✔
157
            mode: 'rb',
1✔
158
        );
159

160
        if (false === $source) {
1✔
161
            throw new FilesystemException(
1✔
162
                message: sprintf(
1✔
163
                    'Can not read stream from file "%s". %s',
1✔
164
                    $filename,
1✔
165
                    error_get_last()['message'] ?? '',
1✔
166
                ),
167
            );
168
        }
169

170
        return new StreamResource(
1✔
171
            pathInfo: $pathInfo,
1✔
172
            source: $source,
173
            mimeType: function (StreamResource $resource): ?string {
1✔
174
                $mimeType = @mime_content_type($resource->getSource());
175

176
                return false === $mimeType ? null : $mimeType;
177
            },
1✔
178
            filesize: function () use ($filename): ?int {
1✔
179
                $filesize = @filesize(
180
                    filename: $filename,
181
                );
182

183
                return false === $filesize ? null : $filesize;
184
            },
1✔
185
        );
186
    }
187

188
    /**
189
     * @param array<int, string> $headers
190
     */
191
    private function getHeaderValue(array $headers, string $name): ?string
192
    {
NEW
193
        $name = strtolower($name);
×
194

NEW
195
        foreach ($headers as $header) {
×
NEW
196
            $header = trim(strtolower($header));
×
197

NEW
198
            if (!str_starts_with($header, $name . ':')) {
×
NEW
199
                continue;
×
200
            }
201

NEW
202
            $value = substr(
×
NEW
203
                string: $header,
×
NEW
204
                offset: strlen($name) + 1,
×
205
            );
206

NEW
207
            return trim($value);
×
208
        }
209

NEW
210
        return null;
×
211
    }
212
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc