2424use Composer \Installer ;
2525use Composer \Installer \InstallerEvent ;
2626use Composer \Installer \InstallerEvents ;
27- use Composer \Installer \NoopInstaller ;
2827use Composer \Installer \PackageEvent ;
2928use Composer \Installer \PackageEvents ;
3029use Composer \Installer \SuggestedPackagesReporter ;
@@ -71,6 +70,10 @@ class Flex implements PluginInterface, EventSubscriberInterface
7170 private $ options ;
7271 private $ configurator ;
7372 private $ downloader ;
73+
74+ /**
75+ * @var Installer
76+ */
7477 private $ installer ;
7578 private $ postInstallOutput = ['' ];
7679 private $ operations = [];
@@ -293,8 +296,7 @@ public function configureInstaller()
293296 $ backtrace = debug_backtrace ();
294297 foreach ($ backtrace as $ trace ) {
295298 if (isset ($ trace ['object ' ]) && $ trace ['object ' ] instanceof Installer) {
296- $ this ->installer = \Closure::bind (function () { return $ this ->update ? $ this : null ; }, $ trace ['object ' ], $ trace ['object ' ])();
297- $ trace ['object ' ]->setSuggestedPackagesReporter (new SuggestedPackagesReporter (new NullIO ()));
299+ $ this ->installer = $ trace ['object ' ]->setSuggestedPackagesReporter (new SuggestedPackagesReporter (new NullIO ()));
298300 }
299301
300302 if (isset ($ trace ['object ' ]) && $ trace ['object ' ] instanceof GlobalCommand) {
@@ -328,33 +330,21 @@ public function configureProject(Event $event)
328330 @unlink ('LICENSE ' );
329331
330332 // Update composer.json (project is proprietary by default)
331- $ json = new JsonFile ( Factory::getComposerFile () );
332- $ contents = file_get_contents ($ json -> getPath () );
333+ $ file = Factory::getComposerFile ();
334+ $ contents = file_get_contents ($ file );
333335 $ manipulator = new JsonManipulator ($ contents );
336+ $ json = JsonFile::parseJson ($ contents );
334337
335338 // new projects are most of the time proprietary
336339 $ manipulator ->addMainKey ('license ' , 'proprietary ' );
337340
338341 // extra.branch-alias doesn't apply to the project
339342 $ manipulator ->removeSubNode ('extra ' , 'branch-alias ' );
340343
341- // replace unbounded constraints for symfony/* packages by extra.symfony.require
342- $ config = json_decode ($ contents , true );
343- if ($ symfonyVersion = $ config ['extra ' ]['symfony ' ]['require ' ] ?? null ) {
344- $ versions = $ this ->downloader ->getVersions ();
345- foreach (['require ' , 'require-dev ' ] as $ type ) {
346- foreach ($ config [$ type ] ?? [] as $ package => $ version ) {
347- if ('* ' === $ version && isset ($ versions ['splits ' ][$ package ])) {
348- $ manipulator ->addLink ($ type , $ package , $ symfonyVersion );
349- }
350- }
351- }
352- }
353-
354344 // 'name' and 'description' are only required for public packages
355345 // don't use $manipulator->removeProperty() for BC with Composer 1.0
356346 $ contents = preg_replace (['{^\s*+"name":.*,$\n}m ' , '{^\s*+"description":.*,$\n}m ' ], '' , $ manipulator ->getContents (), 1 );
357- file_put_contents ($ json -> getPath () , $ contents );
347+ file_put_contents ($ file , $ contents );
358348
359349 $ this ->updateComposerLock ();
360350 }
@@ -366,106 +356,49 @@ public function record(PackageEvent $event)
366356 }
367357 }
368358
369- public function checkForUpdate (PackageEvent $ event )
370- {
371- if (null === $ this ->installer || $ this ->cacheDirPopulated || 'symfony/flex ' !== $ event ->getOperation ()->getPackage ()->getName ()) {
372- return ;
373- }
374-
375- $ this ->update ();
376- $ this ->cacheDirPopulated = true ;
377- $ this ->composer ->getInstallationManager ()->addInstaller (new NoopInstaller ());
378-
379- \Closure::bind (function () {
380- $ this ->io = new NullIO ();
381- $ this ->writeLock = false ;
382- $ this ->executeOperations = false ;
383- $ this ->dumpAutoloader = false ;
384- $ this ->runScripts = false ;
385- }, $ this ->installer , $ this ->installer )();
386- }
387-
388- public function update (Event $ event = null , $ operations = [])
359+ public function update (Event $ event , $ operations = [])
389360 {
390361 if ($ operations ) {
391362 $ this ->operations = $ operations ;
392363 }
393364
394365 $ this ->install ($ event );
395366
396- $ jsonPath = Factory::getComposerFile ();
397- $ json = file_get_contents ($ jsonPath );
398- $ manipulator = new JsonManipulator ($ json );
399- $ json = json_decode ($ json , true );
367+ $ file = Factory::getComposerFile ();
368+ $ contents = file_get_contents ($ file );
369+ $ json = JsonFile::parseJson ($ contents );
370+
371+ if (!isset ($ json ['flex-require ' ]) && !isset ($ json ['flex-require ' ])) {
372+ $ this ->unpack ($ event );
400373
401- if (null === $ event ) {
402- // called from checkForUpdate()
403- } elseif (null === $ this ->installer || (!isset ($ json ['flex-require ' ]) && !isset ($ json ['flex-require-dev ' ]))) {
404374 return ;
405- } else {
406- $ event ->stopPropagation ();
407375 }
408376
377+ // merge "flex-require" with "require"
378+ $ manipulator = new JsonManipulator ($ contents );
409379 $ sortPackages = $ this ->composer ->getConfig ()->get ('sort-packages ' );
410- $ unpackOp = new Operation ( true , $ sortPackages ) ;
411-
380+ $ symfonyVersion = $ json [ ' extra ' ][ ' symfony ' ][ ' require ' ] ?? null ;
381+ $ versions = $ symfonyVersion ? $ this -> downloader -> getVersions () : null ;
412382 foreach (['require ' , 'require-dev ' ] as $ type ) {
413383 if (isset ($ json ['flex- ' .$ type ])) {
414384 foreach ($ json ['flex- ' .$ type ] as $ package => $ constraint ) {
415- $ unpackOp ->addPackage ($ package , $ constraint , 'require-dev ' === $ type );
385+ if ($ symfonyVersion && '* ' === $ constraint && isset ($ versions ['splits ' ][$ package ])) {
386+ // replace unbounded constraints for symfony/* packages by extra.symfony.require
387+ $ constraint = $ symfonyVersion ;
388+ }
416389 $ manipulator ->addLink ($ type , $ package , $ constraint , $ sortPackages );
417390 }
418391
419392 $ manipulator ->removeMainKey ('flex- ' .$ type );
420393 }
421394 }
422395
423- file_put_contents ($ jsonPath , $ manipulator ->getContents ());
424-
425- $ this ->cacheDirPopulated = false ;
426- $ rm = $ this ->composer ->getRepositoryManager ();
427- $ package = Factory::create ($ this ->io )->getPackage ();
428- $ this ->composer ->setPackage ($ package );
429- \Closure::bind (function () use ($ package , $ rm ) {
430- $ this ->package = $ package ;
431- $ this ->repositoryManager = $ rm ;
432- }, $ this ->installer , $ this ->installer )();
433- $ this ->composer ->getEventDispatcher ()->__construct ($ this ->composer , $ this ->io );
434-
435- $ status = $ this ->installer ->run ();
436- if (0 !== $ status ) {
437- exit ($ status );
438- }
439-
440- $ unpacker = new Unpacker ($ this ->composer , new PackageResolver ($ this ->downloader ), $ this ->dryRun );
441- $ result = $ unpacker ->unpack ($ unpackOp );
442- $ unpacker ->updateLock ($ result , $ this ->io );
443-
444- $ io = new NullIO ();
445- $ composer = Factory::create ($ io , null , true );
446- $ installer = Installer::create ($ io , $ composer );
447- $ installer
448- ->setDevMode ($ this ->dryRun )
449- ->setDumpAutoloader (false )
450- ->setIgnorePlatformRequirements (true )
451- ->setUpdate (true )
452- ->setUpdateAllowList (['php ' ])
453- ;
454-
455- if (method_exists ($ composer ->getEventDispatcher (), 'setRunScripts ' )) {
456- $ composer ->getEventDispatcher ()->setRunScripts (false );
457- } else {
458- $ installer ->setRunScripts (false );
459- }
460-
461- if (method_exists ($ installer , 'setSkipSuggest ' )) {
462- $ installer ->setSkipSuggest (true );
463- }
396+ file_put_contents ($ file , $ manipulator ->getContents ());
464397
465- $ installer -> run ( );
398+ $ this -> reinstall ( $ event , true );
466399 }
467400
468- public function install (Event $ event = null )
401+ public function install (Event $ event )
469402 {
470403 $ rootDir = $ this ->options ->get ('root-dir ' );
471404
@@ -813,7 +746,7 @@ public function fetchRecipes(array $operations): array
813746 ];
814747 $ packRecipes = [];
815748
816- foreach ($ operations as $ i => $ operation ) {
749+ foreach ($ operations as $ operation ) {
817750 if ($ operation instanceof UpdateOperation) {
818751 $ package = $ operation ->getTargetPackage ();
819752 } else {
@@ -992,14 +925,73 @@ private function updateComposerLock()
992925 $ lockFile ->write ($ lockData );
993926 }
994927
928+ private function unpack (Event $ event )
929+ {
930+ $ jsonPath = Factory::getComposerFile ();
931+ $ json = JsonFile::parseJson (file_get_contents ($ jsonPath ));
932+ $ sortPackages = $ this ->composer ->getConfig ()->get ('sort-packages ' );
933+ $ unpackOp = new Operation (true , $ sortPackages );
934+
935+ foreach (['require ' , 'require-dev ' ] as $ type ) {
936+ foreach ($ json [$ type ] ?? [] as $ package => $ constraint ) {
937+ $ unpackOp ->addPackage ($ package , $ constraint , 'require-dev ' === $ type );
938+ }
939+ }
940+
941+ $ unpacker = new Unpacker ($ this ->composer , new PackageResolver ($ this ->downloader ), $ this ->dryRun );
942+ $ result = $ unpacker ->unpack ($ unpackOp );
943+
944+ if (!$ result ->getUnpacked ()) {
945+ return ;
946+ }
947+
948+ $ this ->io ->writeError ('<info>Unpacking Symfony packs</> ' );
949+ foreach ($ result ->getUnpacked () as $ pkg ) {
950+ $ this ->io ->writeError (sprintf (' - Unpacked <info>%s</> ' , $ pkg ->getName ()));
951+ }
952+
953+ $ unpacker ->updateLock ($ result , $ this ->io );
954+
955+ $ this ->reinstall ($ event , false );
956+ }
957+
958+ private function reinstall (Event $ event , bool $ update )
959+ {
960+ $ event ->stopPropagation ();
961+ $ composer = Factory::create ($ this ->io );
962+
963+ $ installer = clone $ this ->installer ;
964+ $ installer ->__construct (
965+ $ this ->io ,
966+ $ composer ->getConfig (),
967+ $ composer ->getPackage (),
968+ $ composer ->getDownloadManager (),
969+ $ composer ->getRepositoryManager (),
970+ $ composer ->getLocker (),
971+ $ composer ->getInstallationManager (),
972+ $ composer ->getEventDispatcher (),
973+ $ composer ->getAutoloadGenerator ()
974+ );
975+
976+ if (!$ update ) {
977+ $ installer ->setUpdateAllowList (['php ' ]);
978+ }
979+
980+ if (method_exists ($ installer , 'setSkipSuggest ' )) {
981+ $ installer ->setSkipSuggest (true );
982+ }
983+
984+ $ installer ->run ();
985+ }
986+
995987 public static function getSubscribedEvents (): array
996988 {
997989 if (!self ::$ activated ) {
998990 return [];
999991 }
1000992
1001993 $ events = [
1002- PackageEvents::POST_PACKAGE_INSTALL => __CLASS__ === self ::class ? [[ ' record ' ], [ ' checkForUpdate ' ]] : 'record ' ,
994+ PackageEvents::POST_PACKAGE_INSTALL => 'record ' ,
1003995 PackageEvents::POST_PACKAGE_UPDATE => [['record ' ], ['enableThanksReminder ' ]],
1004996 PackageEvents::POST_PACKAGE_UNINSTALL => 'record ' ,
1005997 ScriptEvents::POST_CREATE_PROJECT_CMD => 'configureProject ' ,
0 commit comments