Passed
Pull Request — 1.x (#1)
by Maxim
13:15
created

PhpFileSchemaProvider::config()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 3
c 1
b 0
f 1
nc 1
nop 2
dl 0
loc 7
ccs 0
cts 4
cp 0
crap 2
rs 10
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\SchemaProviderException;
9
use Cycle\Schema\Renderer\PhpSchemaRenderer;
10
use Spiral\Files\Files;
11
use Spiral\Files\FilesInterface;
12
13
final class PhpFileSchemaProvider implements SchemaProviderInterface
14
{
15
    public const MODE_READ_AND_WRITE = 0;
16
    public const MODE_WRITE_ONLY = 1;
17
18
    private string $file = '';
19
    private int $mode = self::MODE_READ_AND_WRITE;
20
21
    /**
22
     * @var \Closure(non-empty-string): non-empty-string
23
     */
24
    private \Closure $pathResolver;
25
    private FilesInterface $files;
26
27
    /**
28
     * @param null|callable(non-empty-string): non-empty-string $pathResolver A function that resolves
29
     *        framework-specific file paths.
30
     */
31 12
    public function __construct(?callable $pathResolver = null, ?FilesInterface $files = null)
32
    {
33 12
        $this->files = $files ?? new Files();
34 12
        $this->pathResolver = $pathResolver === null
35 12
            ? static fn (string $path): string => $path
36 12
            : \Closure::fromCallable($pathResolver);
37
    }
38
39
    /**
40
     * Create a configuration array for the {@see self::withConfig()} method.
41
     */
42
    public static function config(
43
        string $file,
44
        int $mode = self::MODE_READ_AND_WRITE,
45
    ): array {
46
        return [
47
            'file' => $file,
48
            'mode' => $mode,
49
        ];
50
    }
51
52
    /**
53
     * @param array{
54
     *     file: non-empty-string,
55
     *     mode?: self::MODE_READ_AND_WRITE|self::MODE_WRITE_ONLY,
56
     * } $config
57
     */
58 10
    public function withConfig(array $config): self
59
    {
60 10
        $new = clone $this;
61
62
        // required option
63 10
        if ($this->file === '' && !array_key_exists('file', $config)) {
64 1
            throw new ConfigurationException('The `file` parameter is required.');
65
        }
66 9
        $new->file = ($this->pathResolver)($config['file']);
67
68 9
        $new->mode = $config['mode'] ?? $this->mode;
69
70 9
        return $new;
71
    }
72
73 8
    public function read(?SchemaProviderInterface $nextProvider = null): ?array
74
    {
75 8
        if (!$this->isReadable()) {
76 4
            if ($nextProvider === null) {
77 2
                throw new SchemaProviderException(__CLASS__ . ' can not read schema.');
78
            }
79 2
            $schema = null;
80
        } else {
81
            /** @psalm-suppress UnresolvableInclude */
82 4
            $schema = !$this->files->isFile($this->file) ? null : (include $this->file);
83
        }
84
85 6
        if ($schema !== null || $nextProvider === null) {
86 2
            return $schema;
87
        }
88
89 4
        $schema = $nextProvider->read();
90 4
        if ($schema !== null) {
91 3
            $this->write($schema);
92
        }
93 3
        return $schema;
94
    }
95
96 3
    public function clear(): bool
97
    {
98
        try {
99 3
            return $this->removeFile();
100 1
        } catch (\Throwable $e) {
101 1
            return false;
102
        }
103
    }
104
105 3
    private function write(array $schema): bool
106
    {
107 3
        if (\basename($this->file) === '') {
108 1
            throw new SchemaProviderException('The `file` parameter must not be empty.');
109
        }
110
111 2
        $content = (new PhpSchemaRenderer())->render($schema);
112 2
        $this->files->write($this->file, $content, 0777, true);
113
114 2
        return true;
115
    }
116
117 3
    private function removeFile(): bool
118
    {
119 3
        if (!$this->files->exists($this->file)) {
120 1
            return true;
121
        }
122 2
        if (!$this->files->isFile($this->file)) {
123 1
            throw new SchemaProviderException(\sprintf('`%s` is not a file.', $this->file));
124
        }
125 1
        if (!\is_writable($this->file)) {
126
            throw new SchemaProviderException(\sprintf('File `%s` is not writeable.', $this->file));
127
        }
128
129 1
        return $this->files->delete($this->file);
130
    }
131
132 8
    private function isReadable(): bool
133
    {
134 8
        return $this->mode !== self::MODE_WRITE_ONLY;
135
    }
136
}
137