ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
cweagans\Composer\Patches Class Reference
+ Inheritance diagram for cweagans\Composer\Patches:
+ Collaboration diagram for cweagans\Composer\Patches:

Public Member Functions

 activate (Composer $composer, IOInterface $io)
 Apply plugin modifications to composer. More...
 
 checkPatches (Event $event)
 Before running composer install,. More...
 
 gatherPatches (PackageEvent $event)
 Gather patches from dependencies and store them for later use. More...
 
 grabPatches ()
 Get the patches from root composer or external file. More...
 

Static Public Member Functions

static getSubscribedEvents ()
 Returns an array of event names this subscriber wants to listen to. More...
 

Protected Member Functions

 getPackageFromOperation (OperationInterface $operation)
 Get a Package object from an OperationInterface object. More...
 
 getAndApplyPatch (RemoteFilesystem $downloader, $install_path, $patch_url)
 Apply a patch on code in the specified directory. More...
 
 isPatchingEnabled ()
 Checks if the root package enables patching. More...
 
 writePatchReport ($patches, $directory)
 Writes a patch report to the target directory. More...
 
 executeCommand ($cmd)
 Executes a shell command with escaping. More...
 

Protected Attributes

 $composer
 
 $io
 
 $eventDispatcher
 
 $executor
 
 $patches
 

Detailed Description

Definition at line 28 of file Patches.php.

Member Function Documentation

◆ activate()

cweagans\Composer\Patches::activate ( Composer  $composer,
IOInterface  $io 
)

Apply plugin modifications to composer.

Parameters
Composer$composer
IOInterface$io

Definition at line 57 of file Patches.php.

57 {
58 $this->composer = $composer;
59 $this->io = $io;
60 $this->eventDispatcher = $composer->getEventDispatcher();
61 $this->executor = new ProcessExecutor($this->io);
62 $this->patches = array();
63 }

References cweagans\Composer\Patches\$composer, and cweagans\Composer\Patches\$io.

◆ checkPatches()

cweagans\Composer\Patches::checkPatches ( Event  $event)

Before running composer install,.

Parameters
Event$event

Definition at line 83 of file Patches.php.

83 {
84 if (!$this->isPatchingEnabled()) {
85 return;
86 }
87
88 try {
89 $repositoryManager = $this->composer->getRepositoryManager();
90 $localRepository = $repositoryManager->getLocalRepository();
91 $installationManager = $this->composer->getInstallationManager();
92 $packages = $localRepository->getPackages();
93
94 $tmp_patches = $this->grabPatches();
95 if ($tmp_patches == FALSE) {
96 $this->io->write('<info>No patches supplied.</info>');
97 return;
98 }
99
100 foreach ($packages as $package) {
101 $extra = $package->getExtra();
102 $patches = isset($extra['patches']) ? $extra['patches'] : array();
103 $tmp_patches = array_merge_recursive($tmp_patches, $patches);
104 }
105
106 // Remove packages for which the patch set has changed.
107 foreach ($packages as $package) {
108 if (!($package instanceof AliasPackage)) {
109 $package_name = $package->getName();
110 $extra = $package->getExtra();
111 $has_patches = isset($tmp_patches[$package_name]);
112 $has_applied_patches = isset($extra['patches_applied']);
113 if (($has_patches && !$has_applied_patches)
114 || (!$has_patches && $has_applied_patches)
115 || ($has_patches && $has_applied_patches && $tmp_patches[$package_name] !== $extra['patches_applied'])) {
116 $uninstallOperation = new UninstallOperation($package, 'Removing package so it can be re-installed and re-patched.');
117 $this->io->write('<info>Removing package ' . $package_name . ' so that it can be re-installed and re-patched.</info>');
118 $installationManager->uninstall($localRepository, $uninstallOperation);
119 }
120 }
121 }
122 }
123 // If the Locker isn't available, then we don't need to do this.
124 // It's the first time packages have been installed.
125 catch (\LogicException $e) {
126 return;
127 }
128 }
grabPatches()
Get the patches from root composer or external file.
Definition: Patches.php:181
isPatchingEnabled()
Checks if the root package enables patching.
Definition: Patches.php:372

