AppendOp.php

Same filename in other branches
  1. 9 composer/Plugin/Scaffold/Operations/AppendOp.php
  2. 8.9.x composer/Plugin/Scaffold/Operations/AppendOp.php
  3. 11.x composer/Plugin/Scaffold/Operations/AppendOp.php

Namespace

Drupal\Composer\Plugin\Scaffold\Operations

File

composer/Plugin/Scaffold/Operations/AppendOp.php

View source
<?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|null $prepend_path
     *   (optional) The relative path to the prepend file.
     * @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath|null $append_path
     *   (optional) The relative path to the append file.
     * @param bool $force_append
     *   (optional) TRUE if is okay to append to a file that was not scaffolded.
     * @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath|null $default_path
     *   (optional) 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 str_contains($contents, $data);
    }

}

Classes

Title Deprecated Summary
AppendOp Scaffold operation to add to the beginning and/or end of a scaffold file.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.