default services conflit ?
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
|
||||
|
||||
/**
|
||||
* Provides default behaviors for operations.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractOperation implements OperationInterface {
|
||||
|
||||
/**
|
||||
* Cached contents of scaffold file to be written to disk.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $contents;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
final public function contents() {
|
||||
if (!isset($this->contents)) {
|
||||
$this->contents = $this->generateContents();
|
||||
}
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the scaffold contents or otherwise generate what is needed.
|
||||
*
|
||||
* @return string
|
||||
* The contents of the scaffold file.
|
||||
*/
|
||||
abstract protected function generateContents();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function scaffoldOverExistingTarget(OperationInterface $existing_target) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function scaffoldAtNewLocation(ScaffoldFilePath $destination) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
207
old.vendor/drupal/core-composer-scaffold/Operations/AppendOp.php
Normal file
207
old.vendor/drupal/core-composer-scaffold/Operations/AppendOp.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
|
||||
|
||||
/**
|
||||
* Scaffold operation to add to the beginning and/or end of a scaffold file.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class AppendOp extends AbstractOperation {
|
||||
|
||||
/**
|
||||
* Identifies Append operations.
|
||||
*/
|
||||
const ID = 'append';
|
||||
|
||||
/**
|
||||
* Path to the source file to prepend, if any.
|
||||
*
|
||||
* @var \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath
|
||||
*/
|
||||
protected $prepend;
|
||||
|
||||
/**
|
||||
* Path to the source file to append, if any.
|
||||
*
|
||||
* @var \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath
|
||||
*/
|
||||
protected $append;
|
||||
|
||||
/**
|
||||
* Path to the default data to use when appending to an empty file.
|
||||
*
|
||||
* @var \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath
|
||||
*/
|
||||
protected $default;
|
||||
|
||||
/**
|
||||
* An indicator of whether the file we are appending to is managed or not.
|
||||
*/
|
||||
protected $managed;
|
||||
|
||||
/**
|
||||
* An indicator of whether we are allowed to append to a non-scaffolded file.
|
||||
*/
|
||||
protected $forceAppend;
|
||||
|
||||
/**
|
||||
* The contents from the file that we are prepending / appending to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $originalContents;
|
||||
|
||||
/**
|
||||
* Constructs an AppendOp.
|
||||
*
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $prepend_path
|
||||
* The relative path to the prepend file.
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $append_path
|
||||
* The relative path to the append file.
|
||||
* @param bool $force_append
|
||||
* TRUE if is okay to append to a file that was not scaffolded.
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $default_path
|
||||
* The relative path to the default data.
|
||||
*/
|
||||
public function __construct(ScaffoldFilePath $prepend_path = NULL, ScaffoldFilePath $append_path = NULL, $force_append = FALSE, ScaffoldFilePath $default_path = NULL) {
|
||||
$this->forceAppend = $force_append;
|
||||
$this->prepend = $prepend_path;
|
||||
$this->append = $append_path;
|
||||
$this->default = $default_path;
|
||||
$this->managed = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function generateContents() {
|
||||
// Fetch the prepend contents, if provided.
|
||||
$prepend_contents = '';
|
||||
if (!empty($this->prepend)) {
|
||||
$prepend_contents = file_get_contents($this->prepend->fullPath()) . "\n";
|
||||
}
|
||||
// Fetch the append contents, if provided.
|
||||
$append_contents = '';
|
||||
if (!empty($this->append)) {
|
||||
$append_contents = "\n" . file_get_contents($this->append->fullPath());
|
||||
}
|
||||
|
||||
// Get the original contents, or the default data if the original is empty.
|
||||
$original_contents = $this->originalContents;
|
||||
if (empty($original_contents) && !empty($this->default)) {
|
||||
$original_contents = file_get_contents($this->default->fullPath());
|
||||
}
|
||||
|
||||
// Attach it all together.
|
||||
return $prepend_contents . $original_contents . $append_contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options) {
|
||||
$destination_path = $destination->fullPath();
|
||||
$interpolator = $destination->getInterpolator();
|
||||
|
||||
// Be extra-noisy of creating a new file or appending to a non-scaffold
|
||||
// file. Note that if the file already has the append contents, then the
|
||||
// OperationFactory will make a SkipOp instead, and we will not get here.
|
||||
if (!$this->managed) {
|
||||
$message = ' - <info>NOTICE</info> Modifying existing file at <info>[dest-rel-path]</info>.';
|
||||
if (!file_exists($destination_path)) {
|
||||
$message = ' - <info>NOTICE</info> Creating a new file at <info>[dest-rel-path]</info>.';
|
||||
}
|
||||
$message .= ' Examine the contents and ensure that it came out correctly.';
|
||||
$io->write($interpolator->interpolate($message));
|
||||
}
|
||||
|
||||
// Notify that we are prepending, if there is prepend data.
|
||||
if (!empty($this->prepend)) {
|
||||
$this->prepend->addInterpolationData($interpolator, 'prepend');
|
||||
$io->write($interpolator->interpolate(" - Prepend to <info>[dest-rel-path]</info> from <info>[prepend-rel-path]</info>"));
|
||||
}
|
||||
// Notify that we are appending, if there is append data.
|
||||
if (!empty($this->append)) {
|
||||
$this->append->addInterpolationData($interpolator, 'append');
|
||||
$io->write($interpolator->interpolate(" - Append to <info>[dest-rel-path]</info> from <info>[append-rel-path]</info>"));
|
||||
}
|
||||
|
||||
// Write the resulting data
|
||||
file_put_contents($destination_path, $this->contents());
|
||||
|
||||
// Return a ScaffoldResult with knowledge of whether this file is managed.
|
||||
return new ScaffoldResult($destination, $this->managed);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function scaffoldOverExistingTarget(OperationInterface $existing_target) {
|
||||
$this->originalContents = $existing_target->contents();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function scaffoldAtNewLocation(ScaffoldFilePath $destination) {
|
||||
// If there is no existing scaffold file at the target location, then any
|
||||
// append we do will be to an unmanaged file.
|
||||
$this->managed = FALSE;
|
||||
|
||||
// Default: do not allow an append over a file that was not scaffolded.
|
||||
if (!$this->forceAppend) {
|
||||
$message = " - Skip <info>[dest-rel-path]</info>: cannot append to a path that was not scaffolded unless 'force-append' property is set.";
|
||||
return new SkipOp($message);
|
||||
}
|
||||
|
||||
// If the target file does not exist, then we will allow the append to
|
||||
// happen if we have default data to provide for it.
|
||||
if (!file_exists($destination->fullPath())) {
|
||||
if (!empty($this->default)) {
|
||||
return $this;
|
||||
}
|
||||
$message = " - Skip <info>[dest-rel-path]</info>: no file exists at the target path, and no default data provided.";
|
||||
return new SkipOp($message);
|
||||
}
|
||||
|
||||
// If the target file DOES exist, and it already contains the append/prepend
|
||||
// data, then we will skip the operation.
|
||||
$existingData = file_get_contents($destination->fullPath());
|
||||
if ($this->existingFileHasData($existingData, $this->append) || $this->existingFileHasData($existingData, $this->prepend)) {
|
||||
$message = " - Skip <info>[dest-rel-path]</info>: the file already has the append/prepend data.";
|
||||
return new SkipOp($message);
|
||||
}
|
||||
|
||||
// Cache the original data to use during append.
|
||||
$this->originalContents = $existingData;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the append/prepend data has already been applied.
|
||||
*
|
||||
* @param string $contents
|
||||
* The contents of the target file.
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $data_path
|
||||
* The path to the data to append or prepend
|
||||
*
|
||||
* @return bool
|
||||
* 'TRUE' if the append/prepend data already exists in contents.
|
||||
*/
|
||||
protected function existingFileHasData($contents, $data_path) {
|
||||
if (empty($data_path)) {
|
||||
return FALSE;
|
||||
}
|
||||
$data = file_get_contents($data_path->fullPath());
|
||||
|
||||
return strpos($contents, $data) !== FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
/**
|
||||
* Holds parameter data for operation objects during operation creation only.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OperationData {
|
||||
|
||||
const MODE = 'mode';
|
||||
const PATH = 'path';
|
||||
const OVERWRITE = 'overwrite';
|
||||
const PREPEND = 'prepend';
|
||||
const APPEND = 'append';
|
||||
const DEFAULT = 'default';
|
||||
const FORCE_APPEND = 'force-append';
|
||||
|
||||
/**
|
||||
* The parameter data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* The destination path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $destination;
|
||||
|
||||
/**
|
||||
* OperationData constructor.
|
||||
*
|
||||
* @param string $destination
|
||||
* The destination path.
|
||||
* @param mixed $data
|
||||
* The raw data array to wrap.
|
||||
*/
|
||||
public function __construct($destination, $data) {
|
||||
$this->destination = $destination;
|
||||
$this->data = $this->normalizeScaffoldMetadata($destination, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the destination path that this operation data is associated with.
|
||||
*
|
||||
* @return string
|
||||
* The destination path for the scaffold result.
|
||||
*/
|
||||
public function destination() {
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets operation mode.
|
||||
*
|
||||
* @return string
|
||||
* Operation mode.
|
||||
*/
|
||||
public function mode() {
|
||||
return $this->data[self::MODE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if path exists.
|
||||
*
|
||||
* @return bool
|
||||
* Returns true if path exists
|
||||
*/
|
||||
public function hasPath() {
|
||||
return isset($this->data[self::PATH]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets path.
|
||||
*
|
||||
* @return string
|
||||
* The path.
|
||||
*/
|
||||
public function path() {
|
||||
return $this->data[self::PATH];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines overwrite.
|
||||
*
|
||||
* @return bool
|
||||
* Returns true if overwrite mode was selected.
|
||||
*/
|
||||
public function overwrite() {
|
||||
return !empty($this->data[self::OVERWRITE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether 'force-append' has been set.
|
||||
*
|
||||
* @return bool
|
||||
* Returns true if 'force-append' mode was selected.
|
||||
*/
|
||||
public function forceAppend() {
|
||||
if ($this->hasDefault()) {
|
||||
return TRUE;
|
||||
}
|
||||
return !empty($this->data[self::FORCE_APPEND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if prepend path exists.
|
||||
*
|
||||
* @return bool
|
||||
* Returns true if prepend exists.
|
||||
*/
|
||||
public function hasPrepend() {
|
||||
return isset($this->data[self::PREPEND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets prepend path.
|
||||
*
|
||||
* @return string
|
||||
* Path to prepend data
|
||||
*/
|
||||
public function prepend() {
|
||||
return $this->data[self::PREPEND];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if append path exists.
|
||||
*
|
||||
* @return bool
|
||||
* Returns true if prepend exists.
|
||||
*/
|
||||
public function hasAppend() {
|
||||
return isset($this->data[self::APPEND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets append path.
|
||||
*
|
||||
* @return string
|
||||
* Path to append data
|
||||
*/
|
||||
public function append() {
|
||||
return $this->data[self::APPEND];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if default path exists.
|
||||
*
|
||||
* @return bool
|
||||
* Returns true if there is default data available.
|
||||
*/
|
||||
public function hasDefault() {
|
||||
return isset($this->data[self::DEFAULT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets default path.
|
||||
*
|
||||
* @return string
|
||||
* Path to default data
|
||||
*/
|
||||
public function default() {
|
||||
return $this->data[self::DEFAULT];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes metadata by converting literal values into arrays.
|
||||
*
|
||||
* Conversions performed include:
|
||||
* - Boolean 'false' means "skip".
|
||||
* - A string means "replace", with the string value becoming the path.
|
||||
*
|
||||
* @param string $destination
|
||||
* The destination path for the scaffold file.
|
||||
* @param mixed $value
|
||||
* The metadata for this operation object, which varies by operation type.
|
||||
*
|
||||
* @return array
|
||||
* Normalized scaffold metadata with default values.
|
||||
*/
|
||||
protected function normalizeScaffoldMetadata($destination, $value) {
|
||||
$defaultScaffoldMetadata = [
|
||||
self::MODE => ReplaceOp::ID,
|
||||
self::PREPEND => NULL,
|
||||
self::APPEND => NULL,
|
||||
self::DEFAULT => NULL,
|
||||
self::OVERWRITE => TRUE,
|
||||
];
|
||||
|
||||
return $this->convertScaffoldMetadata($destination, $value) + $defaultScaffoldMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the conversion-to-array step in normalizeScaffoldMetadata.
|
||||
*
|
||||
* @param string $destination
|
||||
* The destination path for the scaffold file.
|
||||
* @param mixed $value
|
||||
* The metadata for this operation object, which varies by operation type.
|
||||
*
|
||||
* @return array
|
||||
* Normalized scaffold metadata.
|
||||
*/
|
||||
protected function convertScaffoldMetadata($destination, $value) {
|
||||
if (is_bool($value)) {
|
||||
if (!$value) {
|
||||
return [self::MODE => SkipOp::ID];
|
||||
}
|
||||
throw new \RuntimeException("File mapping {$destination} cannot be given the value 'true'.");
|
||||
}
|
||||
if (empty($value)) {
|
||||
throw new \RuntimeException("File mapping {$destination} cannot be empty.");
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$value = [self::PATH => $value];
|
||||
}
|
||||
// If there is no 'mode', but there is an 'append' or a 'prepend' path,
|
||||
// then the mode is 'append' (append + prepend).
|
||||
if (!isset($value[self::MODE]) && (isset($value[self::APPEND]) || isset($value[self::PREPEND]))) {
|
||||
$value[self::MODE] = AppendOp::ID;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
|
||||
|
||||
/**
|
||||
* Create Scaffold operation objects based on provided metadata.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OperationFactory {
|
||||
|
||||
/**
|
||||
* The Composer service.
|
||||
*
|
||||
* @var \Composer\Composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* OperationFactory constructor.
|
||||
*
|
||||
* @param \Composer\Composer $composer
|
||||
* Reference to the 'Composer' object, since the Scaffold Operation Factory
|
||||
* is also responsible for evaluating relative package paths as it creates
|
||||
* scaffold operations.
|
||||
*/
|
||||
public function __construct(Composer $composer) {
|
||||
$this->composer = $composer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scaffolding operation object as determined by the metadata.
|
||||
*
|
||||
* @param \Composer\Package\PackageInterface $package
|
||||
* The package that relative paths will be relative from.
|
||||
* @param OperationData $operation_data
|
||||
* The parameter data for this operation object; varies by operation type.
|
||||
*
|
||||
* @return \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface
|
||||
* The scaffolding operation object (skip, replace, etc.)
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* Exception thrown when parameter data does not identify a known scaffol
|
||||
* operation.
|
||||
*/
|
||||
public function create(PackageInterface $package, OperationData $operation_data) {
|
||||
switch ($operation_data->mode()) {
|
||||
case SkipOp::ID:
|
||||
return new SkipOp();
|
||||
|
||||
case ReplaceOp::ID:
|
||||
return $this->createReplaceOp($package, $operation_data);
|
||||
|
||||
case AppendOp::ID:
|
||||
return $this->createAppendOp($package, $operation_data);
|
||||
}
|
||||
throw new \RuntimeException("Unknown scaffold operation mode <comment>{$operation_data->mode()}</comment>.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a 'replace' scaffold op.
|
||||
*
|
||||
* Replace ops may copy or symlink, depending on settings.
|
||||
*
|
||||
* @param \Composer\Package\PackageInterface $package
|
||||
* The package that relative paths will be relative from.
|
||||
* @param OperationData $operation_data
|
||||
* The parameter data for this operation object, i.e. the relative 'path'.
|
||||
*
|
||||
* @return \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface
|
||||
* A scaffold replace operation object.
|
||||
*/
|
||||
protected function createReplaceOp(PackageInterface $package, OperationData $operation_data) {
|
||||
if (!$operation_data->hasPath()) {
|
||||
throw new \RuntimeException("'path' component required for 'replace' operations.");
|
||||
}
|
||||
$package_name = $package->getName();
|
||||
$package_path = $this->getPackagePath($package);
|
||||
$source = ScaffoldFilePath::sourcePath($package_name, $package_path, $operation_data->destination(), $operation_data->path());
|
||||
$op = new ReplaceOp($source, $operation_data->overwrite());
|
||||
return $op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an 'append' (or 'prepend') scaffold op.
|
||||
*
|
||||
* @param \Composer\Package\PackageInterface $package
|
||||
* The package that relative paths will be relative from.
|
||||
* @param OperationData $operation_data
|
||||
* The parameter data for this operation object, i.e. the relative 'path'.
|
||||
*
|
||||
* @return \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface
|
||||
* A scaffold replace operation object.
|
||||
*/
|
||||
protected function createAppendOp(PackageInterface $package, OperationData $operation_data) {
|
||||
$package_name = $package->getName();
|
||||
$package_path = $this->getPackagePath($package);
|
||||
$prepend_source_file = NULL;
|
||||
$append_source_file = NULL;
|
||||
$default_data_file = NULL;
|
||||
if ($operation_data->hasPrepend()) {
|
||||
$prepend_source_file = ScaffoldFilePath::sourcePath($package_name, $package_path, $operation_data->destination(), $operation_data->prepend());
|
||||
}
|
||||
if ($operation_data->hasAppend()) {
|
||||
$append_source_file = ScaffoldFilePath::sourcePath($package_name, $package_path, $operation_data->destination(), $operation_data->append());
|
||||
}
|
||||
if ($operation_data->hasDefault()) {
|
||||
$default_data_file = ScaffoldFilePath::sourcePath($package_name, $package_path, $operation_data->destination(), $operation_data->default());
|
||||
}
|
||||
if (!$this->hasContent($prepend_source_file) && !$this->hasContent($append_source_file)) {
|
||||
$message = ' - Keep <info>[dest-rel-path]</info> unchanged: no content to prepend / append was provided.';
|
||||
return new SkipOp($message);
|
||||
}
|
||||
|
||||
return new AppendOp($prepend_source_file, $append_source_file, $operation_data->forceAppend(), $default_data_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the specified scaffold file exists and has content.
|
||||
*
|
||||
* @param Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $file
|
||||
* Scaffold file to check.
|
||||
*
|
||||
* @return bool
|
||||
* True if the file exists and has content.
|
||||
*/
|
||||
protected function hasContent(ScaffoldFilePath $file = NULL) {
|
||||
if (!$file) {
|
||||
return FALSE;
|
||||
}
|
||||
$path = $file->fullPath();
|
||||
return is_file($path) && (filesize($path) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file path of a package.
|
||||
*
|
||||
* Note that if we call getInstallPath on the root package, we get the
|
||||
* wrong answer (the installation manager thinks our package is in
|
||||
* vendor). We therefore add special checking for this case.
|
||||
*
|
||||
* @param \Composer\Package\PackageInterface $package
|
||||
* The package.
|
||||
*
|
||||
* @return string
|
||||
* The file path.
|
||||
*/
|
||||
protected function getPackagePath(PackageInterface $package) {
|
||||
if ($package->getName() == $this->composer->getPackage()->getName()) {
|
||||
// This will respect the --working-dir option if Composer is invoked with
|
||||
// it. There is no API or method to determine the filesystem path of
|
||||
// a package's composer.json file.
|
||||
return getcwd();
|
||||
}
|
||||
return $this->composer->getInstallationManager()->getInstallPath($package);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
|
||||
|
||||
/**
|
||||
* Interface for scaffold operation objects.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface OperationInterface {
|
||||
|
||||
/**
|
||||
* Returns the exact data that will be written to the scaffold files.
|
||||
*
|
||||
* @return string
|
||||
* Data to be written to the scaffold location.
|
||||
*/
|
||||
public function contents();
|
||||
|
||||
/**
|
||||
* Process this scaffold operation.
|
||||
*
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $destination
|
||||
* Scaffold file's destination path.
|
||||
* @param \Composer\IO\IOInterface $io
|
||||
* IOInterface to write to.
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldOptions $options
|
||||
* Various options that may alter the behavior of the operation.
|
||||
*
|
||||
* @return \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldResult
|
||||
* Result of the scaffolding operation.
|
||||
*/
|
||||
public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options);
|
||||
|
||||
/**
|
||||
* Determines what to do if operation is used at same path as a previous op.
|
||||
*
|
||||
* Default behavior is to scaffold this operation at the specified
|
||||
* destination, ignoring whatever was there before.
|
||||
*
|
||||
* @param OperationInterface $existing_target
|
||||
* Existing file at the destination path that we should combine with.
|
||||
*
|
||||
* @return OperationInterface
|
||||
* The op to use at this destination.
|
||||
*/
|
||||
public function scaffoldOverExistingTarget(OperationInterface $existing_target);
|
||||
|
||||
/**
|
||||
* Determines what to do if operation is used without a previous operation.
|
||||
*
|
||||
* Default behavior is to scaffold this operation at the specified
|
||||
* destination. Most operations overwrite rather than modify existing files,
|
||||
* and therefore do not need to do anything special when there is no existing
|
||||
* file.
|
||||
*
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $destination
|
||||
* Scaffold file's destination path.
|
||||
*
|
||||
* @return OperationInterface
|
||||
* The op to use at this destination.
|
||||
*/
|
||||
public function scaffoldAtNewLocation(ScaffoldFilePath $destination);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
|
||||
|
||||
/**
|
||||
* Scaffold operation to copy or symlink from source to destination.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ReplaceOp extends AbstractOperation {
|
||||
|
||||
/**
|
||||
* Identifies Replace operations.
|
||||
*/
|
||||
const ID = 'replace';
|
||||
|
||||
/**
|
||||
* The relative path to the source file.
|
||||
*
|
||||
* @var \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath
|
||||
*/
|
||||
protected $source;
|
||||
|
||||
/**
|
||||
* Whether to overwrite existing files.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $overwrite;
|
||||
|
||||
/**
|
||||
* Constructs a ReplaceOp.
|
||||
*
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $sourcePath
|
||||
* The relative path to the source file.
|
||||
* @param bool $overwrite
|
||||
* Whether to allow this scaffold file to overwrite files already at
|
||||
* the destination. Defaults to TRUE.
|
||||
*/
|
||||
public function __construct(ScaffoldFilePath $sourcePath, $overwrite = TRUE) {
|
||||
$this->source = $sourcePath;
|
||||
$this->overwrite = $overwrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function generateContents() {
|
||||
return file_get_contents($this->source->fullPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options) {
|
||||
$fs = new Filesystem();
|
||||
$destination_path = $destination->fullPath();
|
||||
// Do nothing if overwrite is 'false' and a file already exists at the
|
||||
// destination.
|
||||
if ($this->overwrite === FALSE && file_exists($destination_path)) {
|
||||
$interpolator = $destination->getInterpolator();
|
||||
$io->write($interpolator->interpolate(" - Skip <info>[dest-rel-path]</info> because it already exists and overwrite is <comment>false</comment>."));
|
||||
return new ScaffoldResult($destination, FALSE);
|
||||
}
|
||||
|
||||
// Get rid of the destination if it exists, and make sure that
|
||||
// the directory where it's going to be placed exists.
|
||||
$fs->remove($destination_path);
|
||||
$fs->ensureDirectoryExists(dirname($destination_path));
|
||||
if ($options->symlink()) {
|
||||
return $this->symlinkScaffold($destination, $io);
|
||||
}
|
||||
return $this->copyScaffold($destination, $io);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the scaffold file.
|
||||
*
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $destination
|
||||
* Scaffold file to process.
|
||||
* @param \Composer\IO\IOInterface $io
|
||||
* IOInterface to writing to.
|
||||
*
|
||||
* @return \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldResult
|
||||
* The scaffold result.
|
||||
*/
|
||||
protected function copyScaffold(ScaffoldFilePath $destination, IOInterface $io) {
|
||||
$interpolator = $destination->getInterpolator();
|
||||
$this->source->addInterpolationData($interpolator);
|
||||
$success = file_put_contents($destination->fullPath(), $this->contents());
|
||||
if (!$success) {
|
||||
throw new \RuntimeException($interpolator->interpolate("Could not copy source file <info>[src-rel-path]</info> to <info>[dest-rel-path]</info>!"));
|
||||
}
|
||||
$io->write($interpolator->interpolate(" - Copy <info>[dest-rel-path]</info> from <info>[src-rel-path]</info>"));
|
||||
return new ScaffoldResult($destination, $this->overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Symlinks the scaffold file.
|
||||
*
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $destination
|
||||
* Scaffold file to process.
|
||||
* @param \Composer\IO\IOInterface $io
|
||||
* IOInterface to writing to.
|
||||
*
|
||||
* @return \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldResult
|
||||
* The scaffold result.
|
||||
*/
|
||||
protected function symlinkScaffold(ScaffoldFilePath $destination, IOInterface $io) {
|
||||
$interpolator = $destination->getInterpolator();
|
||||
try {
|
||||
$fs = new Filesystem();
|
||||
$fs->relativeSymlink($this->source->fullPath(), $destination->fullPath());
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
throw new \RuntimeException($interpolator->interpolate("Could not symlink source file <info>[src-rel-path]</info> to <info>[dest-rel-path]</info>!"), [], $e);
|
||||
}
|
||||
$io->write($interpolator->interpolate(" - Link <info>[dest-rel-path]</info> from <info>[src-rel-path]</info>"));
|
||||
return new ScaffoldResult($destination, $this->overwrite);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?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}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
|
||||
|
||||
/**
|
||||
* Record the result of a scaffold operation.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ScaffoldResult {
|
||||
|
||||
/**
|
||||
* The path to the scaffold file that was processed.
|
||||
*
|
||||
* @var \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath
|
||||
*/
|
||||
protected $destination;
|
||||
|
||||
/**
|
||||
* Indicates if this scaffold file is managed by the scaffold command.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $managed;
|
||||
|
||||
/**
|
||||
* ScaffoldResult constructor.
|
||||
*
|
||||
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $destination
|
||||
* The path to the scaffold file that was processed.
|
||||
* @param bool $isManaged
|
||||
* (optional) Whether this result is managed. Defaults to FALSE.
|
||||
*/
|
||||
public function __construct(ScaffoldFilePath $destination, $isManaged = FALSE) {
|
||||
$this->destination = $destination;
|
||||
$this->managed = $isManaged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this scaffold file is managed.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if this scaffold file is managed, FALSE if not.
|
||||
*/
|
||||
public function isManaged() {
|
||||
return $this->managed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the destination scaffold file that this result refers to.
|
||||
*
|
||||
* @return \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath
|
||||
* The destination path for the scaffold result.
|
||||
*/
|
||||
public function destination() {
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Composer\Plugin\Scaffold\Operations;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
|
||||
use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions;
|
||||
|
||||
/**
|
||||
* Scaffold operation to skip a scaffold file (do nothing).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SkipOp extends AbstractOperation {
|
||||
|
||||
/**
|
||||
* Identifies Skip operations.
|
||||
*/
|
||||
const ID = 'skip';
|
||||
|
||||
/**
|
||||
* The message to output while processing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* SkipOp constructor.
|
||||
*
|
||||
* @param string $message
|
||||
* (optional) A custom message to output while skipping.
|
||||
*/
|
||||
public function __construct($message = " - Skip <info>[dest-rel-path]</info>: disabled") {
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function generateContents() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options) {
|
||||
$interpolator = $destination->getInterpolator();
|
||||
$io->write($interpolator->interpolate($this->message));
|
||||
return new ScaffoldResult($destination, FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user