1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Doctrine\Common\Persistence\Mapping\Driver; |
4
|
|
|
|
5
|
|
|
use Doctrine\Common\Annotations\AnnotationReader; |
6
|
|
|
use Doctrine\Common\Persistence\Mapping\MappingException; |
7
|
|
|
use function array_merge; |
8
|
|
|
use function array_unique; |
9
|
|
|
use function get_class; |
10
|
|
|
use function get_declared_classes; |
11
|
|
|
use function in_array; |
12
|
|
|
use function is_dir; |
13
|
|
|
use function preg_match; |
14
|
|
|
use function preg_quote; |
15
|
|
|
use function realpath; |
16
|
|
|
use function str_replace; |
17
|
|
|
use function strpos; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* The AnnotationDriver reads the mapping metadata from docblock annotations. |
21
|
|
|
*/ |
22
|
|
|
abstract class AnnotationDriver implements MappingDriver |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* The AnnotationReader. |
26
|
|
|
* |
27
|
|
|
* @var AnnotationReader |
28
|
|
|
*/ |
29
|
|
|
protected $reader; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* The paths where to look for mapping files. |
33
|
|
|
* |
34
|
|
|
* @var string[] |
35
|
|
|
*/ |
36
|
|
|
protected $paths = []; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* The paths excluded from path where to look for mapping files. |
40
|
|
|
* |
41
|
|
|
* @var string[] |
42
|
|
|
*/ |
43
|
|
|
protected $excludePaths = []; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* The file extension of mapping documents. |
47
|
|
|
* |
48
|
|
|
* @var string |
49
|
|
|
*/ |
50
|
|
|
protected $fileExtension = '.php'; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Cache for AnnotationDriver#getAllClassNames(). |
54
|
|
|
* |
55
|
|
|
* @var string[]|null |
56
|
|
|
*/ |
57
|
|
|
protected $classNames; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Name of the entity annotations as keys. |
61
|
|
|
* |
62
|
|
|
* @var string[] |
63
|
|
|
*/ |
64
|
|
|
protected $entityAnnotationClasses = []; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading |
68
|
|
|
* docblock annotations. |
69
|
|
|
* |
70
|
|
|
* @param AnnotationReader $reader The AnnotationReader to use, duck-typed. |
71
|
|
|
* @param string|string[]|null $paths One or multiple paths where mapping classes can be found. |
72
|
|
|
*/ |
73
|
1 |
|
public function __construct($reader, $paths = null) |
74
|
|
|
{ |
75
|
1 |
|
$this->reader = $reader; |
76
|
1 |
|
if (! $paths) { |
77
|
|
|
return; |
78
|
|
|
} |
79
|
|
|
|
80
|
1 |
|
$this->addPaths((array) $paths); |
81
|
1 |
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Appends lookup paths to metadata driver. |
85
|
|
|
* |
86
|
|
|
* @param string[] $paths |
87
|
|
|
* |
88
|
|
|
* @return void |
89
|
|
|
*/ |
90
|
1 |
|
public function addPaths(array $paths) |
91
|
|
|
{ |
92
|
1 |
|
$this->paths = array_unique(array_merge($this->paths, $paths)); |
93
|
1 |
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Retrieves the defined metadata lookup paths. |
97
|
|
|
* |
98
|
|
|
* @return string[] |
99
|
|
|
*/ |
100
|
|
|
public function getPaths() |
101
|
|
|
{ |
102
|
|
|
return $this->paths; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Append exclude lookup paths to metadata driver. |
107
|
|
|
* |
108
|
|
|
* @param string[] $paths |
109
|
|
|
*/ |
110
|
|
|
public function addExcludePaths(array $paths) |
111
|
|
|
{ |
112
|
|
|
$this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Retrieve the defined metadata lookup exclude paths. |
117
|
|
|
* |
118
|
|
|
* @return string[] |
119
|
|
|
*/ |
120
|
|
|
public function getExcludePaths() |
121
|
|
|
{ |
122
|
|
|
return $this->excludePaths; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Retrieve the current annotation reader |
127
|
|
|
* |
128
|
|
|
* @return AnnotationReader |
129
|
|
|
*/ |
130
|
|
|
public function getReader() |
131
|
|
|
{ |
132
|
|
|
return $this->reader; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Gets the file extension used to look for mapping files under. |
137
|
|
|
* |
138
|
|
|
* @return string |
139
|
|
|
*/ |
140
|
|
|
public function getFileExtension() |
141
|
|
|
{ |
142
|
|
|
return $this->fileExtension; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Sets the file extension used to look for mapping files under. |
147
|
|
|
* |
148
|
|
|
* @param string $fileExtension The file extension to set. |
149
|
|
|
* |
150
|
|
|
* @return void |
151
|
|
|
*/ |
152
|
|
|
public function setFileExtension($fileExtension) |
153
|
|
|
{ |
154
|
|
|
$this->fileExtension = $fileExtension; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Returns whether the class with the specified name is transient. Only non-transient |
159
|
|
|
* classes, that is entities and mapped superclasses, should have their metadata loaded. |
160
|
|
|
* |
161
|
|
|
* A class is non-transient if it is annotated with an annotation |
162
|
|
|
* from the {@see AnnotationDriver::entityAnnotationClasses}. |
163
|
|
|
* |
164
|
|
|
* @param string $className |
165
|
|
|
* |
166
|
|
|
* @return bool |
167
|
|
|
*/ |
168
|
1 |
|
public function isTransient($className) |
169
|
|
|
{ |
170
|
1 |
|
$classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className)); |
171
|
|
|
|
172
|
1 |
|
foreach ($classAnnotations as $annot) { |
173
|
1 |
|
if (isset($this->entityAnnotationClasses[get_class($annot)])) { |
174
|
1 |
|
return false; |
175
|
|
|
} |
176
|
|
|
} |
177
|
1 |
|
return true; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* {@inheritDoc} |
182
|
|
|
*/ |
183
|
1 |
|
public function getAllClassNames() |
184
|
|
|
{ |
185
|
1 |
|
if ($this->classNames !== null) { |
186
|
|
|
return $this->classNames; |
187
|
|
|
} |
188
|
|
|
|
189
|
1 |
|
if (! $this->paths) { |
|
|
|
|
190
|
|
|
throw MappingException::pathRequired(); |
191
|
|
|
} |
192
|
|
|
|
193
|
1 |
|
$classes = []; |
194
|
1 |
|
$includedFiles = []; |
195
|
|
|
|
196
|
1 |
|
foreach ($this->paths as $path) { |
197
|
1 |
|
if (! is_dir($path)) { |
198
|
|
|
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); |
199
|
|
|
} |
200
|
|
|
|
201
|
1 |
|
$iterator = new \RegexIterator( |
202
|
1 |
|
new \RecursiveIteratorIterator( |
203
|
1 |
|
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), |
204
|
1 |
|
\RecursiveIteratorIterator::LEAVES_ONLY |
205
|
|
|
), |
206
|
1 |
|
'/^.+' . preg_quote($this->fileExtension) . '$/i', |
207
|
1 |
|
\RecursiveRegexIterator::GET_MATCH |
208
|
|
|
); |
209
|
|
|
|
210
|
1 |
|
foreach ($iterator as $file) { |
211
|
1 |
|
$sourceFile = $file[0]; |
212
|
|
|
|
213
|
1 |
|
if (! preg_match('(^phar:)i', $sourceFile)) { |
214
|
1 |
|
$sourceFile = realpath($sourceFile); |
215
|
|
|
} |
216
|
|
|
|
217
|
1 |
|
foreach ($this->excludePaths as $excludePath) { |
218
|
|
|
$exclude = str_replace('\\', '/', realpath($excludePath)); |
219
|
|
|
$current = str_replace('\\', '/', $sourceFile); |
220
|
|
|
|
221
|
|
|
if (strpos($current, $exclude) !== false) { |
222
|
|
|
continue 2; |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
|
226
|
1 |
|
require_once $sourceFile; |
227
|
|
|
|
228
|
1 |
|
$includedFiles[] = $sourceFile; |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
1 |
|
$declared = get_declared_classes(); |
233
|
|
|
|
234
|
1 |
|
foreach ($declared as $className) { |
235
|
1 |
|
$rc = new \ReflectionClass($className); |
236
|
1 |
|
$sourceFile = $rc->getFileName(); |
237
|
1 |
|
if (! in_array($sourceFile, $includedFiles) || $this->isTransient($className)) { |
238
|
1 |
|
continue; |
239
|
|
|
} |
240
|
|
|
|
241
|
1 |
|
$classes[] = $className; |
242
|
|
|
} |
243
|
|
|
|
244
|
1 |
|
$this->classNames = $classes; |
245
|
|
|
|
246
|
1 |
|
return $classes; |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
|
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.