Skip to content

Commit e2c61c7

Browse files
committed
Issue #3103090 by greg.1.anderson, alexpott, jungle, ravi.shankar, dww, Mile23, larowlan: Avoid re-scaffolding unchanged files (and printing scaffold file information over and over)
1 parent bb6f05b commit e2c61c7

File tree

9 files changed

+205
-110
lines changed

9 files changed

+205
-110
lines changed

Handler.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,17 @@ public function scaffold() {
154154
$scaffold_options = $this->manageOptions->getOptions();
155155

156156
// Create a collection of scaffolded files to process. This determines which
157-
// take priority and which are conjoined.
157+
// take priority and which are combined.
158158
$scaffold_files = new ScaffoldFileCollection($file_mappings, $location_replacements);
159159

160+
// Get the scaffold files whose contents on disk match what we are about to
161+
// write. We can remove these from consideration, as rewriting would be a
162+
// no-op.
163+
$unchanged = $scaffold_files->checkUnchanged();
164+
$scaffold_files->filterFiles($unchanged);
165+
160166
// Process the list of scaffolded files.
161-
$scaffold_results = ScaffoldFileCollection::process($scaffold_files, $this->io, $scaffold_options);
167+
$scaffold_results = $scaffold_files->process($this->io, $scaffold_options);
162168

163169
// Generate an autoload file in the document root that includes the
164170
// autoload.php file in the vendor directory, wherever that is. Drupal
@@ -195,7 +201,7 @@ protected function getVendorPath() {
195201
* A multidimensional array of file mappings, as returned by
196202
* self::getAllowedPackages().
197203
*
198-
* @return \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface[]
204+
* @return \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface[][]
199205
* An array of destination paths => scaffold operation objects.
200206
*/
201207
protected function getFileMappingsFromPackages(array $allowed_packages) {

Operations/AbstractOperation.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,42 @@
1111
*/
1212
abstract class AbstractOperation implements OperationInterface {
1313

14+
/**
15+
* Cached contents of scaffold file to be written to disk.
16+
*
17+
* @var string
18+
*/
19+
protected $contents;
20+
21+
/**
22+
* {@inheritdoc}
23+
*/
24+
final public function contents() {
25+
if (!isset($this->contents)) {
26+
$this->contents = $this->generateContents();
27+
}
28+
return $this->contents;
29+
}
30+
31+
/**
32+
* Load the scaffold contents or otherwise generate what is needed.
33+
*
34+
* @return string
35+
* The contents of the scaffold file.
36+
*/
37+
abstract protected function generateContents();
38+
1439
/**
1540
* {@inheritdoc}
1641
*/
17-
public function combineWithConjunctionTarget(OperationInterface $conjunction_target) {
42+
public function scaffoldOverExistingTarget(OperationInterface $existing_target) {
1843
return $this;
1944
}
2045

2146
/**
2247
* {@inheritdoc}
2348
*/
24-
public function missingConjunctionTarget(ScaffoldFilePath $destination) {
49+
public function scaffoldAtNewLocation(ScaffoldFilePath $destination) {
2550
return $this;
2651
}
2752

Operations/AppendOp.php

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ class AppendOp extends AbstractOperation {
4949
*/
5050
protected $forceAppend;
5151

52+
/**
53+
* The contents from the file that we are prepending / appending to.
54+
*
55+
* @var string
56+
*/
57+
protected $originalContents;
58+
5259
/**
5360
* Constructs an AppendOp.
5461
*
@@ -69,16 +76,36 @@ public function __construct(ScaffoldFilePath $prepend_path = NULL, ScaffoldFileP
6976
$this->managed = TRUE;
7077
}
7178

79+
/**
80+
* {@inheritdoc}
81+
*/
82+
protected function generateContents() {
83+
// Fetch the prepend contents, if provided.
84+
$prepend_contents = '';
85+
if (!empty($this->prepend)) {
86+
$prepend_contents = file_get_contents($this->prepend->fullPath()) . "\n";
87+
}
88+
// Fetch the append contents, if provided.
89+
$append_contents = '';
90+
if (!empty($this->append)) {
91+
$append_contents = "\n" . file_get_contents($this->append->fullPath());
92+
}
93+
94+
// Get the original contents, or the default data if the original is empty.
95+
$original_contents = $this->originalContents;
96+
if (empty($original_contents) && !empty($this->default)) {
97+
$original_contents = file_get_contents($this->default->fullPath());
98+
}
99+
100+
// Attach it all together.
101+
return $prepend_contents . $original_contents . $append_contents;
102+
}
103+
72104
/**
73105
* {@inheritdoc}
74106
*/
75107
public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options) {
76108
$destination_path = $destination->fullPath();
77-
// This is just a sanity check; the OperationFactory has in theory already
78-
// accounted for this, and will return a SkipOp with a warning message.
79-
if (!file_exists($destination_path) && empty($this->default)) {
80-
throw new \RuntimeException($destination->getInterpolator()->interpolate("Cannot append/prepend because no prior package provided a scaffold file at [dest-rel-path]."));
81-
}
82109
$interpolator = $destination->getInterpolator();
83110

84111
// Be extra-noisy of creating a new file or appending to a non-scaffold
@@ -93,33 +120,20 @@ public function process(ScaffoldFilePath $destination, IOInterface $io, Scaffold
93120
$io->write($interpolator->interpolate($message));
94121
}
95122

96-
// Fetch the prepend contents, if provided.
97-
$prepend_contents = '';
123+
// Notify that we are prepending, if there is prepend data.
98124
if (!empty($this->prepend)) {
99125
$this->prepend->addInterpolationData($interpolator, 'prepend');
100-
$prepend_contents = file_get_contents($this->prepend->fullPath()) . "\n";
101126
$io->write($interpolator->interpolate(" - Prepend to <info>[dest-rel-path]</info> from <info>[prepend-rel-path]</info>"));
102127
}
103-
// Fetch the append contents, if provided.
128+
// Notify that we are appending, if there is append data.
104129
$append_contents = '';
105130
if (!empty($this->append)) {
106131
$this->append->addInterpolationData($interpolator, 'append');
107-
$append_contents = "\n" . file_get_contents($this->append->fullPath());
108132
$io->write($interpolator->interpolate(" - Append to <info>[dest-rel-path]</info> from <info>[append-rel-path]</info>"));
109133
}
110-
// We typically should always have content if we get here; the
111-
// OperationFactory should create a SkipOp instead of an AppendOp if there
112-
// is no append / prepend content. The edge case is if there is content
113-
// that is all 'trim'ed away. Then we get a message that we are appending,
114-
// although nothing will in fact actually happen.
115-
if (!empty(trim($prepend_contents)) || !empty(trim($append_contents))) {
116-
// None of our asset files are very large, so we will load each one into
117-
// memory for processing.
118-
$original_contents = file_get_contents(file_exists($destination_path) ? $destination_path : $this->default->fullPath());
119-
// Write the appended and prepended contents back to the file.
120-
$altered_contents = $prepend_contents . $original_contents . $append_contents;
121-
file_put_contents($destination_path, $altered_contents);
122-
}
134+
135+
// Write the resulting data
136+
file_put_contents($destination_path, $this->contents());
123137

124138
// Return a ScaffoldResult with knowledge of whether this file is managed.
125139
return new ScaffoldResult($destination, $this->managed);
@@ -128,16 +142,17 @@ public function process(ScaffoldFilePath $destination, IOInterface $io, Scaffold
128142
/**
129143
* {@inheritdoc}
130144
*/
131-
public function combineWithConjunctionTarget(OperationInterface $conjunction_target) {
132-
return new ConjunctionOp($conjunction_target, $this);
145+
public function scaffoldOverExistingTarget(OperationInterface $existing_target) {
146+
$this->originalContents = $existing_target->contents();
147+
return $this;
133148
}
134149

135150
/**
136151
* {@inheritdoc}
137152
*/
138-
public function missingConjunctionTarget(ScaffoldFilePath $destination) {
139-
// If there is no conjunction target (the destination is not scaffolded),
140-
// then any append we do will be to an unmanaged file.
153+
public function scaffoldAtNewLocation(ScaffoldFilePath $destination) {
154+
// If there is no existing scaffold file at the target location, then any
155+
// append we do will be to an unmanaged file.
141156
$this->managed = FALSE;
142157

143158
// Default: do not allow an append over a file that was not scaffolded.
@@ -164,6 +179,9 @@ public function missingConjunctionTarget(ScaffoldFilePath $destination) {
164179
return new SkipOp($message);
165180
}
166181

182+
// Cache the original data to use during append.
183+
$this->originalContents = $existingData;
184+
167185
return $this;
168186
}
169187

Operations/ConjunctionOp.php

Lines changed: 0 additions & 53 deletions
This file was deleted.

Operations/OperationInterface.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313
*/
1414
interface OperationInterface {
1515

16+
/**
17+
* Returns the exact data that will be written to the scaffold files.
18+
*
19+
* @return string
20+
* Data to be written to the scaffold location.
21+
*/
22+
public function contents();
23+
1624
/**
1725
* Process this scaffold operation.
1826
*
@@ -29,18 +37,18 @@ interface OperationInterface {
2937
public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options);
3038

3139
/**
32-
* Determines what to do if operation is used with a previous operation.
40+
* Determines what to do if operation is used at same path as a previous op.
3341
*
3442
* Default behavior is to scaffold this operation at the specified
3543
* destination, ignoring whatever was there before.
3644
*
37-
* @param OperationInterface $conjunction_target
45+
* @param OperationInterface $existing_target
3846
* Existing file at the destination path that we should combine with.
3947
*
4048
* @return OperationInterface
4149
* The op to use at this destination.
4250
*/
43-
public function combineWithConjunctionTarget(OperationInterface $conjunction_target);
51+
public function scaffoldOverExistingTarget(OperationInterface $existing_target);
4452

4553
/**
4654
* Determines what to do if operation is used without a previous operation.
@@ -50,9 +58,12 @@ public function combineWithConjunctionTarget(OperationInterface $conjunction_tar
5058
* and therefore do not need to do anything special when there is no existing
5159
* file.
5260
*
61+
* @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $destination
62+
* Scaffold file's destination path.
63+
*
5364
* @return OperationInterface
5465
* The op to use at this destination.
5566
*/
56-
public function missingConjunctionTarget(ScaffoldFilePath $destination);
67+
public function scaffoldAtNewLocation(ScaffoldFilePath $destination);
5768

5869
}

Operations/ReplaceOp.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ public function __construct(ScaffoldFilePath $sourcePath, $overwrite = TRUE) {
4747
$this->overwrite = $overwrite;
4848
}
4949

50+
/**
51+
* {@inheritdoc}
52+
*/
53+
protected function generateContents() {
54+
return file_get_contents($this->source->fullPath());
55+
}
56+
5057
/**
5158
* {@inheritdoc}
5259
*/
@@ -85,8 +92,7 @@ public function process(ScaffoldFilePath $destination, IOInterface $io, Scaffold
8592
protected function copyScaffold(ScaffoldFilePath $destination, IOInterface $io) {
8693
$interpolator = $destination->getInterpolator();
8794
$this->source->addInterpolationData($interpolator);
88-
$fs = new Filesystem();
89-
$success = $fs->copy($this->source->fullPath(), $destination->fullPath());
95+
$success = file_put_contents($destination->fullPath(), $this->contents());
9096
if (!$success) {
9197
throw new \RuntimeException($interpolator->interpolate("Could not copy source file <info>[src-rel-path]</info> to <info>[dest-rel-path]</info>!"));
9298
}

0 commit comments

Comments
 (0)