References cweagans\Composer\Patches\$patches, cweagans\Composer\Patches\grabPatches(), and cweagans\Composer\Patches\isPatchingEnabled().

+ Here is the call graph for this function:

◆ executeCommand()

cweagans\Composer\Patches::executeCommand (   $cmd)
protected

Executes a shell command with escaping.

Parameters
string$cmd
Returns
bool

Definition at line 407 of file Patches.php.

407 {
408 // Shell-escape all arguments except the command.
409 $args = func_get_args();
410 foreach ($args as $index => $arg) {
411 if ($index !== 0) {
412 $args[$index] = escapeshellarg($arg);
413 }
414 }
415
416 // And replace the arguments.
417 $command = call_user_func_array('sprintf', $args);
418 $output = '';
419 if ($this->io->isVerbose()) {
420 $this->io->write('<comment>' . $command . '</comment>');
421 $io = $this->io;
422 $output = function ($type, $data) use ($io) {
423 if ($type == Process::ERR) {
424 $io->write('<error>' . $data . '</error>');
425 }
426 else {
427 $io->write('<comment>' . $data . '</comment>');
428 }
429 };
430 }
431 return ($this->executor->execute($command, $output) == 0);
432 }
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\s+" &#(? foreach( $entity_files as $file) $output
$index
Definition: metadata.php:60
$type

References $data, $index, cweagans\Composer\Patches\$io, $output, and $type.

Referenced by cweagans\Composer\Patches\getAndApplyPatch().

+ Here is the caller graph for this function:

◆ gatherPatches()

cweagans\Composer\Patches::gatherPatches ( PackageEvent  $event)

Gather patches from dependencies and store them for later use.

Parameters
PackageEvent$event

Definition at line 135 of file Patches.php.

135 {
136 // If we've already done this, then don't do it again.
137 if (isset($this->patches['_patchesGathered'])) {
138 return;
139 }
140 // If patching has been disabled, bail out here.
141 elseif (!$this->isPatchingEnabled()) {
142 return;
143 }
144
145 $this->patches = $this->grabPatches();
146 if (empty($this->patches)) {
147 $this->io->write('<info>No patches supplied.</info>');
148 }
149
150 // Now add all the patches from dependencies that will be installed.
151 $operations = $event->getOperations();
152 $this->io->write('<info>Gathering patches for dependencies. This might take a minute.</info>');
153 foreach ($operations as $operation) {
154 if ($operation->getJobType() == 'install' || $operation->getJobType() == 'update') {
155 $package = $this->getPackageFromOperation($operation);
156 $extra = $package->getExtra();
157 if (isset($extra['patches'])) {
158 $this->patches = array_merge_recursive($this->patches, $extra['patches']);
159 }
160 }
161 }
162
163 // If we're in verbose mode, list the projects we're going to patch.
164 if ($this->io->isVerbose()) {
165 foreach ($this->patches as $package => $patches) {
166 $number = count($patches);
167 $this->io->write('<info>Found ' . $number . ' patches for ' . $package . '.</info>');
168 }
169 }
170
171 // Make sure we don't gather patches again. Extra keys in $this->patches
172 // won't hurt anything, so we'll just stash it there.
173 $this->patches['_patchesGathered'] = TRUE;
174 }
getPackageFromOperation(OperationInterface $operation)
Get a Package object from an OperationInterface object.
Definition: Patches.php:291

References cweagans\Composer\Patches\$patches, cweagans\Composer\Patches\getPackageFromOperation(), cweagans\Composer\Patches\grabPatches(), and cweagans\Composer\Patches\isPatchingEnabled().

+ Here is the call graph for this function:

◆ getAndApplyPatch()

cweagans\Composer\Patches::getAndApplyPatch ( RemoteFilesystem  $downloader,
  $install_path,
  $patch_url 
)
protected

Apply a patch on code in the specified directory.

