Skip to content

Instantly share code, notes, and snippets.

@adamzero1
Last active July 16, 2026 08:57
Show Gist options
  • Select an option

  • Save adamzero1/9be573ed6d1366f7198ede715a135035 to your computer and use it in GitHub Desktop.

Select an option

Save adamzero1/9be573ed6d1366f7198ede715a135035 to your computer and use it in GitHub Desktop.
Add composer patches
#!/usr/bin/env php
<?php
declare(strict_types=1);
/**
* Merge patch declarations into composer.json extra.patches.
*
* Usage:
* php add_composer_patches.php payload.json [composer.json]
* cat payload.json | php add_composer_patches.php [composer.json]
*/
function fail(string $message, int $code = 1): void
{
fwrite(STDERR, $message . PHP_EOL);
exit($code);
}
function getPayloadJson(array $argv): string
{
if (isset($argv[1]) && $argv[1] !== '') {
if (!is_file($argv[1])) {
fail("Payload file not found: {$argv[1]}");
}
$contents = file_get_contents($argv[1]);
if ($contents === false) {
fail("Unable to read payload file: {$argv[1]}");
}
return $contents;
}
$stdin = stream_get_contents(STDIN);
if ($stdin === false || trim($stdin) === '') {
fail('No payload provided. Pass a JSON file path or pipe JSON via stdin.');
}
return $stdin;
}
function loadJsonFile(string $path): array
{
if (!is_file($path)) {
fail("composer.json not found: {$path}");
}
$contents = file_get_contents($path);
if ($contents === false) {
fail("Unable to read file: {$path}");
}
try {
$decoded = json_decode($contents, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
fail("Invalid JSON in {$path}: {$e->getMessage()}");
}
if (!is_array($decoded)) {
fail("Unexpected JSON structure in {$path}");
}
return $decoded;
}
function decodePayload(string $json): array
{
try {
$decoded = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
fail('Invalid payload JSON: ' . $e->getMessage());
}
if (!is_array($decoded) || !isset($decoded['patches']) || !is_array($decoded['patches'])) {
fail('Payload must contain a top-level "patches" object.');
}
return $decoded['patches'];
}
function normalizePackagePatches(mixed $value): array
{
return is_array($value) ? $value : [];
}
function declarationExists(array $existingDeclarations, string $label, string $path): bool
{
if (array_key_exists($label, $existingDeclarations)) {
return true;
}
return in_array($path, $existingDeclarations, true);
}
$payloadJson = getPayloadJson($argv);
$composerPath = $argv[2] ?? 'composer.json';
$payloadPatches = decodePayload($payloadJson);
$composer = loadJsonFile($composerPath);
if (!isset($composer['extra']) || !is_array($composer['extra'])) {
$composer['extra'] = [];
}
if (!isset($composer['extra']['patches']) || !is_array($composer['extra']['patches'])) {
$composer['extra']['patches'] = [];
}
foreach ($payloadPatches as $packageName => $declarations) {
if (!is_array($declarations)) {
continue;
}
$existingDeclarations = normalizePackagePatches($composer['extra']['patches'][$packageName] ?? []);
foreach ($declarations as $label => $patchPath) {
if (!is_string($label) || !is_string($patchPath)) {
continue;
}
if (declarationExists($existingDeclarations, $label, $patchPath)) {
echo "declaration already present: {$packageName} :: {$label}" . PHP_EOL;
continue;
}
$existingDeclarations[$label] = $patchPath;
echo "adding declaration: {$packageName} :: {$label}" . PHP_EOL;
}
$composer['extra']['patches'][$packageName] = $existingDeclarations;
}
$encoded = json_encode(
$composer,
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
);
if ($encoded === false) {
fail('Failed to encode updated composer.json');
}
$encoded .= PHP_EOL;
if (file_put_contents($composerPath, $encoded) === false) {
fail("Unable to write file: {$composerPath}");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment