Newer
Older
<?php
namespace Drupal\Tests\package_manager\Traits;

Adam G-H
committed
use Drupal\Component\Utility\NestedArray;
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
/**
* A utility for all things fixtures.
*/
trait FixtureUtilityTrait {
/**
* Mirrors a fixture directory to the given path.
*
* Files not in the source fixture directory will not be deleted from
* destination directory. After copying the files to the destination directory
* the files and folders will be converted so that can be used in the tests.
* The conversion includes:
* - Renaming '_git' directories to '.git'
* - Renaming files ending in '.info.yml.hide' to remove '.hide'.
*
* @param string $source_path
* The source path.
* @param string $destination_path
* The path to which the fixture files should be mirrored.
*/
protected static function copyFixtureFilesTo(string $source_path, string $destination_path): void {
(new Filesystem())->mirror($source_path, $destination_path, NULL, [
'override' => TRUE,
'delete' => FALSE,
]);
static::renameInfoYmlFiles($destination_path);
static::renameGitDirectories($destination_path);
}
/**
* Renames all files that end with .info.yml.hide.
*
* @param string $dir
* The directory to be iterated through.
*/
protected static function renameInfoYmlFiles(string $dir) {
// Construct the iterator.
$it = new RecursiveDirectoryIterator($dir, \RecursiveIteratorIterator::SELF_FIRST);
// Loop through files and rename them.
foreach (new \RecursiveIteratorIterator($it) as $file) {
if ($file->getExtension() == 'hide') {
rename($file->getPathname(), $dir . DIRECTORY_SEPARATOR .
$file->getRelativePath() . DIRECTORY_SEPARATOR . str_replace(".hide", "", $file->getFilename()));
}
}
}
/**
* Renames _git directories to .git.
*
* @param string $dir
* The directory to be iterated through.
*/
private static function renameGitDirectories(string $dir) {
$iter = new \RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST,
\RecursiveIteratorIterator::CATCH_GET_CHILD
);
/** @var \Symfony\Component\Finder\SplFileInfo $file */
foreach ($iter as $file) {
if ($file->isDir() && $file->getFilename() === '_git' && $file->getRelativePathname()) {
rename(
$file->getPathname(),
$file->getPath() . DIRECTORY_SEPARATOR . '.git'
);
}
}
}

Adam G-H
committed
/**
* Adds a package.
*
* If $package contains an `install_path` key, it should be relative to the
* location of `installed.json` and `installed.php`, which are in
* `vendor/composer`. For example, if the package would be installed at
* `vendor/kirk/enterprise`, the install path should be `../kirk/enterprise`.
* If the package would be installed outside of vendor (for example, a Drupal
* module in the `modules` directory), it would be `../../modules/my_module`.
*

Adam G-H
committed
* @param string $dir
* The root Composer-managed directory (e.g., the project root or staging
* area).
* @param array $package
* The package info that should be added to installed.json and
* installed.php. Must include a `name` key.
*/
protected function addPackage(string $dir, array $package): void {
$this->assertArrayHasKey('name', $package);
$this->setPackage($dir, $package['name'], $package, FALSE);
}
/**
* Modifies a package's installed info.
*
* See ::addPackage() for information on how the `install_path` key is
* handled, if $package has it.
*

Adam G-H
committed
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
* @param string $dir
* The root Composer-managed directory (e.g., the project root or staging
* area).
* @param string $name
* The name of the package to modify.
* @param array $package
* The package info that should be updated in installed.json and
* installed.php.
*/
protected function modifyPackage(string $dir, string $name, array $package): void {
$this->setPackage($dir, $name, $package, TRUE);
}
/**
* Removes a package.
*
* @param string $dir
* The root Composer-managed directory (e.g., the project root or staging
* area).
* @param string $name
* The name of the package to remove.
*/
protected function removePackage(string $dir, string $name): void {
$this->setPackage($dir, $name, NULL, TRUE);
}
/**
* Changes a package's installation information in a particular directory.
*
* This function is internal and should not be called directly. Use
* ::addPackage(), ::modifyPackage(), and ::removePackage() instead.
*
* @param string $dir
* The root Composer-managed directory (e.g., the project root or staging
* area).
* @param string $name
* The name of the package to add, update, or remove.
* @param array|null $package
* The package information to be set in installed.json and installed.php, or
* NULL to remove it. Will be merged into the existing information if the
* package is already installed.
* @param bool $should_exist
* Whether or not the package is expected to already be installed.
*/
private function setPackage(string $dir, string $name, ?array $package, bool $should_exist): void {
$dir .= '/vendor/composer';
$file = $dir . '/installed.json';

Adam G-H
committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
$this->assertFileIsWritable($file);
$data = file_get_contents($file);
$data = json_decode($data, TRUE, 512, JSON_THROW_ON_ERROR);
// If the package is already installed, find its numerical index.
$position = NULL;
for ($i = 0; $i < count($data['packages']); $i++) {
if ($data['packages'][$i]['name'] === $name) {
$position = $i;
break;
}
}
// Ensure that we actually expect to find the package already installed (or
// not).
$message = $should_exist
? "Expected package '$name' to be installed, but it wasn't."
: "Expected package '$name' to not be installed, but it was.";
$this->assertSame($should_exist, isset($position), $message);
if (isset($position)) {
// If we're going to be updating the package data, merge the incoming data
// into what we already have.
if ($package) {
$package = NestedArray::mergeDeep($data['packages'][$position], $package);
}
// Remove the existing package; the array will be re-keyed by
// array_splice().
array_splice($data['packages'], $position, 1);
$data['dev-package-names'] = array_diff($data['dev-package-names'], [$name]);
$data['dev-package-names'] = array_values($data['dev-package-names']);
}
// Add the package back to the list, if we have data for it.
if ($package) {
// If an install path was provided, ensure it's relative.
if (array_key_exists('install_path', $package)) {
$this->assertStringStartsWith('../', $package['install_path']);
}

Adam G-H
committed
$package['name'] = $name;
$data['packages'][] = $package;
if (!empty($package['dev_requirement'])) {
$data['dev-package-names'][] = $name;
}
}
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$file = $dir . '/installed.php';

Adam G-H
committed
$this->assertFileIsWritable($file);
$data = require $file;
unset($data['versions'][$name]);
// The installation paths in $data will have been interpreted by the PHP
// runtime, so make them all relative again by stripping $dir out.
array_walk($data['versions'], function (array &$package) use ($dir): void {
if (array_key_exists('install_path', $package)) {
$package['install_path'] = str_replace("$dir/", '', $package['install_path']);
}
});

Adam G-H
committed
if ($package) {
$data['versions'][$name] = $package;
}
$data = var_export($data, TRUE);
$data = str_replace("'install_path' => '../", "'install_path' => __DIR__ . '/../", $data);

Adam G-H
committed
file_put_contents($file, "<?php\nreturn $data;");
}