Passed
Push — master ( 72c793...7f699f )
by Kirill
03:22
created

ViewLoader::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 8
rs 10
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Views;
13
14
use Spiral\Files\Files;
15
use Spiral\Files\FilesInterface;
16
use Spiral\Views\Exception\LoaderException;
17
use Spiral\Views\Loader\PathParser;
18
use Spiral\Views\Loader\ViewPath;
19
20
/**
21
 * Loads and locates view files associated with specific extensions.
22
 */
23
final class ViewLoader implements LoaderInterface
24
{
25
    /** @var FilesInterface */
26
    private $files;
27
28
    /** @var PathParser|null */
29
    private $parser = null;
30
31
    /** @var array */
32
    private $namespaces = [];
33
34
    /** @var string */
35
    private $defaultNamespace = self::DEFAULT_NAMESPACE;
36
37
    /**
38
     * @param array          $namespaces
39
     * @param FilesInterface $files
40
     * @param string         $defaultNamespace
41
     */
42
    public function __construct(
43
        array $namespaces,
44
        FilesInterface $files = null,
45
        string $defaultNamespace = self::DEFAULT_NAMESPACE
46
    ) {
47
        $this->namespaces = $namespaces;
48
        $this->files = $files ?? new Files();
49
        $this->defaultNamespace = $defaultNamespace;
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function withExtension(string $extension): LoaderInterface
56
    {
57
        $loader = clone $this;
58
        $loader->parser = new PathParser($this->defaultNamespace, $extension);
59
60
        return $loader;
61
    }
62
63
    /**
64
     * @return string|null
65
     */
66
    public function getExtension(): ?string
67
    {
68
        if ($this->parser !== null) {
69
            return $this->parser->getExtension();
70
        }
71
72
        return null;
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     *
78
     * @param string   $filename
79
     * @param ViewPath $parsed
80
     */
81
    public function exists(string $path, string &$filename = null, ViewPath &$parsed = null): bool
82
    {
83
        if (empty($this->parser)) {
84
            throw new LoaderException('Unable to locate view source, no extension has been associated.');
85
        }
86
87
        $parsed = $this->parser->parse($path);
88
        if (empty($parsed)) {
89
            return false;
90
        }
91
92
        if (!isset($this->namespaces[$parsed->getNamespace()])) {
93
            return false;
94
        }
95
96
        foreach ((array)$this->namespaces[$parsed->getNamespace()] as $directory) {
97
            $directory = $this->files->normalizePath($directory, true);
98
            if ($this->files->exists(sprintf('%s%s', $directory, $parsed->getBasename()))) {
99
                $filename = sprintf('%s%s', $directory, $parsed->getBasename());
100
101
                return true;
102
            }
103
        }
104
105
        return false;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function load(string $path): ViewSource
112
    {
113
        if (!$this->exists($path, $filename, $parsed)) {
114
            throw new LoaderException("Unable to load view `$path`, file does not exists.");
115
        }
116
117
        /** @var ViewPath $parsed */
118
        return new ViewSource($filename, $parsed->getNamespace(), $parsed->getName());
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function list(string $namespace = null): array
125
    {
126
        if (empty($this->parser)) {
127
            throw new LoaderException('Unable to list view sources, no extension has been associated.');
128
        }
129
130
        $result = [];
131
        foreach ($this->namespaces as $ns => $directories) {
132
            if (!empty($namespace) && $namespace != $ns) {
133
                continue;
134
            }
135
136
            foreach ((array)$directories as $directory) {
137
                $files = $this->files->getFiles($directory);
138
139
                foreach ($files as $filename) {
140
                    if (!$this->parser->match($filename)) {
141
                        // does not belong to this loader
142
                        continue;
143
                    }
144
145
                    $name = $this->parser->fetchName($this->files->relativePath($filename, $directory));
146
                    $result[] = sprintf('%s%s%s', $ns, self::NS_SEPARATOR, $name);
147
                }
148
            }
149
        }
150
151
        return $result;
152
    }
153
}
154