Issues (6)

src/FromFilesSchemaProvider.php (2 issues)

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
The doc comment array<non-empty-string> at position 2 could not be parsed: Unknown type name 'non-empty-string' at position 2 in array<non-empty-string>.
Loading history...
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
Documentation Bug introduced by
The doc comment array<non-empty-string> at position 2 could not be parsed: Unknown type name 'non-empty-string' at position 2 in array<non-empty-string>.
Loading history...
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