@@ -40,6 +40,9 @@ class Resetter implements ResetterInterface
4040 /** @var array */
4141 private readonly array $ classList ;
4242
43+ /** @var array */
44+ private array $ sortedClassListsByClass = [];
45+
4346 /**
4447 * @param ComponentRegistrarInterface|null $componentRegistrar
4548 * @param array $classList
@@ -172,11 +175,67 @@ private function resetStateWithReflection(object $instance)
172175 $ instance ->{self ::RESET_STATE_METHOD }();
173176 return ;
174177 }
175- foreach ($ this ->classList as $ className => $ value ) {
176- if ($ instance instanceof $ className ) {
177- $ this ->resetStateWithReflectionByClassName ($ instance , $ className );
178+ $ className = get_class ($ instance );
179+ if (!array_key_exists ($ className , $ this ->sortedClassListsByClass )) {
180+ $ temporaryClassList = [];
181+ foreach ($ this ->classList as $ key => $ value ) {
182+ if ($ instance instanceof $ key ) {
183+ $ temporaryClassList [] = $ key ;
184+ }
178185 }
186+ $ this ->sortClasses ($ temporaryClassList );
187+ $ this ->sortedClassListsByClass [$ className ] = $ temporaryClassList ;
188+ }
189+ foreach ($ this ->sortedClassListsByClass [$ className ] as $ currentClassName ) {
190+ $ this ->resetStateWithReflectionByClassName ($ instance , $ currentClassName );
191+ }
192+ }
193+
194+ /**
195+ * Sorts an array of strings that are class names and interface names
196+ *
197+ * Note: This sorting algorithm only takes arrays that are keyed by contiguous numbers starting at zero.
198+ * Note: This sorting algorithm works with comparators that return false on unrelated items.
199+ *
200+ * @param array $array
201+ * @return void
202+ */
203+ private function sortClasses (array &$ array ) : void
204+ {
205+ $ i = 0 ;
206+ $ count = count ($ array );
207+ while ($ i + 1 < $ count ) {
208+ for ($ j = $ i + 1 ; $ j < $ count ; $ j ++) {
209+ if ($ this ->sortClassesComparitor ($ array [$ i ], $ array [$ j ])) {
210+ $ swapTemp = $ array [$ i ];
211+ $ array [$ i ] = $ array [$ j ];
212+ $ array [$ j ] = $ swapTemp ;
213+ continue 2 ;
214+ }
215+ }
216+ $ i ++;
217+ }
218+ }
219+
220+ /**
221+ * Comparator for class/interface sorter that returns true if $b should come before $a.
222+ *
223+ * @param string $a
224+ * @param string $b
225+ * @return bool
226+ */
227+ private function sortClassesComparitor (string $ a , string $ b ) : bool
228+ {
229+ if (is_a ($ a , $ b , true )) {
230+ return true ;
231+ }
232+ if (is_a ($ b , $ a , true )) {
233+ return false ;
234+ }
235+ if (interface_exists ($ a ) && class_exists ($ b )) {
236+ return true ; // Note: If they aren't related, classes should come before interfaces
179237 }
238+ return false ; // No relation
180239 }
181240
182241 /**
0 commit comments