1
+ <?php
2
+ /**
3
+ * Copyright © Magento, Inc. All rights reserved.
4
+ * See COPYING.txt for license details.
5
+ */
6
+ declare (strict_types=1 );
7
+
8
+ namespace Magento \SemanticVersionChecker \Analyzer ;
9
+
10
+ use PhpParser \Node \Stmt ;
11
+ use PHPSemVerChecker \Registry \Registry ;
12
+ use PHPSemVerChecker \Report \Report ;
13
+ use PHPSemVerChecker \SemanticVersioning \Level ;
14
+ use Magento \SemanticVersionChecker \Operation \EtSchema \EtSchemaOperation ;
15
+
16
+ /**
17
+ * Class EtSchemaAnalyzer analyzes changes in et_schema.xml
18
+ */
19
+ class EtSchemaAnalyzer implements AnalyzerInterface
20
+ {
21
+ private static $ actions = [
22
+ 'addedRecord ' => [
23
+ 'level ' => Level::MINOR ,
24
+ 'code ' => 'T004 ' ,
25
+ 'message ' => 'Added a new declaration for record %s. '
26
+ ],
27
+ 'removedRecord ' => [
28
+ 'level ' => Level::MAJOR ,
29
+ 'code ' => 'T001 ' ,
30
+ 'message ' => 'Removed declaration for type %s. '
31
+ ],
32
+ 'addedField ' => [
33
+ 'level ' => Level::PATCH ,
34
+ 'code ' => 'T005 ' ,
35
+ 'message ' => 'Added field %s to type %s. '
36
+ ],
37
+ 'removedField ' => [
38
+ 'level ' => Level::MAJOR ,
39
+ 'code ' => 'T002 ' ,
40
+ 'message ' => 'Removed field %s from type %s. '
41
+ ],
42
+ 'changedField ' => [
43
+ 'level ' => Level::MAJOR ,
44
+ 'code ' => 'T003 ' ,
45
+ 'message ' => 'Changed field %s declaration in type %s. '
46
+ ]
47
+ ];
48
+
49
+ /**
50
+ * @var string
51
+ */
52
+ private $ context = 'etSchema ' ;
53
+
54
+ /**
55
+ * @var Report
56
+ */
57
+ private $ report ;
58
+
59
+ /**
60
+ * Constructor.
61
+ *
62
+ * @param Report $report
63
+ */
64
+ public function __construct (Report $ report )
65
+ {
66
+ $ this ->report = $ report ;
67
+ }
68
+
69
+ private function reportAddedModuleConfig (array $ moduleConfig ): array
70
+ {
71
+ return [];
72
+ }
73
+
74
+ private function removedModuleConfig (array $ moduleConfig ): array
75
+ {
76
+ return [];
77
+ }
78
+
79
+ /**
80
+ * Register record creation
81
+ *
82
+ * @param string $moduleName
83
+ * @param string $recordName
84
+ * @return array
85
+ */
86
+ private function addedRecord (string $ moduleName , string $ recordName ): array
87
+ {
88
+ return [
89
+ 'level ' => self ::$ actions [__FUNCTION__ ]['level ' ],
90
+ 'code ' => self ::$ actions [__FUNCTION__ ]['code ' ],
91
+ 'location ' => sprintf ('urn:magento:module:%s:etc/et_schema.xml %s ' , $ moduleName , $ recordName ),
92
+ 'target ' => $ recordName ,
93
+ 'reason ' => sprintf (self ::$ actions [__FUNCTION__ ]['message ' ], $ recordName )
94
+ ];
95
+ }
96
+
97
+ /**
98
+ * Register record removal
99
+ *
100
+ * @param string $moduleName
101
+ * @param string $recordName
102
+ * @return array
103
+ */
104
+ private function removedRecord (string $ moduleName , string $ recordName ): array
105
+ {
106
+ return [
107
+ 'level ' => self ::$ actions [__FUNCTION__ ]['level ' ],
108
+ 'code ' => self ::$ actions [__FUNCTION__ ]['code ' ],
109
+ 'location ' => sprintf ('urn:magento:module:%s:etc/et_schema.xml %s ' , $ moduleName , $ recordName ),
110
+ 'target ' => $ recordName ,
111
+ 'reason ' => sprintf (self ::$ actions [__FUNCTION__ ]['message ' ], $ recordName )
112
+ ];
113
+ }
114
+
115
+ /**
116
+ * @param string $moduleName
117
+ * @param string $recordName
118
+ * @param string $fieldName
119
+ * @return array
120
+ */
121
+ private function removedField (string $ moduleName , string $ recordName , string $ fieldName ): array
122
+ {
123
+ return [
124
+ 'level ' => self ::$ actions [__FUNCTION__ ]['level ' ],
125
+ 'code ' => self ::$ actions [__FUNCTION__ ]['code ' ],
126
+ 'location ' => sprintf (
127
+ 'urn:magento:module:%s:etc/et_schema.xml %s:%s ' ,
128
+ $ moduleName ,
129
+ $ recordName ,
130
+ $ fieldName
131
+ ),
132
+ 'target ' => $ recordName ,
133
+ 'reason ' => sprintf (self ::$ actions [__FUNCTION__ ]['message ' ], $ fieldName , $ recordName )
134
+ ];
135
+ }
136
+
137
+ /**
138
+ * @param string $moduleName
139
+ * @param string $recordName
140
+ * @param string $fieldName
141
+ * @return array
142
+ */
143
+ private function addedField (string $ moduleName , string $ recordName , string $ fieldName ): array
144
+ {
145
+ return [
146
+ 'level ' => self ::$ actions [__FUNCTION__ ]['level ' ],
147
+ 'code ' => self ::$ actions [__FUNCTION__ ]['code ' ],
148
+ 'location ' => sprintf (
149
+ 'urn:magento:module:%s:etc/et_schema.xml %s:%s ' ,
150
+ $ moduleName ,
151
+ $ recordName ,
152
+ $ fieldName
153
+ ),
154
+ 'target ' => $ recordName ,
155
+ 'reason ' => sprintf (self ::$ actions [__FUNCTION__ ]['message ' ], $ fieldName , $ recordName )
156
+ ];
157
+ }
158
+
159
+ /**
160
+ * @param string $moduleName
161
+ * @param string $recordName
162
+ * @param string $fieldName
163
+ * @return array
164
+ */
165
+ private function changedField (string $ moduleName , string $ recordName , string $ fieldName ): array
166
+ {
167
+ return [
168
+ 'level ' => self ::$ actions [__FUNCTION__ ]['level ' ],
169
+ 'code ' => self ::$ actions [__FUNCTION__ ]['code ' ],
170
+ 'location ' => sprintf (
171
+ 'urn:magento:module:%s:etc/et_schema.xml %s:%s ' ,
172
+ $ moduleName ,
173
+ $ recordName ,
174
+ $ fieldName
175
+ ),
176
+ 'target ' => $ recordName ,
177
+ 'reason ' => sprintf (self ::$ actions [__FUNCTION__ ]['message ' ], $ fieldName , $ recordName )
178
+ ];
179
+ }
180
+
181
+ /**
182
+ * Analyze record structure
183
+ *
184
+ * @param string $moduleName
185
+ * @param $beforeRecord
186
+ * @param $afterRecord
187
+ * @return array
188
+ */
189
+ private function analyzeRecord (string $ moduleName , $ beforeRecord , $ afterRecord ): array
190
+ {
191
+ $ changes = [];
192
+ $ commonFields = array_intersect (
193
+ array_keys ($ beforeRecord ['field ' ]),
194
+ array_keys ($ afterRecord ['field ' ])
195
+ );
196
+ foreach ($ commonFields as $ fieldName ) {
197
+ if ($ beforeRecord ['field ' ][$ fieldName ]['type ' ] != $ afterRecord ['field ' ][$ fieldName ]['type ' ]
198
+ || $ beforeRecord ['field ' ][$ fieldName ]['repeated ' ] != $ afterRecord ['field ' ][$ fieldName ]['repeated ' ]
199
+ ) {
200
+ $ this ->changedField ($ moduleName , $ beforeRecord ['name ' ], $ fieldName );
201
+ }
202
+ }
203
+ $ diff = array_merge (
204
+ array_diff (
205
+ array_keys ($ beforeRecord ['field ' ]),
206
+ array_keys ($ afterRecord ['field ' ])
207
+ ),
208
+ array_diff (
209
+ array_keys ($ afterRecord ['field ' ]),
210
+ array_keys ($ beforeRecord ['field ' ])
211
+ )
212
+ );
213
+ foreach ($ diff as $ fieldName ) {
214
+ if (isset ($ beforeRecord ['field ' ][$ fieldName ])) {
215
+ $ changes [] = $ this ->removedField ($ moduleName , $ beforeRecord ['name ' ], $ fieldName );
216
+ } else {
217
+ $ changes [] = $ this ->addedField ($ moduleName , $ afterRecord ['name ' ], $ fieldName );
218
+ }
219
+ }
220
+ return $ changes ;
221
+ }
222
+
223
+ /**
224
+ * Analyze module configuration file
225
+ *
226
+ * @param string $moduleName
227
+ * @param array $beforeModuleConfig
228
+ * @param array $afterModuleConfig
229
+ * @return array
230
+ */
231
+ private function analyzeModuleConfig (string $ moduleName , array $ beforeModuleConfig , array $ afterModuleConfig ): array
232
+ {
233
+ $ changes = [];
234
+ $ commonRecords = array_intersect (
235
+ array_keys ($ beforeModuleConfig ),
236
+ array_keys ($ afterModuleConfig )
237
+ );
238
+ foreach ($ commonRecords as $ recordName ) {
239
+ $ changes += $ this ->analyzeRecord (
240
+ $ moduleName ,
241
+ $ beforeModuleConfig [$ recordName ],
242
+ $ afterModuleConfig [$ recordName ]
243
+ );
244
+ }
245
+ $ diff = array_merge (
246
+ array_diff (
247
+ array_keys ($ beforeModuleConfig ),
248
+ array_keys ($ afterModuleConfig )
249
+ ),
250
+ array_diff (
251
+ array_keys ($ afterModuleConfig ),
252
+ array_keys ($ beforeModuleConfig )
253
+ )
254
+ );
255
+ foreach ($ diff as $ recordName ) {
256
+ if (isset ($ beforeModuleConfig [$ recordName ])) {
257
+ $ changes [] = $ this ->removedRecord ($ moduleName , $ recordName );
258
+ } else {
259
+ $ changes [] = $ this ->addedRecord ($ moduleName , $ recordName );
260
+ }
261
+ }
262
+ return $ changes ;
263
+ }
264
+
265
+ /**
266
+ * Register changes to the report
267
+ *
268
+ * @param array $changes
269
+ */
270
+ public function reportChanges (array $ changes ): void
271
+ {
272
+ foreach ($ changes as $ change ) {
273
+ $ this ->report ->add (
274
+ $ this ->context ,
275
+ new EtSchemaOperation (
276
+ $ change ['location ' ],
277
+ $ change ['code ' ],
278
+ $ change ['target ' ],
279
+ $ change ['reason ' ],
280
+ $ change ['level ' ]
281
+ )
282
+ );
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Analyze configuration changes
288
+ *
289
+ * @param Stmt|Registry $registryBefore
290
+ * @param Stmt|Registry $registryAfter
291
+ * @return Report
292
+ */
293
+ public function analyze ($ registryBefore , $ registryAfter )
294
+ {
295
+ $ changes = [];
296
+ $ commonModules = array_intersect (
297
+ array_keys ($ registryBefore ->data ['etSchema ' ]),
298
+ array_keys ($ registryAfter ->data ['etSchema ' ])
299
+ );
300
+ foreach ($ commonModules as $ moduleName ) {
301
+ $ changes += $ this ->analyzeModuleConfig (
302
+ $ moduleName ,
303
+ $ registryBefore ->data ['etSchema ' ][$ moduleName ],
304
+ $ registryAfter ->data ['etSchema ' ][$ moduleName ]
305
+ );
306
+ }
307
+ $ this ->reportChanges ($ changes );
308
+ return $ this ->report ;
309
+ }
310
+ }
0 commit comments