Parameters
RemoteFilesystem$downloader
$install_path
$patch_url
Exceptions

Exception

Definition at line 313 of file Patches.php.

313 {
314
315 // Local patch file.
316 if (file_exists($patch_url)) {
317 $filename = $patch_url;
318 }
319 else {
320 // Generate random (but not cryptographically so) filename.
321 $filename = uniqid("/tmp/") . ".patch";
322
323 // Download file from remote filesystem to this location.
324 $hostname = parse_url($patch_url, PHP_URL_HOST);
325 $downloader->copy($hostname, $patch_url, $filename, FALSE);
326 }
327
328 // Modified from drush6:make.project.inc
329 $patched = FALSE;
330 // The order here is intentional. p1 is most likely to apply with git apply.
331 // p0 is next likely. p2 is extremely unlikely, but for some special cases,
332 // it might be useful.
333 $patch_levels = array('-p1', '-p0', '-p2');
334 foreach ($patch_levels as $patch_level) {
335 $checked = $this->executeCommand('cd %s && GIT_DIR=. git apply --check %s %s', $install_path, $patch_level, $filename);
336 if ($checked) {
337 // Apply the first successful style.
338 $patched = $this->executeCommand('cd %s && GIT_DIR=. git apply %s %s', $install_path, $patch_level, $filename);
339 break;
340 }
341 }
342
343 // In some rare cases, git will fail to apply a patch, fallback to using
344 // the 'patch' command.
345 if (!$patched) {
346 foreach ($patch_levels as $patch_level) {
347 // --no-backup-if-mismatch here is a hack that fixes some
348 // differences between how patch works on windows and unix.
349 if ($patched = $this->executeCommand("patch %s --no-backup-if-mismatch -d %s < %s", $patch_level, $install_path, $filename)) {
350 break;
351 }
352 }
353 }
354
355 // Clean up the temporary patch file.
356 if (isset($hostname)) {
357 unlink($filename);
358 }
359 // If the patch *still* isn't applied, then give up and throw an Exception.
360 // Otherwise, let the user know it worked.
361 if (!$patched) {
362 throw new \Exception("Cannot apply patch $patch_url");
363 }
364 }
executeCommand($cmd)
Executes a shell command with escaping.
Definition: Patches.php:407

References $filename, and cweagans\Composer\Patches\executeCommand().

+ Here is the call graph for this function:

◆ getPackageFromOperation()

cweagans\Composer\Patches::getPackageFromOperation ( OperationInterface  $operation)
protected

Get a Package object from an OperationInterface object.

Parameters
OperationInterface$operation
Returns
PackageInterface
Exceptions

Exception

Definition at line 291 of file Patches.php.

291 {
292 if ($operation instanceof InstallOperation) {
293 $package = $operation->getPackage();
294 }
295 elseif ($operation instanceof UpdateOperation) {
296 $package = $operation->getTargetPackage();
297 }
298 else {
299 throw new \Exception('Unknown operation: ' . get_class($operation));
300 }
301
302 return $package;
303 }

Referenced by cweagans\Composer\Patches\gatherPatches().

+ Here is the caller graph for this function:

◆ getSubscribedEvents()

static cweagans\Composer\Patches::getSubscribedEvents ( )
static

Returns an array of event names this subscriber wants to listen to.

Definition at line 68 of file Patches.php.

68 {
69 return array(
70 ScriptEvents::PRE_INSTALL_CMD => "checkPatches",
71 ScriptEvents::PRE_UPDATE_CMD => "checkPatches",
72 PackageEvents::PRE_PACKAGE_INSTALL => "gatherPatches",
73 PackageEvents::PRE_PACKAGE_UPDATE => "gatherPatches",
74 PackageEvents::POST_PACKAGE_INSTALL => "postInstall",
75 PackageEvents::POST_PACKAGE_UPDATE => "postInstall",
76 );
77 }

◆ grabPatches()

cweagans\Composer\Patches::grabPatches ( )

Get the patches from root composer or external file.

Returns
Patches
Exceptions

Exception

Definition at line 181 of file Patches.php.

181 {
182 // First, try to get the patches from the root composer.json.
183 $extra = $this->composer->getPackage()->getExtra();
184 if (isset($extra['patches'])) {
185 $this->io->write('<info>Gathering patches for root package.</info>');
186 $patches = $extra['patches'];
187 return $patches;
188 }
189 // If it's not specified there, look for a patches-file definition.
190 elseif (isset($extra['patches-file'])) {
191 $this->io->write('<info>Gathering patches from patch file.</info>');
192 $patches = file_get_contents($extra['patches-file']);
193 $patches = json_decode($patches, TRUE);
194 $error = json_last_error();
195 if ($error != 0) {
196 switch ($error) {
197 case JSON_ERROR_DEPTH:
198 $msg = ' - Maximum stack depth exceeded';
199 break;
200 case JSON_ERROR_STATE_MISMATCH:
201 $msg = ' - Underflow or the modes mismatch';
202 break;
203 case JSON_ERROR_CTRL_CHAR:
204 $msg = ' - Unexpected control character found';
205 break;
206 case JSON_ERROR_SYNTAX:
207 $msg = ' - Syntax error, malformed JSON';
208 break;
209 case JSON_ERROR_UTF8:
210 $msg = ' - Malformed UTF-8 characters, possibly incorrectly encoded';
211 break;
212 default:
213 $msg = ' - Unknown error';
214 break;
215 }
216 throw new \Exception('There was an error in the supplied patches file:' . $msg);
217 }
218 if (isset($patches['patches'])) {
219 $patches = $patches['patches'];
220 return $patches;
221 }
222 elseif(!$patches) {
223 throw new \Exception('There was an error in the supplied patch file');
224 }
225 }
226 else {
227 return array();
228 }
229 }
$error
Definition: Error.php:17

References $error, and cweagans\Composer\Patches\$patches.

Referenced by cweagans\Composer\Patches\checkPatches(), and cweagans\Composer\Patches\gatherPatches().

+ Here is the caller graph for this function:

◆ isPatchingEnabled()

cweagans\Composer\Patches::isPatchingEnabled ( )
protected

Checks if the root package enables patching.

Returns
bool Whether patching is enabled. Defaults to TRUE.

Definition at line 372 of file Patches.php.

372 {
373 $extra = $this->composer->getPackage()->getExtra();
374
375 if (empty($extra['patches'])) {
376 // The root package has no patches of its own, so only allow patching if
377 // it has specifically opted in.
378 return isset($extra['enable-patching']) ? $extra['enable-patching'] : FALSE;
379 }
380 else {
381 return TRUE;
382 }
383 }

Referenced by cweagans\Composer\Patches\checkPatches(), and cweagans\Composer\Patches\gatherPatches().

+ Here is the caller graph for this function:

◆ writePatchReport()

cweagans\Composer\Patches::writePatchReport (   $patches,
  $directory 
)
protected

Writes a patch report to the target directory.

Parameters
array$patches
string$directory

Definition at line 391 of file Patches.php.

391 {
392 $output = "This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches)\n";
393 $output .= "Patches applied to this directory:\n\n";
394 foreach ($patches as $description => $url) {
395 $output .= $description . "\n";
396 $output .= 'Source: ' . $url . "\n\n\n";
397 }
398 file_put_contents($directory . "/PATCHES.txt", $output);
399 }
$url

References $description, $output, cweagans\Composer\Patches\$patches, and $url.

Field Documentation

◆ $composer

Composer cweagans\Composer\Patches::$composer
protected

Definition at line 33 of file Patches.php.

Referenced by cweagans\Composer\Patches\activate().

◆ $eventDispatcher

EventDispatcher cweagans\Composer\Patches::$eventDispatcher
protected

Definition at line 41 of file Patches.php.

◆ $executor

ProcessExecutor cweagans\Composer\Patches::$executor
protected

Definition at line 45 of file Patches.php.

◆ $io

IOInterface cweagans\Composer\Patches::$io
protected

◆ $patches


The documentation for this class was generated from the following file: