Completed
Pull Request — master (#50)
by Jonathan
15:11 queued 13:00
created

FileDriver::getElement()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.25

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 22
ccs 9
cts 12
cp 0.75
rs 9.9
c 0
b 0
f 0
cc 4
nc 6
nop 1
crap 4.25
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Persistence\Mapping\Driver;
6
7
use Doctrine\Persistence\Mapping\ClassMetadata;
8
use Doctrine\Persistence\Mapping\MappingException;
9
use function array_keys;
10
use function array_merge;
11
use function array_unique;
12
use function is_file;
13
use function is_string;
14
use function str_replace;
15
16
/**
17
 * Base driver for file-based metadata drivers.
18
 *
19
 * A file driver operates in a mode where it loads the mapping files of individual
20
 * classes on demand. This requires the user to adhere to the convention of 1 mapping
21
 * file per class and the file names of the mapping files must correspond to the full
22
 * class name, including namespace, with the namespace delimiters '\', replaced by dots '.'.
23
 */
24
abstract class FileDriver implements MappingDriver
25
{
26
    /** @var FileLocator */
27
    protected $locator;
28
29
    /** @var ClassMetadata[]|null */
30
    protected $classCache;
31
32
    /** @var string */
33
    protected $globalBasename = '';
34
35
    /**
36
     * Initializes a new FileDriver that looks in the given path(s) for mapping
37
     * documents and operates in the specified operating mode.
38
     *
39
     * @param string|array<int, string>|FileLocator $locator A FileLocator or one/multiple paths
40
     *                                                       where mapping documents can be found.
41
     */
42 12
    public function __construct($locator, ?string $fileExtension = null)
43
    {
44 12
        if ($locator instanceof FileLocator) {
45 9
            $this->locator = $locator;
46
        } else {
47 3
            $paths = is_string($locator) ? [$locator] : $locator;
48
49 3
            $this->locator = new DefaultFileLocator($paths, $fileExtension);
50
        }
51 12
    }
52
53
    /**
54
     * Sets the global basename.
55
     */
56 6
    public function setGlobalBasename(string $file) : void
57
    {
58 6
        $this->globalBasename = $file;
59 6
    }
60
61
    /**
62
     * Retrieves the global basename.
63
     */
64 1
    public function getGlobalBasename() : string
65
    {
66 1
        return $this->globalBasename;
67
    }
68
69
    /**
70
     * Gets the element of schema meta data for the class from the mapping file.
71
     * This will lazily load the mapping file if it is not loaded yet.
72
     *
73
     * @return ClassMetadata The element of schema meta data.
74
     *
75
     * @throws MappingException
76
     */
77 4
    public function getElement(string $className) : ClassMetadata
78
    {
79 4
        if ($this->classCache === null) {
80 4
            $this->initialize();
81
        }
82
83 4
        if (isset($this->classCache[$className])) {
84 2
            return $this->classCache[$className];
85
        }
86
87 3
        $result = $this->loadMappingFile($this->locator->findMappingFile($className));
88
89 3
        if (! isset($result[$className])) {
90
            throw MappingException::invalidMappingFile(
91
                $className,
92
                str_replace('\\', '.', $className) . $this->locator->getFileExtension()
93
            );
94
        }
95
96 3
        $this->classCache[$className] = $result[$className];
97
98 3
        return $result[$className];
99
    }
100
101
    /**
102
     * {@inheritDoc}
103
     */
104 3
    public function isTransient(string $className) : bool
105
    {
106 3
        if ($this->classCache === null) {
107 3
            $this->initialize();
108
        }
109
110 3
        if (isset($this->classCache[$className])) {
111 1
            return false;
112
        }
113
114 3
        return ! $this->locator->fileExists($className);
115
    }
116
117
    /**
118
     * {@inheritDoc}
119
     */
120 4
    public function getAllClassNames() : array
121
    {
122 4
        if ($this->classCache === null) {
123 3
            $this->initialize();
124
        }
125
126 4
        if ($this->classCache === []) {
127 1
            return $this->locator->getAllClassNames($this->globalBasename);
128
        }
129
130
        /** @var ClassMetadata[] $classCache */
131 3
        $classCache = $this->classCache;
132
133
        /** @var string[] $keys */
134 3
        $keys = array_keys($classCache);
135
136
        /** @var string[] $merged */
137 3
        $merged = array_unique(array_merge(
138 3
            $keys,
139 3
            $this->locator->getAllClassNames($this->globalBasename)
140
        ));
141
142 3
        return $merged;
143
    }
144
145
    /**
146
     * Loads a mapping file with the given name and returns a map
147
     * from class/entity names to their corresponding file driver elements.
148
     *
149
     * @param string $file The mapping file to load.
150
     *
151
     * @return ClassMetadata[]
152
     */
153
    abstract protected function loadMappingFile(string $file) : array;
154
155
    /**
156
     * Initializes the class cache from all the global files.
157
     *
158
     * Using this feature adds a substantial performance hit to file drivers as
159
     * more metadata has to be loaded into memory than might actually be
160
     * necessary. This may not be relevant to scenarios where caching of
161
     * metadata is in place, however hits very hard in scenarios where no
162
     * caching is used.
163
     */
164 10
    protected function initialize() : void
165
    {
166 10
        $this->classCache = [];
167 10
        if ($this->globalBasename === null) {
168
            return;
169
        }
170
171 10
        foreach ($this->locator->getPaths() as $path) {
172 10
            $file = $path . '/' . $this->globalBasename . $this->locator->getFileExtension();
173 10
            if (! is_file($file)) {
174 5
                continue;
175
            }
176
177 5
            $this->classCache = array_merge(
178 5
                $this->classCache,
179 5
                $this->loadMappingFile($file)
180
            );
181
        }
182 10
    }
183
184
    /**
185
     * Retrieves the locator used to discover mapping files by className.
186
     */
187
    public function getLocator() : FileLocator
188
    {
189
        return $this->locator;
190
    }
191
192
    /**
193
     * Sets the locator used to discover mapping files by className.
194
     */
195
    public function setLocator(FileLocator $locator) : void
196
    {
197
        $this->locator = $locator;
198
    }
199
}
200