1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Cycle\Schema\Provider; |
||
6 | |||
7 | use Cycle\Schema\Provider\Exception\ConfigurationException; |
||
8 | use Cycle\Schema\Provider\Exception\SchemaFileNotFoundException; |
||
9 | use Webmozart\Glob\Glob; |
||
10 | use Webmozart\Glob\Iterator\GlobIterator; |
||
11 | use Cycle\Schema\Provider\Support\SchemaMerger; |
||
12 | |||
13 | /** |
||
14 | * Be careful, using this class may be insecure. |
||
15 | */ |
||
16 | final class FromFilesSchemaProvider implements SchemaProviderInterface |
||
17 | { |
||
18 | /** |
||
19 | * @var array<non-empty-string> Schema files |
||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||
20 | */ |
||
21 | private array $files = []; |
||
22 | |||
23 | /** |
||
24 | * @var bool Throw exception if file not found |
||
25 | */ |
||
26 | private bool $strict = false; |
||
27 | |||
28 | /** |
||
29 | * @var \Closure(non-empty-string): non-empty-string |
||
30 | */ |
||
31 | private \Closure $pathResolver; |
||
32 | |||
33 | /** |
||
34 | * @param null|callable(non-empty-string): non-empty-string $pathResolver A function that resolves |
||
35 | * framework-specific file paths. |
||
36 | */ |
||
37 | 21 | public function __construct(?callable $pathResolver = null) |
|
38 | { |
||
39 | /** @psalm-suppress PropertyTypeCoercion */ |
||
40 | 21 | $this->pathResolver = $pathResolver === null |
|
41 | 21 | ? static fn (string $path): string => $path |
|
42 | 21 | : \Closure::fromCallable($pathResolver); |
|
43 | } |
||
44 | |||
45 | /** |
||
46 | * Create a configuration array for the {@see self::withConfig()} method. |
||
47 | * @param array<non-empty-string> $files |
||
0 ignored issues
–
show
|
|||
48 | */ |
||
49 | 1 | public static function config(array $files, bool $strict = false): array |
|
50 | { |
||
51 | 1 | return [ |
|
52 | 1 | 'files' => $files, |
|
53 | 1 | 'strict' => $strict, |
|
54 | 1 | ]; |
|
55 | } |
||
56 | |||
57 | 18 | public function withConfig(array $config): self |
|
58 | { |
||
59 | 18 | $files = $config['files'] ?? []; |
|
60 | 18 | if (!\is_array($files)) { |
|
61 | 1 | throw new ConfigurationException('The `files` parameter must be an array.'); |
|
62 | } |
||
63 | 17 | if (\count($files) === 0) { |
|
64 | 2 | throw new ConfigurationException('Schema file list is not set.'); |
|
65 | } |
||
66 | |||
67 | 15 | $strict = $config['strict'] ?? $this->strict; |
|
68 | 15 | if (!\is_bool($strict)) { |
|
69 | 1 | throw new ConfigurationException('The `strict` parameter must be a boolean.'); |
|
70 | } |
||
71 | |||
72 | 14 | $files = \array_map( |
|
73 | 14 | function (mixed $file) { |
|
74 | 14 | if (!\is_string($file) || $file === '') { |
|
75 | 6 | throw new ConfigurationException('The `files` parameter must contain non-empty string values.'); |
|
76 | } |
||
77 | 8 | return ($this->pathResolver)($file); |
|
78 | 14 | }, |
|
79 | 14 | $files |
|
80 | 14 | ); |
|
81 | |||
82 | 8 | $new = clone $this; |
|
83 | 8 | $new->files = $files; |
|
84 | 8 | $new->strict = $strict; |
|
85 | 8 | return $new; |
|
86 | } |
||
87 | |||
88 | 10 | public function read(?SchemaProviderInterface $nextProvider = null): ?array |
|
89 | { |
||
90 | 10 | $schema = (new SchemaMerger())->merge(...$this->readFiles()); |
|
91 | |||
92 | 8 | return $schema !== null || $nextProvider === null ? $schema : $nextProvider->read(); |
|
93 | } |
||
94 | |||
95 | 1 | public function clear(): bool |
|
96 | { |
||
97 | 1 | return false; |
|
98 | } |
||
99 | |||
100 | /** |
||
101 | * Read schema from each file |
||
102 | * |
||
103 | * @return \Generator<int, array|null> |
||
104 | */ |
||
105 | 10 | private function readFiles(): \Generator |
|
106 | { |
||
107 | 10 | foreach ($this->files as $path) { |
|
108 | 8 | $path = \str_replace('\\', '/', $path); |
|
109 | 8 | if (!Glob::isDynamic($path)) { |
|
110 | 7 | yield $this->loadFile($path); |
|
111 | 7 | continue; |
|
112 | } |
||
113 | 1 | foreach (new GlobIterator($path) as $file) { |
|
114 | 1 | yield $this->loadFile($file); |
|
115 | } |
||
116 | } |
||
117 | } |
||
118 | |||
119 | 8 | private function loadFile(string $path): ?array |
|
120 | { |
||
121 | 8 | $isFile = \is_file($path); |
|
122 | |||
123 | 8 | if (!$isFile && $this->strict) { |
|
124 | 1 | throw new SchemaFileNotFoundException($path); |
|
125 | } |
||
126 | |||
127 | 8 | return $isFile ? require $path : null; |
|
128 | } |
||
129 | } |
||
130 |