@@ -106,6 +106,11 @@ protected function outputTable(OutputInterface $output, Report $report, $context
106
106
$ reportForLevel = $ report [$ context ][$ level ];
107
107
/** @var \PHPSemVerChecker\Operation\Operation $operation */
108
108
foreach ($ reportForLevel as $ operation ) {
109
+ // Skip private method/property changes as they shouldn't be in breaking change reports
110
+ if ($ this ->isPrivateChange ($ operation )) {
111
+ continue ;
112
+ }
113
+
109
114
$ levelLabel = $ this ->getLevelLabel ($ level );
110
115
$ target = $ operation ->getTarget ();
111
116
$ reason = $ operation ->getReason ();
@@ -136,6 +141,128 @@ private function getLevelLabel(int $level): string
136
141
}
137
142
}
138
143
144
+ /**
145
+ * Check if the operation represents a private method or property change
146
+ *
147
+ * Private changes are filtered out as they don't affect the public API contract.
148
+ *
149
+ * @param \PHPSemVerChecker\Operation\Operation $operation
150
+ * @return bool
151
+ */
152
+ private function isPrivateChange ($ operation ): bool
153
+ {
154
+ $ target = $ operation ->getTarget ();
155
+ $ reason = $ operation ->getReason ();
156
+ $ operationClass = get_class ($ operation );
157
+
158
+ // For visibility operations, check if they involve private visibility
159
+ if ($ operation instanceof \Magento \SemanticVersionChecker \Operation \VisibilityOperation) {
160
+ try {
161
+ // Use reflection to access protected properties
162
+ $ reflection = new \ReflectionClass ($ operation );
163
+
164
+ if ($ reflection ->hasProperty ('memberBefore ' )) {
165
+ $ memberBeforeProperty = $ reflection ->getProperty ('memberBefore ' );
166
+ $ memberBeforeProperty ->setAccessible (true );
167
+ $ memberBefore = $ memberBeforeProperty ->getValue ($ operation );
168
+
169
+ if ($ memberBefore && method_exists ('\PHPSemVerChecker\Operation\Visibility ' , 'getForContext ' )) {
170
+ $ visibilityBefore = \PHPSemVerChecker \Operation \Visibility::getForContext ($ memberBefore );
171
+ // 1 = public, 2 = protected, 3 = private
172
+ if ($ visibilityBefore === 3 ) {
173
+ return true ;
174
+ }
175
+ }
176
+ }
177
+
178
+ if ($ reflection ->hasProperty ('memberAfter ' )) {
179
+ $ memberAfterProperty = $ reflection ->getProperty ('memberAfter ' );
180
+ $ memberAfterProperty ->setAccessible (true );
181
+ $ memberAfter = $ memberAfterProperty ->getValue ($ operation );
182
+
183
+ if ($ memberAfter && method_exists ('\PHPSemVerChecker\Operation\Visibility ' , 'getForContext ' )) {
184
+ $ visibilityAfter = \PHPSemVerChecker \Operation \Visibility::getForContext ($ memberAfter );
185
+ // 1 = public, 2 = protected, 3 = private
186
+ if ($ visibilityAfter === 3 ) {
187
+ return true ;
188
+ }
189
+ }
190
+ }
191
+ } catch (\Exception $ e ) {
192
+ // Fall back to string matching if reflection fails
193
+ }
194
+ }
195
+
196
+ // Check if the reason explicitly mentions private visibility
197
+ if (preg_match ('/\[private\]/ ' , $ reason )) {
198
+ return true ;
199
+ }
200
+
201
+ // Check if the target or reason indicates a private method/property
202
+ $ privateIndicators = [
203
+ 'private method ' ,
204
+ 'private property ' ,
205
+ 'Private method ' ,
206
+ 'Private property ' ,
207
+ '::private ' ,
208
+ ' private ' ,
209
+ 'private function ' ,
210
+ 'private static ' ,
211
+ 'visibility has been changed to lower lever from private ' ,
212
+ 'visibility has been changed to higher lever from private ' ,
213
+ 'visibility has been changed from private ' ,
214
+ 'visibility has been changed to private ' ,
215
+ 'Method visibility has been changed from public to private ' ,
216
+ 'Method visibility has been changed from protected to private ' ,
217
+ 'Property visibility has been changed from public to private ' ,
218
+ 'Property visibility has been changed from protected to private ' ,
219
+ ];
220
+
221
+ foreach ($ privateIndicators as $ indicator ) {
222
+ if (stripos ($ target , $ indicator ) !== false || stripos ($ reason , $ indicator ) !== false ) {
223
+ return true ;
224
+ }
225
+ }
226
+
227
+ // Check for visibility operations that involve private members
228
+ if (strpos ($ operationClass , 'Visibility ' ) !== false ) {
229
+ // For visibility operations, check if it involves changing from/to private
230
+ if (stripos ($ reason , 'private ' ) !== false ) {
231
+ return true ;
232
+ }
233
+ }
234
+
235
+ // Check for specific operation classes that handle private changes
236
+ $ privateOperationClasses = [
237
+ 'PrivateMethod ' ,
238
+ 'PrivateProperty ' ,
239
+ 'Private ' ,
240
+ ];
241
+
242
+ foreach ($ privateOperationClasses as $ privateClass ) {
243
+ if (stripos ($ operationClass , $ privateClass ) !== false ) {
244
+ return true ;
245
+ }
246
+ }
247
+
248
+ // Check if the target contains patterns that suggest private members
249
+ // Pattern: ClassName::privateMethodName or ClassName::$privateProperty
250
+ if (preg_match ('/::([a-z_][a-zA-Z0-9_]*|\$[a-z_][a-zA-Z0-9_]*)/ ' , $ target , $ matches )) {
251
+ $ memberName = $ matches [1 ];
252
+ // If member name starts with underscore (common private naming convention)
253
+ if (strpos ($ memberName , '_ ' ) === 0 ) {
254
+ return true ;
255
+ }
256
+ }
257
+
258
+ // Check for common private method patterns in the target
259
+ if (preg_match ('/::(_[a-zA-Z0-9_]+|[a-z][a-zA-Z0-9]*Private[a-zA-Z0-9]*)\(/ ' , $ target )) {
260
+ return true ;
261
+ }
262
+
263
+ return false ;
264
+ }
265
+
139
266
/**
140
267
* Generate the HTML header line for a report section
141
268
*
0 commit comments