Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"symfony/console": "^6.4.34",
"symfony/event-dispatcher": "^6.4.32",
"symfony/process": "^6.4.33",
"thecodingmachine/safe": "^3.4",
"thephpf/attestation": "^0.0.5",
"webmozart/assert": "^1.12.1"
},
Expand Down
145 changes: 144 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions docs/extension-maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,3 +579,18 @@ jobs:
```

Source: [https://github.com/php/php-windows-builder?tab=readme-ov-file#examples](https://github.com/php/php-windows-builder?tab=readme-ov-file#examples)

## Other features

### Placeholder Replacement

To help backwards compatibility with PECL extensions, PIE supports some automatic placeholder replacements within
all `.c` and .`h` files found within the downloaded source directory. These placeholders are replaced after the
download step, and before the build step. PIE will automatically replace the following placeholders:

| Placeholder | Description | Example |
|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|
| `@name@`, `@package_name@`, `@package-name@` | The short, internal name of the PHP extension (e.g., `xdebug`). _Note: this is not the Packagist package name (e.g. `xdebug/xdebug`)_. | `xdebug` |
| `@version@`, `@package_version@`, `@package-version@` | The "pretty" version of the package defined in `composer.json`. | `3.3.2` |
| `@release_date@`, `@release-date@` | The formatted release date according to Composer package metadata. | `2024-01-15T10:00:00+00:00` |
| `@php_bin@`, `@php-bin@` | The full path to the PHP binary executable used during the build. | `/usr/bin/php8.4` |
110 changes: 110 additions & 0 deletions src/Building/PlaceholderReplacer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Building;

use Composer\IO\IOInterface;
use DateTimeInterface;
use Php\Pie\Downloading\DownloadedPackage;
use Php\Pie\Platform\TargetPlatform;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Safe\Exceptions\FilesystemException;
use SplFileInfo;

use function assert;
use function in_array;
use function Safe\file_get_contents;
use function Safe\file_put_contents;
use function str_ireplace;

/**
* This tries to support some of the PECL style replacements, but is less flexible. In PECL, you would define in your
* package.xml something like;
*
* <tasks:replace from="@package_version@" to="version" type="package-info" />
*
* This would perform a replacement on the given file. I [searched through GitHub](https://github.com/search?q=%22tasks%3Areplace%22+language%3AXML&type=code&p=1&l=XML)
* to find some common replacements to make, and defined them in a mostly hard-coded way, or at least the ones that
* make sense or low-hanging fruit (all underscores can also use hyphens):
*
* - @name@ or @package_name@ - replaces with the extension name (NOT the Composer package name)
* - @version@ or @package_version@ - replaces with the Composer "pretty" version
* - @release_date@ - release date according to Composer package, formatted 'Y-m-d\TH:i:sP'
* - @php_bin@ - path to the PHP binary
*
* Some omitted replacements are `@php_dir@` (seems to actually point to PEAR directory, which is redundant anyway),
* `@bin_dir@` (could get this with PHP_BINDIR constant from the $targetPlatform, but not sure if is worth the time),
* and a few other variations (@phd_ide_version@ for example, which could be replaced with @package_version@).
*
* If this feature needs more flexibility in future, we can look into it, but this implementation seems to be a fairly
* easy win for some basic replacements anyway...
*/
class PlaceholderReplacer
{
private const FILE_EXTENSIONS = ['c', 'h'];

public function replacePlaceholdersWithPlaceholderReplacements(IOInterface $io, TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage): void
{
$replacements = [
'ext-name' => $downloadedPackage->package->extensionName()->name(),
'version' => $downloadedPackage->package->composerPackage()->getPrettyVersion(),
'release-date' => (string) $downloadedPackage->package->composerPackage()->getReleaseDate()?->format(DateTimeInterface::ATOM),
'php-bin' => $targetPlatform->phpBinaryPath->phpBinaryPath,
];

foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($downloadedPackage->extractedSourcePath)) as $file) {
assert($file instanceof SplFileInfo);

if (! $file->isFile() || ! in_array($file->getExtension(), self::FILE_EXTENSIONS)) {
continue;
}

$io->write('Replacing placeholders in: ' . $file->getPathname(), verbosity: IOInterface::VERY_VERBOSE);
try {
$this->replaceReplacementsInFile($replacements, $file->getPathname());
} catch (FilesystemException $e) {
$io->write('Placeholder replacement failed in : ' . $file->getPathname(), verbosity: IOInterface::VERBOSE);
$io->write((string) $e, verbosity: IOInterface::VERBOSE);
}
}
}

/**
* @param array{ext-name: string, version: string, release-date: string, php-bin: string} $replacements
*
* @throws FilesystemException
*/
private function replaceReplacementsInFile(array $replacements, string $filename): void
{
file_put_contents(
$filename,
str_ireplace(
[
'@name@',
'@package_name@',
'@package-name@',
'@package_version@',
'@package-version@',
'@release_date@',
'@release-date@',
'@php_bin@',
'@php-bin@',
],
[
$replacements['ext-name'],
$replacements['ext-name'],
$replacements['ext-name'],
$replacements['version'],
$replacements['version'],
$replacements['release-date'],
$replacements['release-date'],
$replacements['php-bin'],
$replacements['php-bin'],
],
file_get_contents($filename),
),
);
}
}
8 changes: 8 additions & 0 deletions src/ComposerIntegration/InstallAndBuildProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Composer\Package\CompletePackageInterface;
use Composer\PartialComposer;
use Php\Pie\Building\Build;
use Php\Pie\Building\PlaceholderReplacer;
use Php\Pie\DependencyResolver\Package;
use Php\Pie\Downloading\DownloadedPackage;
use Php\Pie\Installing\Install;
Expand All @@ -20,6 +21,7 @@ public function __construct(
private readonly Build $pieBuild,
private readonly Install $pieInstall,
private readonly InstalledJsonMetadata $installedJsonMetadata,
private readonly PlaceholderReplacer $placeholderReplacer,
) {
}

Expand All @@ -42,6 +44,12 @@ public function __invoke(
$downloadedPackage->extractedSourcePath,
));

$this->placeholderReplacer->replacePlaceholdersWithPlaceholderReplacements(
$io,
$composerRequest->targetPlatform,
$downloadedPackage,
);

$this->installedJsonMetadata->addDownloadMetadata(
$composer,
$composerRequest,
Expand Down
Loading