ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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.

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

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  }

◆ checkPatches()

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

Before running composer install,.

Parameters
Event$event

Definition at line 83 of file Patches.php.

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

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
+ 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.

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

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

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  }
$type
$index
Definition: metadata.php:60
$data
Definition: bench.php:6
+ 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.

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

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  }
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
getPackageFromOperation(OperationInterface $operation)
Get a Package object from an OperationInterface object.
Definition: Patches.php:291
+ 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

Definition at line 313 of file Patches.php.

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

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

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
$filename
Definition: buildRTE.php:89
+ Here is the call graph for this function:
+ Here is the caller 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

Definition at line 291 of file Patches.php.

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

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  }
+ 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

Definition at line 181 of file Patches.php.

References $description, cweagans\Composer\Patches\$patches, $url, cweagans\Composer\Patches\getAndApplyPatch(), cweagans\Composer\Patches\getPackageFromOperation(), cweagans\Composer\PatchEvents\POST_PATCH_APPLY, cweagans\Composer\PatchEvents\PRE_PATCH_APPLY, and cweagans\Composer\Patches\writePatchReport().

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

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  }
+ Here is the call graph for this function:
+ 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.

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

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  }
+ 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.

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

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

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
+ Here is the caller graph for this function:

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: