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

68publishers / file-storage / 3801321664

pending completion
3801321664

push

github

tg666
Tests + PHPStan level 9 + Dependency update

20 of 20 new or added lines in 10 files covered. (100.0%)

585 of 603 relevant lines covered (97.01%)

0.97 hits per line

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

90.84
/src/Bridge/Nette/DI/FileStorageExtension.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace SixtyEightPublishers\FileStorage\Bridge\Nette\DI;
6

7
use ReflectionClass;
8
use Nette\Schema\Expect;
9
use Nette\Schema\Schema;
10
use ReflectionException;
11
use Nette\DI\CompilerExtension;
12
use League\Flysystem\Filesystem;
13
use League\Flysystem\Visibility;
14
use Composer\Autoload\ClassLoader;
15
use Nette\DI\Definitions\Statement;
16
use League\Flysystem\FilesystemOperator;
17
use Nette\DI\Definitions\ServiceDefinition;
18
use League\Flysystem\Config as FlysystemConfig;
19
use SixtyEightPublishers\FileStorage\FileStorage;
20
use League\Flysystem\Local\LocalFilesystemAdapter;
21
use SixtyEightPublishers\FileStorage\Config\Config;
22
use SixtyEightPublishers\FileStorage\Asset\AssetFactory;
23
use SixtyEightPublishers\FileStorage\Asset\AssetsCopier;
24
use SixtyEightPublishers\FileStorage\Asset\PathsProvider;
25
use SixtyEightPublishers\FileStorage\FileStorageProvider;
26
use SixtyEightPublishers\FileStorage\FileStorageInterface;
27
use SixtyEightPublishers\FileStorage\Cleaner\StorageCleaner;
28
use SixtyEightPublishers\FileStorage\Config\ConfigInterface;
29
use SixtyEightPublishers\FileStorage\Resource\ResourceFactory;
30
use SixtyEightPublishers\FileStorage\Persistence\FilePersister;
31
use SixtyEightPublishers\FileStorage\Exception\RuntimeException;
32
use SixtyEightPublishers\FileStorage\Asset\AssetFactoryInterface;
33
use SixtyEightPublishers\FileStorage\Asset\AssetsCopierInterface;
34
use SixtyEightPublishers\FileStorage\LinkGenerator\LinkGenerator;
35
use SixtyEightPublishers\FileStorage\Asset\PathsProviderInterface;
36
use SixtyEightPublishers\FileStorage\FileStorageProviderInterface;
37
use SixtyEightPublishers\FileStorage\Cleaner\DefaultFileKeepResolver;
38
use SixtyEightPublishers\FileStorage\Cleaner\StorageCleanerInterface;
39
use SixtyEightPublishers\FileStorage\Cleaner\FileKeepResolverInterface;
40
use SixtyEightPublishers\FileStorage\Resource\ResourceFactoryInterface;
41
use SixtyEightPublishers\FileStorage\Persistence\FilePersisterInterface;
42
use SixtyEightPublishers\FileStorage\Bridge\Nette\DI\Config\StorageConfig;
43
use SixtyEightPublishers\FileStorage\LinkGenerator\LinkGeneratorInterface;
44
use SixtyEightPublishers\FileStorage\Bridge\Nette\DI\Config\FilesystemConfig;
45
use SixtyEightPublishers\FileStorage\Bridge\Nette\DI\Config\FileStorageConfig;
46
use function count;
47
use function assert;
48
use function dirname;
49
use function sprintf;
50
use function array_filter;
51
use function class_exists;
52

53
final class FileStorageExtension extends CompilerExtension implements FileStorageDefinitionFactoryInterface
54
{
55
        private string $rootDir;
56

57
        public function __construct(?string $rootDir = null)
1✔
58
        {
59
                $this->rootDir = $rootDir ?? $this->guessRootDir();
1✔
60
        }
1✔
61

62
        public function getConfigSchema(): Schema
63
        {
64
                return Expect::structure([
1✔
65
                        'storages' => Expect::arrayOf(
1✔
66
                                Expect::structure([ # the first one storage is default
1✔
67
                                        'config' => Expect::array([]),
1✔
68
                                        'filesystem' => Expect::structure([
1✔
69
                                                'adapter' => Expect::anyOf(Expect::string(), Expect::type(Statement::class))->required()->before(static function ($factory) {
1✔
70
                                                        return $factory instanceof Statement ? $factory : new Statement($factory);
1✔
71
                                                }),
1✔
72
                                                'config' => Expect::array([
1✔
73
                                                        FlysystemConfig::OPTION_VISIBILITY => Visibility::PUBLIC,
1✔
74
                                                        FlysystemConfig::OPTION_DIRECTORY_VISIBILITY => Visibility::PUBLIC,
1✔
75
                                                ])->mergeDefaults(),
1✔
76
                                        ])->castTo(FilesystemConfig::class),
1✔
77
                                        'assets' => Expect::arrayOf('string', 'string'),
1✔
78
                                ])->castTo(StorageConfig::class)
1✔
79
                        ),
80
                ])->assert(static function (object $config) {
1✔
81
                        return isset($config->storages) && 0 < count($config->storages);
1✔
82
                }, 'At least one storage must be defined.')
1✔
83
                        ->castTo(FileStorageConfig::class);
1✔
84
        }
85

86
        public function loadConfiguration(): void
87
        {
88
                $builder = $this->getContainerBuilder();
1✔
89
                $config = $this->getConfig();
1✔
90
                assert($config instanceof FileStorageConfig);
91

92
                $factories = array_filter($this->compiler->getExtensions(FileStorageDefinitionFactoryInterface::class), function ($extension) {
1✔
93
                        return $extension !== $this;
1✔
94
                });
1✔
95

96
                $factories[] = $this;
1✔
97
                $storageDefinitions = [];
1✔
98
                $defaultStorageDefinition = null;
1✔
99

100
                foreach ($config->storages as $storageName => $storageConfig) {
1✔
101
                        foreach ($factories as $factory) {
1✔
102
                                assert($factory instanceof FileStorageDefinitionFactoryInterface);
103

104
                                if (!$factory->canCreateFileStorage($storageName, $storageConfig)) {
1✔
105
                                        continue;
1✔
106
                                }
107

108
                                $storageDefinition = $factory->createFileStorage($storageName, $storageConfig);
1✔
109

110
                                if (null === $defaultStorageDefinition) {
1✔
111
                                        $defaultStorageDefinition = $storageDefinition->setAutowired();
1✔
112
                                } else {
113
                                        $storageDefinitions[] = $storageDefinition->setAutowired(false);
1✔
114
                                }
115

116
                                continue 2;
1✔
117
                        }
118
                }
119

120
                $builder->addDefinition($this->prefix('file_storage_provider'))
1✔
121
                        ->setType(FileStorageProviderInterface::class)
1✔
122
                        ->setFactory(FileStorageProvider::class, [
1✔
123
                                $defaultStorageDefinition,
1✔
124
                                $storageDefinitions,
1✔
125
                        ]);
126

127
                $this->registerStorageCleaner();
1✔
128
                $this->registerAssets($config);
1✔
129
        }
1✔
130

131
        public function canCreateFileStorage(string $name, object $config): bool
1✔
132
        {
133
                return true;
1✔
134
        }
135

136
        public function createFileStorage(string $name, StorageConfig $config): ServiceDefinition
1✔
137
        {
138
                $builder = $this->getContainerBuilder();
1✔
139

140
                $builder->addDefinition($this->prefix('filesystem.' . $name))
1✔
141
                        ->setType(FilesystemOperator::class)
1✔
142
                        ->setFactory(Filesystem::class, [
1✔
143
                                'adapter' => $config->filesystem->adapter,
1✔
144
                                'config' => $config->filesystem->config,
1✔
145
                        ])
146
                        ->setAutowired(false);
1✔
147

148
                $builder->addDefinition($this->prefix('config.' . $name))
1✔
149
                        ->setType(ConfigInterface::class)
1✔
150
                        ->setFactory(Config::class, [$config->config])
1✔
151
                        ->setAutowired(false);
1✔
152

153
                $builder->addDefinition($this->prefix('resource_factory.' . $name))
1✔
154
                        ->setType(ResourceFactoryInterface::class)
1✔
155
                        ->setFactory(ResourceFactory::class, [$this->prefix('@filesystem.' . $name)])
1✔
156
                        ->setAutowired(false);
1✔
157

158
                $builder->addDefinition($this->prefix('link_generator.' . $name))
1✔
159
                        ->setType(LinkGeneratorInterface::class)
1✔
160
                        ->setFactory(LinkGenerator::class, [$this->prefix('@config.' . $name)])
1✔
161
                        ->setAutowired(false);
1✔
162

163
                $builder->addDefinition($this->prefix('file_persister.' . $name))
1✔
164
                        ->setType(FilePersisterInterface::class)
1✔
165
                        ->setFactory(FilePersister::class, [$this->prefix('@filesystem.' . $name)])
1✔
166
                        ->setAutowired(false);
1✔
167

168
                return $builder->addDefinition($this->prefix('file_storage.' . $name))
1✔
169
                        ->setType(FileStorageInterface::class)
1✔
170
                        ->setFactory(FileStorage::class, [
1✔
171
                                $name,
1✔
172
                                $this->prefix('@config.' . $name),
1✔
173
                                $this->prefix('@resource_factory.' . $name),
1✔
174
                                $this->prefix('@link_generator.' . $name),
1✔
175
                                $this->prefix('@file_persister.' . $name),
1✔
176
                        ])
177
                        ->setAutowired(false);
1✔
178
        }
179

180
        private function registerStorageCleaner(): void
181
        {
182
                $builder = $this->getContainerBuilder();
1✔
183

184
                $builder->addDefinition($this->prefix('file_keep_resolver'))
1✔
185
                        ->setType(FileKeepResolverInterface::class)
1✔
186
                        ->setFactory(DefaultFileKeepResolver::class);
1✔
187

188
                $builder->addDefinition($this->prefix('storage_cleaner'))
1✔
189
                        ->setType(StorageCleanerInterface::class)
1✔
190
                        ->setFactory(StorageCleaner::class);
1✔
191
        }
1✔
192

193
        private function registerAssets(FileStorageConfig $config): void
1✔
194
        {
195
                $builder = $this->getContainerBuilder();
1✔
196

197
                $builder->addDefinition($this->prefix('assets.local_filesystem'))
1✔
198
                        ->setType(FilesystemOperator::class)
1✔
199
                        ->setFactory(Filesystem::class, [
1✔
200
                                new Statement(LocalFilesystemAdapter::class, [$this->rootDir]),
1✔
201
                        ])
202
                        ->setAutowired(false);
1✔
203

204
                $builder->addDefinition($this->prefix('assets.asset_factory'))
1✔
205
                        ->setType(AssetFactoryInterface::class)
1✔
206
                        ->setFactory(AssetFactory::class)
1✔
207
                        ->setAutowired(false);
1✔
208

209
                $provider = $builder->addDefinition($this->prefix('assets.paths_provider'))
1✔
210
                        ->setType(PathsProviderInterface::class)
1✔
211
                        ->setFactory(PathsProvider::class)
1✔
212
                        ->setAutowired(false);
1✔
213

214
                $builder->addDefinition($this->prefix('assets.assets_copier'))
1✔
215
                        ->setType(AssetsCopierInterface::class)
1✔
216
                        ->setFactory(AssetsCopier::class, [
1✔
217
                                $this->prefix('@assets.local_filesystem'),
1✔
218
                                $this->prefix('@assets.paths_provider'),
1✔
219
                                $this->prefix('@assets.asset_factory'),
1✔
220
                        ]);
221

222
                foreach ($config->storages as $storageName => $storageConfig) {
1✔
223
                        if (!empty($storageConfig->assets)) {
1✔
224
                                $provider->addSetup('addPaths', [$storageName, $storageConfig->assets]);
1✔
225
                        }
226
                }
227

228
                foreach ($this->compiler->getExtensions(AssetsProviderInterface::class) as $assetsProviderExtension) {
1✔
229
                        assert($assetsProviderExtension instanceof AssetsProviderInterface);
230

231
                        foreach ($assetsProviderExtension->provideAssets() as $assets) {
1✔
232
                                if (!empty($assets->paths)) {
1✔
233
                                        $provider->addSetup('addPaths', [$assets->storageName, $assets->paths]);
1✔
234
                                }
235
                        }
236
                }
237
        }
1✔
238

239
        /**
240
         * @throws \SixtyEightPublishers\FileStorage\Exception\RuntimeException
241
         */
242
        private function guessRootDir(): string
243
        {
244
                if (!class_exists(ClassLoader::class)) {
×
245
                        throw new RuntimeException(sprintf(
×
246
                                'Project root directory can\'t be detected because the class %s can\'t be found. Please provide the root directory manually into the %s::__construct().',
×
247
                                ClassLoader::class,
×
248
                                self::class
×
249
                        ));
250
                }
251

252
                try {
253
                        $reflection = new ReflectionClass(ClassLoader::class);
×
254

255
                        return dirname((string) $reflection->getFileName(), 3);
×
256
                } catch (ReflectionException $e) {
×
257
                        throw new RuntimeException(sprintf(
×
258
                                'Project root directory can\'t be detected. Please provide the root directory manually  into the %s::__construct().',
×
259
                                self::class
×
260
                        ), 0, $e);
×
261
                }
262
        }
263
}
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

© 2026 Coveralls, Inc