Completed
Pull Request — master (#50)
by Jonathan
04:15 queued 01:55
created

SymfonyFileLocator::realpath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 9
ccs 4
cts 5
cp 0.8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.032
1
<?php
2
3
namespace Doctrine\Persistence\Mapping\Driver;
4
5
use Doctrine\Persistence\Mapping\MappingException;
6
use InvalidArgumentException;
7
use RecursiveDirectoryIterator;
8
use RecursiveIteratorIterator;
9
use RuntimeException;
10
use const DIRECTORY_SEPARATOR;
11
use function array_keys;
12
use function array_merge;
13
use function is_dir;
14
use function is_file;
15
use function realpath;
16
use function sprintf;
17
use function str_replace;
18
use function strlen;
19
use function strpos;
20
use function strrpos;
21
use function strtr;
22
use function substr;
23
24
/**
25
 * The Symfony File Locator makes a simplifying assumptions compared
26
 * to the DefaultFileLocator. By assuming paths only contain entities of a certain
27
 * namespace the mapping files consists of the short classname only.
28
 */
29
class SymfonyFileLocator implements FileLocator
30
{
31
    /**
32
     * The paths where to look for mapping files.
33
     *
34
     * @var string[]
35
     */
36
    protected $paths = [];
37
38
    /**
39
     * A map of mapping directory path to namespace prefix used to expand class shortnames.
40
     *
41
     * @var string[]
42
     */
43
    protected $prefixes = [];
44
45
    /**
46
     * File extension that is searched for.
47
     *
48
     * @var string|null
49
     */
50
    protected $fileExtension;
51
52
    /**
53
     * Represents PHP namespace delimiters when looking for files
54
     *
55
     * @var string
56
     */
57
    private $nsSeparator;
58
59
    /**
60
     * @param string[] $prefixes
61
     * @param string   $nsSeparator String which would be used when converting FQCN to filename and vice versa. Should not be empty
62
     */
63 14
    public function __construct(
64
        array $prefixes,
65
        ?string $fileExtension = null,
66
        ?string $nsSeparator = '.'
67
    ) {
68 14
        $this->addNamespacePrefixes($prefixes);
69 14
        $this->fileExtension = $fileExtension;
70
71 14
        if (empty($nsSeparator)) {
72 1
            throw new InvalidArgumentException('Namespace separator should not be empty');
73
        }
74
75 13
        $this->nsSeparator = (string) $nsSeparator;
76 13
    }
77
78
    /**
79
     * Adds Namespace Prefixes.
80
     *
81
     * @param string[] $prefixes
82
     */
83 14
    public function addNamespacePrefixes(array $prefixes) : void
84
    {
85 14
        $this->prefixes = array_merge($this->prefixes, $prefixes);
86 14
        $this->paths    = array_merge($this->paths, array_keys($prefixes));
87 14
    }
88
89
    /**
90
     * Gets Namespace Prefixes.
91
     *
92
     * @return string[]
93
     */
94 1
    public function getNamespacePrefixes() : array
95
    {
96 1
        return $this->prefixes;
97
    }
98
99
    /**
100
     * {@inheritDoc}
101
     */
102 1
    public function getPaths() : array
103
    {
104 1
        return $this->paths;
105
    }
106
107
    /**
108
     * {@inheritDoc}
109
     */
110 1
    public function getFileExtension() : ?string
111
    {
112 1
        return $this->fileExtension;
113
    }
114
115
    /**
116
     * Sets the file extension used to look for mapping files under.
117
     *
118
     * @param string $fileExtension The file extension to set.
119
     */
120 1
    public function setFileExtension(string $fileExtension) : void
121
    {
122 1
        $this->fileExtension = $fileExtension;
123 1
    }
124
125
    /**
126
     * {@inheritDoc}
127
     */
128 1
    public function fileExists(string $className) : bool
129
    {
130 1
        $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension;
131
132 1
        foreach ($this->paths as $path) {
133 1
            if (! isset($this->prefixes[$path])) {
134
                // global namespace class
135
                if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) {
136
                    return true;
137
                }
138
139
                continue;
140
            }
141
142 1
            $prefix = $this->prefixes[$path];
143
144 1
            if (strpos($className, $prefix . '\\') !== 0) {
145
                continue;
146
            }
147
148 1
            $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension;
149
150 1
            if (is_file($filename)) {
151 1
                return true;
152
            }
153
        }
154
155 1
        return false;
156
    }
157
158
    /**
159
     * {@inheritDoc}
160
     */
161 3
    public function getAllClassNames(?string $globalBasename = null) : array
162
    {
163 3
        $classes = [];
164
165 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...
166 3
            foreach ((array) $this->paths as $path) {
167 3
                if (! is_dir($path)) {
168
                    throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
169
                }
170
171 3
                $iterator = new RecursiveIteratorIterator(
172 3
                    new RecursiveDirectoryIterator($path),
173 3
                    RecursiveIteratorIterator::LEAVES_ONLY
174
                );
175
176 3
                foreach ($iterator as $file) {
177 3
                    $fileName = $file->getBasename($this->fileExtension);
178
179 3
                    if ($fileName === $file->getBasename() || $fileName === $globalBasename) {
180 3
                        continue;
181
                    }
182
183
                    // NOTE: All files found here means classes are not transient!
184 3
                    if (isset($this->prefixes[$path])) {
185
                        // Calculate namespace suffix for given prefix as a relative path from basepath to file path
186 3
                        $nsSuffix = strtr(
187 3
                            substr($this->realpath($file->getPath()), strlen($this->realpath($path))),
188 3
                            $this->nsSeparator,
189 3
                            '\\'
190
                        );
191
192 3
                        $classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' . str_replace($this->nsSeparator, '\\', $fileName);
193
                    } else {
194
                        /** @var string $className */
195
                        $className = str_replace($this->nsSeparator, '\\', $fileName);
196
197
                        $classes[] = $className;
198
                    }
199
                }
200
            }
201
        }
202
203 3
        return $classes;
204
    }
205
206
    /**
207
     * {@inheritDoc}
208
     */
209 6
    public function findMappingFile(string $className) : string
210
    {
211 6
        $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension;
212 6
        foreach ($this->paths as $path) {
213 6
            if (! isset($this->prefixes[$path])) {
214
                if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) {
215
                    return $path . DIRECTORY_SEPARATOR . $defaultFileName;
216
                }
217
218
                continue;
219
            }
220
221 6
            $prefix = $this->prefixes[$path];
222
223 6
            if (strpos($className, $prefix . '\\') !== 0) {
224
                continue;
225
            }
226
227 6
            $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension;
228 6
            if (is_file($filename)) {
229 5
                return $filename;
230
            }
231
        }
232
233 1
        throw MappingException::mappingFileNotFound(
234 1
            $className,
235 1
            substr($className, strrpos($className, '\\') + 1) . $this->fileExtension
236
        );
237
    }
238
239 3
    private function realpath(string $path) : string
240
    {
241 3
        $realpath = realpath($path);
242
243 3
        if ($realpath === false) {
244
            throw new RuntimeException(sprintf('Could not get realpath for %s', $path));
245
        }
246
247 3
        return $realpath;
248
    }
249
}
250