Completed
Pull Request — master (#50)
by Jonathan
04:58 queued 02:35
created

SymfonyFileLocator::getAllClassNames()   B

Complexity

Conditions 8
Paths 3

Size

Total Lines 43
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 8.1624

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 43
ccs 19
cts 22
cp 0.8636
rs 8.4444
c 0
b 0
f 0
cc 8
nc 3
nop 1
crap 8.1624
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Persistence\Mapping\Driver;
6
7
use Doctrine\Persistence\Mapping\MappingException;
8
use InvalidArgumentException;
9
use RecursiveDirectoryIterator;
10
use RecursiveIteratorIterator;
11
use RuntimeException;
12
use const DIRECTORY_SEPARATOR;
13
use function array_keys;
14
use function array_merge;
15
use function is_dir;
16
use function is_file;
17
use function realpath;
18
use function sprintf;
19
use function str_replace;
20
use function strlen;
21
use function strpos;
22
use function strrpos;
23
use function strtr;
24
use function substr;
25
26
/**
27
 * The Symfony File Locator makes a simplifying assumptions compared
28
 * to the DefaultFileLocator. By assuming paths only contain entities of a certain
29
 * namespace the mapping files consists of the short classname only.
30
 */
31
class SymfonyFileLocator implements FileLocator
32
{
33
    /**
34
     * The paths where to look for mapping files.
35
     *
36
     * @var string[]
37
     */
38
    protected $paths = [];
39
40
    /**
41
     * A map of mapping directory path to namespace prefix used to expand class shortnames.
42
     *
43
     * @var string[]
44
     */
45
    protected $prefixes = [];
46
47
    /**
48
     * File extension that is searched for.
49
     *
50
     * @var string|null
51
     */
52
    protected $fileExtension;
53
54
    /**
55
     * Represents PHP namespace delimiters when looking for files
56
     *
57
     * @var string
58
     */
59
    private $nsSeparator;
60
61
    /**
62
     * @param string[] $prefixes
63
     * @param string   $nsSeparator String which would be used when converting FQCN to filename and vice versa. Should not be empty
64
     */
65 14
    public function __construct(
66
        array $prefixes,
67
        ?string $fileExtension = null,
68
        ?string $nsSeparator = '.'
69
    ) {
70 14
        $this->addNamespacePrefixes($prefixes);
71 14
        $this->fileExtension = $fileExtension;
72
73 14
        if (empty($nsSeparator)) {
74 1
            throw new InvalidArgumentException('Namespace separator should not be empty');
75
        }
76
77 13
        $this->nsSeparator = (string) $nsSeparator;
78 13
    }
79
80
    /**
81
     * Adds Namespace Prefixes.
82
     *
83
     * @param string[] $prefixes
84
     */
85 14
    public function addNamespacePrefixes(array $prefixes) : void
86
    {
87 14
        $this->prefixes = array_merge($this->prefixes, $prefixes);
88 14
        $this->paths    = array_merge($this->paths, array_keys($prefixes));
89 14
    }
90
91
    /**
92
     * Gets Namespace Prefixes.
93
     *
94
     * @return string[]
95
     */
96 1
    public function getNamespacePrefixes() : array
97
    {
98 1
        return $this->prefixes;
99
    }
100
101
    /**
102
     * {@inheritDoc}
103
     */
104 1
    public function getPaths() : array
105
    {
106 1
        return $this->paths;
107
    }
108
109
    /**
110
     * {@inheritDoc}
111
     */
112 1
    public function getFileExtension() : ?string
113
    {
114 1
        return $this->fileExtension;
115
    }
116
117
    /**
118
     * Sets the file extension used to look for mapping files under.
119
     *
120
     * @param string $fileExtension The file extension to set.
121
     */
122 1
    public function setFileExtension(string $fileExtension) : void
123
    {
124 1
        $this->fileExtension = $fileExtension;
125 1
    }
126
127
    /**
128
     * {@inheritDoc}
129
     */
130 1
    public function fileExists(string $className) : bool
131
    {
132 1
        $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension;
133
134 1
        foreach ($this->paths as $path) {
135 1
            if (! isset($this->prefixes[$path])) {
136
                // global namespace class
137
                if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) {
138
                    return true;
139
                }
140
141
                continue;
142
            }
143
144 1
            $prefix = $this->prefixes[$path];
145
146 1
            if (strpos($className, $prefix . '\\') !== 0) {
147
                continue;
148
            }
149
150 1
            $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension;
151
152 1
            if (is_file($filename)) {
153 1
                return true;
154
            }
155
        }
156
157 1
        return false;
158
    }
159
160
    /**
161
     * {@inheritDoc}
162
     */
163 3
    public function getAllClassNames(?string $globalBasename = null) : array
164
    {
165 3
        $classes = [];
166
167 3
        if ($this->paths) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->paths of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
168 3
            foreach ((array) $this->paths as $path) {
169 3
                if (! is_dir($path)) {
170
                    throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
171
                }
172
173 3
                $iterator = new RecursiveIteratorIterator(
174 3
                    new RecursiveDirectoryIterator($path),
175 3
                    RecursiveIteratorIterator::LEAVES_ONLY
176
                );
177
178 3
                foreach ($iterator as $file) {
179 3
                    $fileName = $file->getBasename($this->fileExtension);
180
181 3
                    if ($fileName === $file->getBasename() || $fileName === $globalBasename) {
182 3
                        continue;
183
                    }
184
185
                    // NOTE: All files found here means classes are not transient!
186 3
                    if (isset($this->prefixes[$path])) {
187
                        // Calculate namespace suffix for given prefix as a relative path from basepath to file path
188 3
                        $nsSuffix = strtr(
189 3
                            substr($this->realpath($file->getPath()), strlen($this->realpath($path))),
190 3
                            $this->nsSeparator,
191 3
                            '\\'
192
                        );
193
194 3
                        $classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' . str_replace($this->nsSeparator, '\\', $fileName);
195
                    } else {
196
                        /** @var string $className */
197
                        $className = str_replace($this->nsSeparator, '\\', $fileName);
198
199
                        $classes[] = $className;
200
                    }
201
                }
202
            }
203
        }
204
205 3
        return $classes;
206
    }
207
208
    /**
209
     * {@inheritDoc}
210
     */
211 6
    public function findMappingFile(string $className) : string
212
    {
213 6
        $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension;
214 6
        foreach ($this->paths as $path) {
215 6
            if (! isset($this->prefixes[$path])) {
216
                if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) {
217
                    return $path . DIRECTORY_SEPARATOR . $defaultFileName;
218
                }
219
220
                continue;
221
            }
222
223 6
            $prefix = $this->prefixes[$path];
224
225 6
            if (strpos($className, $prefix . '\\') !== 0) {
226
                continue;
227
            }
228
229 6
            $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension;
230 6
            if (is_file($filename)) {
231 5
                return $filename;
232
            }
233
        }
234
235 1
        throw MappingException::mappingFileNotFound(
236 1
            $className,
237 1
            substr($className, strrpos($className, '\\') + 1) . $this->fileExtension
238
        );
239
    }
240
241 3
    private function realpath(string $path) : string
242
    {
243 3
        $realpath = realpath($path);
244
245 3
        if ($realpath === false) {
246
            throw new RuntimeException(sprintf('Could not get realpath for %s', $path));
247
        }
248
249 3
        return $realpath;
250
    }
251
}
252