dansan/php-backport
Dev tool to backport PHP source code for older runtimes (e.g., PHP 7.2+ features down to PHP 7.0). Configure directories to port, run a script on a *_bp branch, commit/push, then require the backported dev branch in Composer.
Installation:
composer require dansan/php-backport --dev
Add to composer.json under require-dev (not require) since this is a dev tool.
Create backport.php:
Place this in your project root (e.g., bin/backport.php):
<?php
require __DIR__.'/../vendor/autoload.php';
$client = new \BackPort\Client();
$client
->setDirsToPort([__DIR__.'/../src'])
->execute();
First Run:
master_bp):
git checkout -b master_bp
php bin/backport.php
Scenario: Your Laravel app (Alpha) depends on a package (Beta) that uses PHP 7.2+ features, but Alpha is locked to PHP 7.0.
Solution:
Beta and create a master_bp branch.backport.php to transform Beta's code to PHP 7.0-compatible syntax.Alpha's composer.json to require Beta from dev-master_bp@dev.Branch Strategy:
master: Latest PHP 7.2+ features (default for most projects).master_bp: Backported PHP 7.0-compatible version (for legacy projects like Alpha).master into master_bp regularly, resolve conflicts by accepting master's changes (since backported code is derived).Automated Backporting:
backport.php to handle project-specific needs:
$client
->setDirsToPort([__DIR__.'/../src', __DIR__.'/../tests'])
->addComposerJsonReplacement(
'/"vendor\/package": "[^"]+"/',
'"vendor/package": "dev-backport-branch@dev"'
)
->execute();
setDirsToPort(): Specify directories to backport (e.g., src, tests).addComposerJsonReplacement(): Update dependencies in composer.json (e.g., switch Gamma to its backport branch).execute(): Run the backport process.CI/CD Pipeline:
backport.php on master_bp after merging master.jobs:
backport:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: master_bp
- run: composer install
- run: php bin/backport.php
- run: composer update
- run: ./vendor/bin/phpunit
Laravel-Specific Patterns:
Beta is a Laravel package, ensure its master_bp version:
function() use(...)).array_key_first() (use reset(array_keys($array)) instead).config/ files to handle PHP 7.0 limitations (e.g., no typed properties).Testing:
# Test backported version
docker run --rm -v $(pwd):/app -w /app composer/php:7.0-cli composer install
docker run --rm -v $(pwd):/app -w /app composer/php:7.0-cli vendor/bin/phpunit
# Test latest version
docker run --rm -v $(pwd):/app -w /app composer/php:8.1-cli vendor/bin/phpunit
Branch Management:
backport.php on master or a feature branch will overwrite files. The script checks for _bp suffix but verify manually.master's changes during merge (backported code is a derivative). Use:
git checkout master_bp
git merge master --strategy-option=ours # Then run backport.php
master_bp if corruption is suspected:
git checkout master
git branch -D master_bp
git push origin -d master_bp
git checkout -b master_bp
PHP Syntax Limitations:
fn($x) => $x with function($x) { return $x; }.$x ??= $y with $x = $x ?? $y.// Before (PHP 7.2+)
private ?string $name;
// After (PHP 7.0)
private $name;
<=> with manual comparison logic.Composer Dependencies:
Beta backports Gamma, ensure Gamma's master_bp is updated first.master_bp's composer.json to avoid unexpected updates:
"require": {
"acme/gamma": "dev-master_bp#1.2.3"
}
Testing Quirks:
PHP_VERSION_ID changes).array_column() requires PHP 5.5+; use array_map fallback).Mockery or PHPUnit’s mocks (PHP 7.0-compatible).Performance:
$client->setCacheDir(__DIR__.'/../var/backport_cache');
Inspect AST Changes:
backport.php:
$client->setVerbose(true);
nikic/php-parser documentation for custom visitor logic.Log Conflicts:
$client->setConflictLogger(function($file, $conflict) {
file_put_contents('backport_conflicts.log', $file . "\n" . $conflict . "\n\n", FILE_APPEND);
});
Dry Runs:
git stash
php bin/backport.php
git diff # Review changes
git stash pop
Custom Visitors:
use PhpParser\NodeVisitorAbstract;
$client->addVisitor(new class extends NodeVisitorAbstract {
public function enterNode(\PhpParser\Node $node) {
if ($node instanceof \PhpParser\Node\Expr\ArrowFunction) {
// Transform arrow function to anonymous function
return new \PhpParser\Node\Expr\Closure(
[$node->getParams()],
$node->getStmts()
);
}
return null;
}
});
Pre/Post Hooks:
$client->setPreBackportHook(function() {
// Example: Run PHPStan to catch issues before backporting
shell_exec('vendor/bin/phpstan analyse --level=5 src');
});
Selective Backporting:
$client->setDirsToPort([__DIR__.'/../src'])
->setExcludedFiles(['src/DeprecatedClass.php']);
PHP Version Targeting:
How can I help you explore Laravel packages today?