Spec: PHP Version
Issue: MAR-33 Date: 2026-04-24 Status: In Review
Overview
PHP version management sets the PHP runtime version for a deployed site. Shipper reads the desired version from ProjectConfig::phpVersion() and delegates the change to provider-specific implementations (Ploi, Forge). An empty string means no change is made.
Current Implementation
Key Classes / Files
| Class/File | Role |
|---|---|
App\Deployment\Contracts\PhpVersionManagerInterface |
Interface defining plan() and apply() |
App\Deployment\Providers\Ploi\PloiPhpVersionManager |
Sets PHP version via Ploi API |
App\Deployment\Providers\Forge\ForgePhpVersionManager |
Sets PHP version via Forge API |
Functional Requirements
FR-001 — No-Op When Version Unset
When ProjectConfig::phpVersion() returns an empty string, plan() returns an empty array and apply() returns OperationResult::ok() without calling the provider API.
FR-002 — Plan Output
When a version is set, plan() returns ["Set PHP version to: {$phpVersion}"].
FR-003 — Apply via Provider
apply() calls the provider API to set the PHP version on the site. If an empty string is passed to apply(), it behaves as a no-op (matching the FR-001 behavior).
FR-004 — Single Version String
The version is a simple string (e.g., '8.2', '8.3'), not a semver range or array.
Configuration Interface
# Project level (shipper.php)
php_version: "8.3"
php_version: ""
Data Contracts
// PhpVersionManagerInterface
interface PhpVersionManagerInterface {
/** @return array<string> */
public function plan(DeploymentContext $context): array;
public function apply(SiteContext $site, string $phpVersion): OperationResult;
}
// Both implementations follow this pattern:
public function apply(SiteContext $site, string $phpVersion): OperationResult {
if ($phpVersion === '') {
return OperationResult::ok();
}
// ... provider API call ...
}
Edge Cases
- Empty string php_version: No API call made; returns
OperationResult::ok(). - Provider API throws: Returns
OperationResult::fail()with descriptive error. - Invalid version string: Not validated at Shipper level; passed directly to provider API.
- Version unchanged from current: Provider API may handle this silently or reject; Shipper forwards the call regardless.
Acceptance Criteria
-
plan()returns an empty array whenphp_versionis empty -
plan()returns["Set PHP version to: {$phpVersion}"]when version is set -
apply('')makes no API call and returnsOperationResult::ok() - Ploi provider calls
$server->sites($site->siteId)->phpVersion($phpVersion) - Forge provider calls
$forge->changeSitePHPVersion($site->serverId, $site->siteId, $phpVersion) - Provider exceptions result in
OperationResult::fail()
Open Questions / Potential Concerns
- Should Shipper validate that the PHP version is available on the target server before attempting to set it?
- Is there a minimum PHP version that Shipper itself requires, separate from the deployed application?
- Should
php_versionsupport multiple versions for different subdirectories or应用的路径?