@@ -89,21 +89,41 @@ public function resolveRootDeltas()
8989 $ target = $ this ->targetMageRootPackage ;
9090 $ user = $ this ->userRootPackage ;
9191
92- $ this ->resolveLinkSection ('require ' , $ original ->getRequires (), $ target ->getRequires (), $ user ->getRequires ());
92+ $ this ->resolveLinkSection (
93+ 'require ' ,
94+ $ original ->getRequires (),
95+ $ target ->getRequires (),
96+ $ user ->getRequires (),
97+ true
98+ );
9399 $ this ->resolveLinkSection (
94100 'require-dev ' ,
95101 $ original ->getDevRequires (),
96102 $ target ->getDevRequires (),
97- $ user ->getDevRequires ()
103+ $ user ->getDevRequires (),
104+ true
98105 );
99106 $ this ->resolveLinkSection (
100107 'conflict ' ,
101108 $ original ->getConflicts (),
102109 $ target ->getConflicts (),
103- $ user ->getConflicts ()
110+ $ user ->getConflicts (),
111+ false
112+ );
113+ $ this ->resolveLinkSection (
114+ 'provide ' ,
115+ $ original ->getProvides (),
116+ $ target ->getProvides (),
117+ $ user ->getProvides (),
118+ false
119+ );
120+ $ this ->resolveLinkSection (
121+ 'replace ' ,
122+ $ original ->getReplaces (),
123+ $ target ->getReplaces (),
124+ $ user ->getReplaces (),
125+ false
104126 );
105- $ this ->resolveLinkSection ('provide ' , $ original ->getProvides (), $ target ->getProvides (), $ user ->getProvides ());
106- $ this ->resolveLinkSection ('replace ' , $ original ->getReplaces (), $ target ->getReplaces (), $ user ->getReplaces ());
107127
108128 $ this ->resolveArraySection ('autoload ' , $ original ->getAutoload (), $ target ->getAutoload (), $ user ->getAutoload ());
109129 $ this ->resolveArraySection (
@@ -215,53 +235,26 @@ public function findResolution(
215235 * @param Link[] $originalMageLinks
216236 * @param Link[] $targetMageLinks
217237 * @param Link[] $userLinks
238+ * @param bool $verifyOrder
218239 * @return array
219240 */
220- public function resolveLinkSection ($ section , $ originalMageLinks , $ targetMageLinks , $ userLinks )
241+ public function resolveLinkSection ($ section , $ originalMageLinks , $ targetMageLinks , $ userLinks, $ verifyOrder )
221242 {
222- /** @var Link[] $originalLinkMap */
223- $ originalLinkMap = static ::linksToMap ($ originalMageLinks );
224-
225- /** @var Link[] $targetLinkMap */
226- $ targetLinkMap = static ::linksToMap ($ targetMageLinks );
227-
228- /** @var Link[] $userLinkMap */
229- $ userLinkMap = static ::linksToMap ($ userLinks );
230-
231243 $ adds = [];
232244 $ removes = [];
233245 $ changes = [];
234- $ magePackages = array_unique (array_merge (array_keys ($ originalLinkMap ), array_keys ($ targetLinkMap )));
246+ $ magePackages = array_unique (array_merge (array_keys ($ originalMageLinks ), array_keys ($ targetMageLinks )));
235247 foreach ($ magePackages as $ pkg ) {
236248 if ($ section === 'require ' && PackageUtils::getMagentoProductEdition ($ pkg )) {
237249 continue ;
238250 }
239- $ field = "$ section: $ pkg " ;
240- $ originalConstraint = key_exists ($ pkg , $ originalLinkMap ) ? $ originalLinkMap [$ pkg ]->getConstraint () : null ;
241- $ originalMageVal = ($ originalConstraint === null ) ? null : $ originalConstraint ->__toString ();
242- $ prettyOriginalMageVal = ($ originalConstraint === null ) ? null : $ originalConstraint ->getPrettyString ();
243- $ targetConstraint = key_exists ($ pkg , $ targetLinkMap ) ? $ targetLinkMap [$ pkg ]->getConstraint () : null ;
244- $ targetMageVal = ($ targetConstraint === null ) ? null : $ targetConstraint ->__toString ();
245- $ prettyTargetMageVal = ($ targetConstraint === null ) ? null : $ targetConstraint ->getPrettyString ();
246- $ userConstraint = key_exists ($ pkg , $ userLinkMap ) ? $ userLinkMap [$ pkg ]->getConstraint () : null ;
247- $ userVal = ($ userConstraint === null ) ? null : $ userConstraint ->__toString ();
248- $ prettyUserVal = ($ userConstraint === null ) ? null : $ userConstraint ->getPrettyString ();
249-
250- $ action = $ this ->findResolution (
251- $ field ,
252- $ originalMageVal ,
253- $ targetMageVal ,
254- $ userVal ,
255- $ prettyOriginalMageVal ,
256- $ prettyTargetMageVal ,
257- $ prettyUserVal
258- );
251+ $ action = $ this ->findLinkResolution ($ section , $ pkg , $ originalMageLinks , $ targetMageLinks , $ userLinks );
259252 if ($ action == static ::ADD_VAL ) {
260- $ adds [$ pkg ] = $ targetLinkMap [$ pkg ];
253+ $ adds [$ pkg ] = $ targetMageLinks [$ pkg ];
261254 } elseif ($ action == static ::REMOVE_VAL ) {
262255 $ removes [] = $ pkg ;
263256 } elseif ($ action == static ::CHANGE_VAL ) {
264- $ changes [$ pkg ] = $ targetLinkMap [$ pkg ];
257+ $ changes [$ pkg ] = $ targetMageLinks [$ pkg ];
265258 }
266259 }
267260
@@ -287,11 +280,26 @@ public function resolveLinkSection($section, $originalMageLinks, $targetMageLink
287280 $ this ->console ->labeledVerbose ("Updating $ section constraints: " . implode (', ' , $ prettyChanges ));
288281 }
289282
283+ $ enforcedOrder = [];
284+ if ($ verifyOrder ) {
285+ $ enforcedOrder = $ this ->getLinkOrderOverride (
286+ $ section ,
287+ array_keys ($ originalMageLinks ),
288+ array_keys ($ targetMageLinks ),
289+ array_keys ($ userLinks )
290+ );
291+ if ($ enforcedOrder !== []) {
292+ $ changed = true ;
293+ $ prettyOrder = " [ \n " . implode (", \n " , $ enforcedOrder ) . "\n ] " ;
294+ $ this ->console ->labeledVerbose ("Updating $ section order: \n$ prettyOrder " );
295+ }
296+ }
297+
290298 if ($ changed ) {
291299 $ replacements = array_values ($ adds );
292300
293301 /** @var Link $userLink */
294- foreach ($ userLinkMap as $ pkg => $ userLink ) {
302+ foreach ($ userLinks as $ pkg => $ userLink ) {
295303 if (in_array ($ pkg , $ removes )) {
296304 continue ;
297305 } elseif (key_exists ($ pkg , $ changes )) {
@@ -301,6 +309,12 @@ public function resolveLinkSection($section, $originalMageLinks, $targetMageLink
301309 }
302310 }
303311
312+ usort ($ replacements , $ this ->buildLinkOrderComparator (
313+ $ enforcedOrder ,
314+ array_keys ($ targetMageLinks ),
315+ array_keys ($ userLinks )
316+ ));
317+
304318 $ newJson = [];
305319 /** @var Link $link */
306320 foreach ($ replacements as $ link ) {
@@ -349,37 +363,15 @@ public function resolveNestedArray($field, $originalMageVal, $targetMageVal, $us
349363 $ result = $ userVal === null ? [] : $ userVal ;
350364
351365 if (is_array ($ originalMageVal ) && is_array ($ targetMageVal ) && is_array ($ userVal )) {
352- $ originalMageAssociativePart = [];
353- $ originalMageFlatPart = [];
354- foreach ($ originalMageVal as $ key => $ value ) {
355- if (is_string ($ key )) {
356- $ originalMageAssociativePart [$ key ] = $ value ;
357- } else {
358- $ originalMageFlatPart [] = $ value ;
359- }
360- }
366+ $ originalMageAssociativePart = array_filter ($ originalMageVal , 'is_string ' , ARRAY_FILTER_USE_KEY );
367+ $ originalMageFlatPart = array_filter ($ originalMageVal , 'is_int ' , ARRAY_FILTER_USE_KEY );
361368
362- $ targetMageAssociativePart = [];
363- $ targetMageFlatPart = [];
364- foreach ($ targetMageVal as $ key => $ value ) {
365- if (is_string ($ key )) {
366- $ targetMageAssociativePart [$ key ] = $ value ;
367- } else {
368- $ targetMageFlatPart [] = $ value ;
369- }
370- }
369+ $ targetMageAssociativePart = array_filter ($ targetMageVal , 'is_string ' , ARRAY_FILTER_USE_KEY );
370+ $ targetMageFlatPart = array_filter ($ targetMageVal , 'is_int ' , ARRAY_FILTER_USE_KEY );
371371
372- $ userAssociativePart = [];
373- $ userFlatPart = [];
374- foreach ($ userVal as $ key => $ value ) {
375- if (is_string ($ key )) {
376- $ userAssociativePart [$ key ] = $ value ;
377- } else {
378- $ userFlatPart [] = $ value ;
379- }
380- }
372+ $ userAssociativePart = array_filter ($ userVal , 'is_string ' , ARRAY_FILTER_USE_KEY );
381373
382- $ associativeResult = array_filter ( $ result , ' is_string ' , ARRAY_FILTER_USE_KEY ) ;
374+ $ associativeResult = $ userAssociativePart ;
383375 $ mageKeys = array_unique (
384376 array_merge (array_keys ($ originalMageAssociativePart ), array_keys ($ targetMageAssociativePart ))
385377 );
@@ -417,7 +409,7 @@ public function resolveNestedArray($field, $originalMageVal, $targetMageVal, $us
417409 }
418410 }
419411
420- $ flatResult = array_filter ($ result , 'is_int ' , ARRAY_FILTER_USE_KEY );
412+ $ flatResult = array_filter ($ userVal , 'is_int ' , ARRAY_FILTER_USE_KEY );
421413 $ flatAdds = array_diff (array_diff ($ targetMageFlatPart , $ originalMageFlatPart ), $ flatResult );
422414 if ($ flatAdds !== []) {
423415 $ valChanged = true ;
@@ -456,17 +448,132 @@ public function resolveNestedArray($field, $originalMageVal, $targetMageVal, $us
456448 }
457449
458450 /**
459- * Helper function to convert a set of links to an associative array with target package names as keys
451+ * Helper function to find the resolution action for a package constraint in the Link sections
460452 *
461- * @param Link[] $links
462- * @return array
453+ * @param string $section
454+ * @param string $pkg
455+ * @param Link[] $originalLinkMap
456+ * @param Link[] $targetLinkMap
457+ * @param Link[] $userLinkMap
458+ * @return string|null ADD_VAL|REMOVE_VAL|CHANGE_VAL to adjust the link constraint, null for no change
463459 */
464- protected function linksToMap ( $ links )
460+ protected function findLinkResolution ( $ section , $ pkg , $ originalLinkMap , $ targetLinkMap , $ userLinkMap )
465461 {
466- $ targets = array_map (function ($ link ) {
467- /** @var Link $link */
468- return $ link ->getTarget ();
469- }, $ links );
470- return array_combine ($ targets , $ links );
462+ $ field = "$ section: $ pkg " ;
463+ $ originalConstraint = key_exists ($ pkg , $ originalLinkMap ) ? $ originalLinkMap [$ pkg ]->getConstraint () : null ;
464+ $ originalMageVal = ($ originalConstraint === null ) ? null : $ originalConstraint ->__toString ();
465+ $ prettyOriginalMageVal = ($ originalConstraint === null ) ? null : $ originalConstraint ->getPrettyString ();
466+ $ targetConstraint = key_exists ($ pkg , $ targetLinkMap ) ? $ targetLinkMap [$ pkg ]->getConstraint () : null ;
467+ $ targetMageVal = ($ targetConstraint === null ) ? null : $ targetConstraint ->__toString ();
468+ $ prettyTargetMageVal = ($ targetConstraint === null ) ? null : $ targetConstraint ->getPrettyString ();
469+ $ userConstraint = key_exists ($ pkg , $ userLinkMap ) ? $ userLinkMap [$ pkg ]->getConstraint () : null ;
470+ $ userVal = ($ userConstraint === null ) ? null : $ userConstraint ->__toString ();
471+ $ prettyUserVal = ($ userConstraint === null ) ? null : $ userConstraint ->getPrettyString ();
472+
473+ return $ this ->findResolution (
474+ $ field ,
475+ $ originalMageVal ,
476+ $ targetMageVal ,
477+ $ userVal ,
478+ $ prettyOriginalMageVal ,
479+ $ prettyTargetMageVal ,
480+ $ prettyUserVal
481+ );
482+ }
483+
484+ /**
485+ * Get the order to use for a link section if local and target versions disagree
486+ *
487+ * @param string $section
488+ * @param string[] $originalMageOrder
489+ * @param string[] $targetMageOrder
490+ * @param string[] $userOrder
491+ * @return string[]
492+ */
493+ protected function getLinkOrderOverride ($ section , $ originalMageOrder , $ targetMageOrder , $ userOrder )
494+ {
495+ $ overrideOrder = [];
496+
497+ $ conflictTargetOrder = array_values (array_intersect ($ targetMageOrder , $ userOrder ));
498+ $ conflictUserOrder = array_values (array_intersect ($ userOrder , $ targetMageOrder ));
499+
500+ // Check if the user's link order does not match the target section for links that appear in both
501+ if ($ conflictTargetOrder != $ conflictUserOrder ) {
502+ $ conflictOriginalOrder = array_values (array_intersect ($ originalMageOrder , $ targetMageOrder ));
503+
504+ // Check if the user's order is different than the target order because the order has changed between
505+ // the original and target Magento versions
506+ if ($ conflictOriginalOrder !== $ conflictUserOrder ) {
507+ $ targetLabel = $ this ->retriever ->getTargetLabel ();
508+ $ userOrderDesc = " [ \n " . implode (", \n " , $ conflictUserOrder ) . "\n ] " ;
509+ $ targetOrderDesc = " [ \n " . implode (", \n " , $ conflictTargetOrder ) . "\n ] " ;
510+ $ conflictDesc = "$ targetLabel is trying to change the existing order of the $ section section. \n" .
511+ "Local order: \n$ userOrderDesc \n$ targetLabel order: \n$ targetOrderDesc " ;
512+ $ shouldOverride = $ this ->overrideUserValues ;
513+ if ($ this ->overrideUserValues ) {
514+ $ this ->console ->log ($ conflictDesc );
515+ $ this ->console ->log (
516+ 'Overriding local order due to -- ' . MageRootRequireCommand::OVERRIDE_OPT . '. '
517+ );
518+ } else {
519+ $ shouldOverride = $ this ->console ->ask (
520+ "$ conflictDesc \nWould you like to override the local order? "
521+ );
522+ }
523+
524+ if (!$ shouldOverride ) {
525+ $ this ->console ->comment ("$ conflictDesc but it will not be changed. Re-run using " .
526+ '-- ' . MageRootRequireCommand::OVERRIDE_OPT . ' or ' .
527+ '-- ' . MageRootRequireCommand::INTERACTIVE_OPT . ' to override with the Magento order. ' );
528+ $ overrideOrder = $ conflictUserOrder ;
529+ } else {
530+ $ overrideOrder = $ conflictTargetOrder ;
531+ }
532+ } else {
533+ $ overrideOrder = $ conflictTargetOrder ;
534+ }
535+ }
536+
537+ return $ overrideOrder ;
538+ }
539+
540+ /**
541+ * Construct a comparison function to use in sorting an array of links by prioritized order lists
542+ *
543+ * @param string[] $overrideOrder
544+ * @param string[] $targetMageOrder
545+ * @param string[] $userOrder
546+ * @return \Closure
547+ */
548+ protected function buildLinkOrderComparator ($ overrideOrder , $ targetMageOrder , $ userOrder )
549+ {
550+ $ prioritizedOrderings = [$ overrideOrder , $ targetMageOrder , $ userOrder ];
551+
552+ return function ($ link1 , $ link2 ) use ($ prioritizedOrderings ) {
553+ /**
554+ * @var Link $link1
555+ * @var Link $link2
556+ */
557+ $ package1 = $ link1 ->getTarget ();
558+ $ package2 = $ link2 ->getTarget ();
559+
560+ // Check each ordering array to see if it contains both links and if so use their positions to sort
561+ // If the ordering array does not contain both links, try the next one
562+ foreach ($ prioritizedOrderings as $ sortOrder ) {
563+ $ index1 = array_search ($ package1 , $ sortOrder );
564+ $ index2 = array_search ($ package2 , $ sortOrder );
565+ if ($ index1 !== false && $ index2 !== false ) {
566+ if ($ index1 == $ index2 ) {
567+ return 0 ;
568+ } else {
569+ return $ index1 < $ index2 ? -1 : 1 ;
570+ }
571+ }
572+ }
573+
574+ // None of the ordering arrays contain both elements, so their relative positions in the sorted array
575+ // do not matter
576+ return 0 ;
577+ };
471578 }
472579}
0 commit comments