1
|
|
|
<?php
|
2
|
|
|
|
3
|
|
|
/*
|
4
|
|
|
* This file is part of the "RocketORM" package.
|
5
|
|
|
*
|
6
|
|
|
* https://github.com/RocketORM/ORM
|
7
|
|
|
*
|
8
|
|
|
* For the full license information, please view the LICENSE
|
9
|
|
|
* file that was distributed with this source code.
|
10
|
|
|
*/
|
11
|
|
|
|
12
|
|
|
namespace Rocket\ORM\Generator\Schema\Loader;
|
13
|
|
|
|
14
|
|
|
use Rocket\ORM\Generator\Schema\Configuration\SchemaConfiguration;
|
15
|
|
|
use Rocket\ORM\Generator\Schema\Loader\Exception\InvalidConfigurationException;
|
16
|
|
|
use Rocket\ORM\Generator\Schema\Loader\Exception\SchemaNotFoundException;
|
17
|
|
|
use Rocket\ORM\Generator\Schema\Schema;
|
18
|
|
|
use Rocket\ORM\Generator\Schema\Transformer\SchemaRelationTransformerInterface;
|
19
|
|
|
use Rocket\ORM\Generator\Schema\Transformer\SchemaTransformerInterface;
|
20
|
|
|
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException as ConfigurationException;
|
21
|
|
|
use Symfony\Component\Config\Definition\Processor;
|
22
|
|
|
use Symfony\Component\Config\Util\XmlUtils;
|
23
|
|
|
use Symfony\Component\Finder\Finder;
|
24
|
|
|
use Symfony\Component\Finder\SplFileInfo;
|
25
|
|
|
use Symfony\Component\Yaml\Yaml;
|
26
|
|
|
|
27
|
|
|
/**
|
28
|
|
|
* @author Sylvain Lorinet <[email protected]>
|
29
|
|
|
*/
|
30
|
|
|
class SchemaLoader
|
31
|
|
|
{
|
32
|
|
|
/**
|
33
|
|
|
* @var string|array
|
34
|
|
|
*/
|
35
|
|
|
protected $path;
|
36
|
|
|
|
37
|
|
|
/**
|
38
|
|
|
* @var string|array
|
39
|
|
|
*/
|
40
|
|
|
protected $exclude;
|
41
|
|
|
|
42
|
|
|
/**
|
43
|
|
|
* @var array
|
44
|
|
|
*/
|
45
|
|
|
protected $options;
|
46
|
|
|
|
47
|
|
|
|
48
|
|
|
/**
|
49
|
|
|
* @param string|array $path
|
50
|
|
|
* @param array $options
|
51
|
|
|
*/
|
52
|
6 |
|
public function __construct($path, array $options = [])
|
53
|
|
|
{
|
54
|
6 |
|
$this->path = $path;
|
55
|
6 |
|
$this->options = array_replace_recursive($this->getDefaultOptions(), $options);
|
56
|
6 |
|
}
|
57
|
|
|
|
58
|
|
|
/**
|
59
|
|
|
* @return array|Schema[]
|
60
|
|
|
*
|
61
|
|
|
* @throws InvalidConfigurationException
|
62
|
|
|
* @throws SchemaNotFoundException
|
63
|
|
|
*/
|
64
|
2 |
|
public function load()
|
65
|
|
|
{
|
66
|
2 |
|
$schemaLocations = $this->getSchemasLocation();
|
67
|
1 |
|
$schemas = [];
|
68
|
|
|
|
69
|
1 |
|
foreach ($schemaLocations['xml'] as $path) {
|
70
|
1 |
|
$schemas[$path] = $this->parseXml($path);
|
71
|
1 |
|
}
|
72
|
|
|
|
73
|
1 |
|
foreach ($schemaLocations['yml'] as $path) {
|
74
|
1 |
|
$schemas[$path] = $this->parseYaml($path);
|
75
|
1 |
|
}
|
76
|
|
|
|
77
|
1 |
|
return $this->validate($schemas);
|
78
|
|
|
}
|
79
|
|
|
|
80
|
|
|
/**
|
81
|
|
|
* @param array $schemas
|
82
|
|
|
*
|
83
|
|
|
* @return array
|
84
|
|
|
*
|
85
|
|
|
* @throws InvalidConfigurationException
|
86
|
|
|
*/
|
87
|
5 |
|
protected function validate(array $schemas)
|
88
|
|
|
{
|
89
|
5 |
|
$processor = new Processor();
|
90
|
5 |
|
$configuration = new SchemaConfiguration();
|
91
|
|
|
|
92
|
5 |
|
$defaultOptions = $this->getDefaultOptions();
|
93
|
5 |
|
$transformerClass = $this->options['transformer']['schema']['class'];
|
94
|
|
|
|
95
|
5 |
|
if (is_object($transformerClass)) {
|
96
|
1 |
|
$schemaTransformer = $transformerClass;
|
97
|
1 |
|
} else {
|
98
|
4 |
|
$schemaTransformer = new $transformerClass($this->options['model'], $defaultOptions['model']);
|
99
|
|
|
}
|
100
|
|
|
|
101
|
5 |
|
if (!$schemaTransformer instanceof SchemaTransformerInterface) {
|
102
|
1 |
|
throw new \InvalidArgumentException(
|
103
|
1 |
|
'The schema transformer class "' . $transformerClass . '" should implements '
|
104
|
1 |
|
. '\Rocket\ORM\Generator\Schema\Transformer\SchemaTransformerInterface'
|
105
|
1 |
|
);
|
106
|
|
|
}
|
107
|
|
|
|
108
|
4 |
|
$normalizedSchemas = [];
|
109
|
4 |
|
foreach ($schemas as $path => $schema) {
|
110
|
|
|
try {
|
111
|
4 |
|
$normalizedSchemas[$path] = $schemaTransformer->transform(
|
112
|
4 |
|
$processor->processConfiguration($configuration, [$schema]),
|
113
|
|
|
$path
|
114
|
4 |
|
);
|
115
|
4 |
|
} catch (ConfigurationException $e) {
|
116
|
1 |
|
throw new InvalidConfigurationException($path, $e);
|
117
|
|
|
}
|
118
|
3 |
|
}
|
119
|
|
|
|
120
|
3 |
|
return $this->validateRelations($normalizedSchemas);
|
121
|
|
|
}
|
122
|
|
|
|
123
|
|
|
/**
|
124
|
|
|
* @param array|Schema[] $normalizedSchemas
|
125
|
|
|
*
|
126
|
|
|
* @return array|Schema[]
|
127
|
|
|
*
|
128
|
|
|
* @throws InvalidConfigurationException
|
129
|
|
|
*/
|
130
|
3 |
|
protected function validateRelations(array $normalizedSchemas)
|
131
|
|
|
{
|
132
|
3 |
|
$transformerClass = $this->options['transformer']['relation']['class'];
|
133
|
3 |
|
if (is_object($transformerClass)) {
|
134
|
1 |
|
$schemaRelationTransformer = $transformerClass;
|
135
|
1 |
|
} else {
|
136
|
2 |
|
$schemaRelationTransformer = new $transformerClass();
|
137
|
|
|
}
|
138
|
|
|
|
139
|
3 |
|
if (!$schemaRelationTransformer instanceof SchemaRelationTransformerInterface) {
|
140
|
1 |
|
throw new \InvalidArgumentException(
|
141
|
1 |
|
'The schema relation transformer class "' . $transformerClass . '" should implements '
|
142
|
1 |
|
. '\Rocket\ORM\Generator\Schema\Transformer\SchemaRelationTransformerInterface'
|
143
|
1 |
|
);
|
144
|
|
|
}
|
145
|
|
|
|
146
|
2 |
|
$validSchemas = [];
|
147
|
|
|
|
148
|
|
|
// All schemas are loaded, now we can validate relations
|
149
|
|
|
/** @var Schema $schema */
|
150
|
2 |
|
foreach ($normalizedSchemas as $path => $schema) {
|
151
|
|
|
try {
|
152
|
2 |
|
foreach ($schema->getTables() as $table) {
|
153
|
2 |
|
$schemaRelationTransformer->transform($table, $normalizedSchemas);
|
154
|
1 |
|
}
|
155
|
|
|
|
156
|
|
|
// Must wait for the relation transformations above ($relation->with)
|
157
|
1 |
|
foreach ($schema->getTables() as $table) {
|
158
|
1 |
|
$schemaRelationTransformer->transformRelatedRelations($table, $normalizedSchemas);
|
|
|
|
|
159
|
1 |
|
}
|
160
|
2 |
|
} catch (ConfigurationException $e) {
|
161
|
1 |
|
throw new InvalidConfigurationException($path, $e);
|
162
|
|
|
}
|
163
|
|
|
|
164
|
1 |
|
$validSchemas[] = $schema;
|
165
|
1 |
|
}
|
166
|
|
|
|
167
|
1 |
|
return $validSchemas;
|
168
|
|
|
}
|
169
|
|
|
|
170
|
|
|
/**
|
171
|
|
|
* @return array
|
172
|
|
|
*
|
173
|
|
|
* @throws SchemaNotFoundException
|
174
|
|
|
*/
|
175
|
2 |
|
protected function getSchemasLocation()
|
176
|
|
|
{
|
177
|
2 |
|
$finder = new Finder();
|
178
|
|
|
$finder
|
179
|
2 |
|
->files()
|
180
|
2 |
|
->in($this->path)
|
181
|
2 |
|
->exclude($this->options['exclude'])
|
182
|
2 |
|
->name('/(.*)?schema.(yml|xml)/')
|
183
|
|
|
;
|
184
|
|
|
|
185
|
2 |
|
if (0 === $finder->count()) {
|
186
|
1 |
|
throw new SchemaNotFoundException('Schema not found in path "' . $this->path . '"');
|
187
|
|
|
}
|
188
|
|
|
|
189
|
|
|
$schemas = [
|
190
|
1 |
|
'xml' => [],
|
191
|
1 |
|
'yml' => []
|
192
|
1 |
|
];
|
193
|
|
|
|
194
|
|
|
/** @var SplFileInfo $file */
|
195
|
1 |
|
foreach ($finder as $file) {
|
196
|
1 |
|
if ('xml' == strtolower($file->getExtension())) {
|
197
|
1 |
|
$schemas['xml'][] = $file->getRealPath();
|
198
|
1 |
|
} elseif ('yml' == strtolower($file->getExtension())) {
|
199
|
1 |
|
$schemas['yml'][] = $file->getRealPath();
|
200
|
1 |
|
}
|
201
|
|
|
|
202
|
|
|
// else, extension not implemented
|
203
|
1 |
|
}
|
204
|
|
|
|
205
|
1 |
|
return $schemas;
|
206
|
|
|
}
|
207
|
|
|
|
208
|
|
|
/**
|
209
|
|
|
* @param string $path
|
210
|
|
|
*
|
211
|
|
|
* @return array
|
|
|
|
|
212
|
|
|
*/
|
213
|
1 |
|
protected function parseXml($path)
|
214
|
|
|
{
|
215
|
1 |
|
return XmlUtils::convertDomElementToArray(XmlUtils::loadFile($path)->documentElement);
|
216
|
|
|
}
|
217
|
|
|
|
218
|
|
|
/**
|
219
|
|
|
* @param string $path
|
220
|
|
|
*
|
221
|
|
|
* @return array
|
|
|
|
|
222
|
|
|
*/
|
223
|
1 |
|
protected function parseYaml($path)
|
224
|
|
|
{
|
225
|
1 |
|
return Yaml::parse(file_get_contents($path));
|
226
|
|
|
}
|
227
|
|
|
|
228
|
|
|
/**
|
229
|
|
|
* Return the default available configurable options
|
230
|
|
|
*
|
231
|
|
|
* @return array
|
232
|
|
|
*/
|
233
|
6 |
|
protected function getDefaultOptions()
|
234
|
|
|
{
|
235
|
|
|
return [
|
236
|
6 |
|
'exclude' => null,
|
237
|
|
|
'model' => [
|
238
|
6 |
|
'schema' => ['class' => '\Rocket\ORM\Generator\Schema\Schema'],
|
239
|
6 |
|
'table' => ['class' => '\Rocket\ORM\Generator\Schema\Table'],
|
240
|
6 |
|
'column' => ['class' => '\Rocket\ORM\Generator\Schema\Column'],
|
241
|
6 |
|
'relation' => ['class' => '\Rocket\ORM\Generator\Schema\Relation']
|
242
|
6 |
|
],
|
243
|
|
|
'transformer' => [
|
244
|
6 |
|
'schema' => ['class' => '\Rocket\ORM\Generator\Schema\Transformer\SchemaTransformer'],
|
245
|
6 |
|
'relation' => ['class' => '\Rocket\ORM\Generator\Schema\Transformer\SchemaRelationTransformer']
|
246
|
6 |
|
]
|
247
|
6 |
|
];
|
248
|
|
|
}
|
249
|
|
|
}
|
250
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.