199 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			199 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Drupal\Composer\Plugin\Scaffold\Operations;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Composer\IO\IOInterface;
							 | 
						||
| 
								 | 
							
								use Drupal\Composer\Plugin\Scaffold\Interpolator;
							 | 
						||
| 
								 | 
							
								use Drupal\Composer\Plugin\Scaffold\ScaffoldFileInfo;
							 | 
						||
| 
								 | 
							
								use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
							 | 
						||
| 
								 | 
							
								use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Collection of scaffold files.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @internal
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class ScaffoldFileCollection implements \IteratorAggregate {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Nested list of all scaffold files.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * The top level array maps from the package name to the collection of
							 | 
						||
| 
								 | 
							
								   * scaffold files provided by that package. Each collection of scaffold files
							 | 
						||
| 
								 | 
							
								   * is keyed by destination path.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @var \Drupal\Composer\Plugin\Scaffold\ScaffoldFileInfo[][]
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  protected $scaffoldFilesByProject = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * ScaffoldFileCollection constructor.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface[][] $file_mappings
							 | 
						||
| 
								 | 
							
								   *   A multidimensional array of file mappings.
							 | 
						||
| 
								 | 
							
								   * @param \Drupal\Composer\Plugin\Scaffold\Interpolator $location_replacements
							 | 
						||
| 
								 | 
							
								   *   An object with the location mappings (e.g. [web-root]).
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public function __construct(array $file_mappings, Interpolator $location_replacements) {
							 | 
						||
| 
								 | 
							
								    // Collection of all destination paths to be scaffolded. Used to determine
							 | 
						||
| 
								 | 
							
								    // when two projects scaffold the same file and we have to either replace or
							 | 
						||
| 
								 | 
							
								    // combine them together.
							 | 
						||
| 
								 | 
							
								    // @see OperationInterface::scaffoldOverExistingTarget().
							 | 
						||
| 
								 | 
							
								    $scaffoldFiles = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Build the list of ScaffoldFileInfo objects by project.
							 | 
						||
| 
								 | 
							
								    foreach ($file_mappings as $package_name => $package_file_mappings) {
							 | 
						||
| 
								 | 
							
								      foreach ($package_file_mappings as $destination_rel_path => $op) {
							 | 
						||
| 
								 | 
							
								        $destination = ScaffoldFilePath::destinationPath($package_name, $destination_rel_path, $location_replacements);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // If there was already a scaffolding operation happening at this path,
							 | 
						||
| 
								 | 
							
								        // allow the new operation to decide how to handle the override.
							 | 
						||
| 
								 | 
							
								        // Usually, the new operation will replace whatever was there before.
							 | 
						||
| 
								 | 
							
								        if (isset($scaffoldFiles[$destination_rel_path])) {
							 | 
						||
| 
								 | 
							
								          $previous_scaffold_file = $scaffoldFiles[$destination_rel_path];
							 | 
						||
| 
								 | 
							
								          $op = $op->scaffoldOverExistingTarget($previous_scaffold_file->op());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          // Remove the previous op so we only touch the destination once.
							 | 
						||
| 
								 | 
							
								          $message = "  - Skip <info>[dest-rel-path]</info>: overridden in <comment>{$package_name}</comment>";
							 | 
						||
| 
								 | 
							
								          $this->scaffoldFilesByProject[$previous_scaffold_file->packageName()][$destination_rel_path] = new ScaffoldFileInfo($destination, new SkipOp($message));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // If there is NOT already a scaffolding operation happening at this
							 | 
						||
| 
								 | 
							
								        // path, notify the scaffold operation of this fact.
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								          $op = $op->scaffoldAtNewLocation($destination);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Combine the scaffold operation with the destination and record it.
							 | 
						||
| 
								 | 
							
								        $scaffold_file = new ScaffoldFileInfo($destination, $op);
							 | 
						||
| 
								 | 
							
								        $scaffoldFiles[$destination_rel_path] = $scaffold_file;
							 | 
						||
| 
								 | 
							
								        $this->scaffoldFilesByProject[$package_name][$destination_rel_path] = $scaffold_file;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Removes any item that has a path matching any path in the provided list.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Matching is done via destination path.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param string[] $files_to_filter
							 | 
						||
| 
								 | 
							
								   *   List of destination paths.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public function filterFiles(array $files_to_filter) {
							 | 
						||
| 
								 | 
							
								    foreach ($this->scaffoldFilesByProject as $project_name => $scaffold_files) {
							 | 
						||
| 
								 | 
							
								      foreach ($scaffold_files as $destination_rel_path => $scaffold_file) {
							 | 
						||
| 
								 | 
							
								        if (in_array($destination_rel_path, $files_to_filter, TRUE)) {
							 | 
						||
| 
								 | 
							
								          unset($scaffold_files[$destination_rel_path]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      $this->scaffoldFilesByProject[$project_name] = $scaffold_files;
							 | 
						||
| 
								 | 
							
								      if (!$this->checkListHasItemWithContent($scaffold_files)) {
							 | 
						||
| 
								 | 
							
								        unset($this->scaffoldFilesByProject[$project_name]);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Scans through a list of scaffold files and determines if any has contents.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFileInfo[] $scaffold_files
							 | 
						||
| 
								 | 
							
								   *   List of scaffold files, path: ScaffoldFileInfo.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return bool
							 | 
						||
| 
								 | 
							
								   *   TRUE if at least one item in the list has content
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  protected function checkListHasItemWithContent(array $scaffold_files) {
							 | 
						||
| 
								 | 
							
								    foreach ($scaffold_files as $scaffold_file) {
							 | 
						||
| 
								 | 
							
								      $contents = $scaffold_file->op()->contents();
							 | 
						||
| 
								 | 
							
								      if (!empty($contents)) {
							 | 
						||
| 
								 | 
							
								        return TRUE;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public function getIterator(): \ArrayIterator {
							 | 
						||
| 
								 | 
							
								    return new \ArrayIterator($this->scaffoldFilesByProject);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Processes the files in our collection.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param \Composer\IO\IOInterface $io
							 | 
						||
| 
								 | 
							
								   *   The Composer IO object.
							 | 
						||
| 
								 | 
							
								   * @param \Drupal\Composer\Plugin\Scaffold\ScaffoldOptions $scaffold_options
							 | 
						||
| 
								 | 
							
								   *   The scaffold options.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldResult[]
							 | 
						||
| 
								 | 
							
								   *   The results array.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public function processScaffoldFiles(IOInterface $io, ScaffoldOptions $scaffold_options) {
							 | 
						||
| 
								 | 
							
								    $results = [];
							 | 
						||
| 
								 | 
							
								    foreach ($this as $project_name => $scaffold_files) {
							 | 
						||
| 
								 | 
							
								      $io->write("Scaffolding files for <comment>{$project_name}</comment>:");
							 | 
						||
| 
								 | 
							
								      foreach ($scaffold_files as $scaffold_file) {
							 | 
						||
| 
								 | 
							
								        $results[$scaffold_file->destination()->relativePath()] = $scaffold_file->process($io, $scaffold_options);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return $results;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Processes the iterator created by ScaffoldFileCollection::create().
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldFileCollection $collection
							 | 
						||
| 
								 | 
							
								   *   The iterator to process.
							 | 
						||
| 
								 | 
							
								   * @param \Composer\IO\IOInterface $io
							 | 
						||
| 
								 | 
							
								   *   The Composer IO object.
							 | 
						||
| 
								 | 
							
								   * @param \Drupal\Composer\Plugin\Scaffold\ScaffoldOptions $scaffold_options
							 | 
						||
| 
								 | 
							
								   *   The scaffold options.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldResult[]
							 | 
						||
| 
								 | 
							
								   *   The results array.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @deprecated. Called when upgrading from the Core Composer Scaffold plugin
							 | 
						||
| 
								 | 
							
								   *   version 8.8.x due to a bug in the plugin and handler classes. Do not use
							 | 
						||
| 
								 | 
							
								   *   in 8.9.x or 9.x, and remove in Drupal 10.x.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static function process(ScaffoldFileCollection $collection, IOInterface $io, ScaffoldOptions $scaffold_options) {
							 | 
						||
| 
								 | 
							
								    $results = [];
							 | 
						||
| 
								 | 
							
								    foreach ($collection as $project_name => $scaffold_files) {
							 | 
						||
| 
								 | 
							
								      $io->write("Scaffolding files for <comment>{$project_name}</comment>:");
							 | 
						||
| 
								 | 
							
								      foreach ($scaffold_files as $scaffold_file) {
							 | 
						||
| 
								 | 
							
								        $results[$scaffold_file->destination()->relativePath()] = $scaffold_file->process($io, $scaffold_options);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return $results;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Returns the list of files that have not changed since they were scaffolded.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Note that there are two reasons a file may have changed:
							 | 
						||
| 
								 | 
							
								   *   - The user modified it after it was scaffolded.
							 | 
						||
| 
								 | 
							
								   *   - The package the file came to was updated, and the file is different in
							 | 
						||
| 
								 | 
							
								   *     the new version.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * With the current scaffold code, we cannot tell the difference between the
							 | 
						||
| 
								 | 
							
								   * two. @see https://www.drupal.org/project/drupal/issues/3092563
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return string[]
							 | 
						||
| 
								 | 
							
								   *   List of relative paths to unchanged files on disk.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public function checkUnchanged() {
							 | 
						||
| 
								 | 
							
								    $results = [];
							 | 
						||
| 
								 | 
							
								    foreach ($this as $scaffold_files) {
							 | 
						||
| 
								 | 
							
								      foreach ($scaffold_files as $scaffold_file) {
							 | 
						||
| 
								 | 
							
								        if (!$scaffold_file->hasChanged()) {
							 | 
						||
| 
								 | 
							
								          $results[] = $scaffold_file->destination()->relativePath();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return $results;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 |