Skip to content

project create: discover, select, and persist the local PHP binary #1303

Description

@shyim

Problem

When shopware-cli project create creates a project without Docker, the CLI currently validates and runs Composer against whichever PHP is resolved from PHP_BINARY or the first php on PATH. The selected executable is not shown to the user and is not persisted in the generated project configuration.

This is fragile on developer machines with several PHP installations. A project can be created with a compatible PHP version today and later use a different default PHP after a shell, Homebrew, package-manager, or PATH change.

The generated configuration is named .shopware-project.yml by the CLI (the reader also supports the .yaml extension).

Goal

For a local (non-Docker) project create flow, discover the usable PHP executables installed on the machine, let an interactive user select one that satisfies the chosen Shopware release's PHP constraint, use that exact binary for creation, and persist its absolute path in the new project configuration.

Symfony CLI's phpstore is a useful reference for cross-platform PHP discovery. It must only be used as an implementation reference unless licensing has explicitly been reviewed: phpstore is AGPL-3.0.

Current behavior

  • internal/system.resolvePHPBinary() resolves PHP_BINARY first and otherwise php on PATH.
  • ValidateProjectDependencies() checks that resolved binary against the PHP constraint for the selected Shopware version.
  • For a local creation, runComposerInstall() invokes $PHP_BINARY composer install only when PHP_BINARY is set; otherwise it invokes composer install.
  • The resulting .shopware-project.yml has no PHP executable setting.
  • Docker creation selects a Docker image PHP version and must retain its current behavior.

Proposed behavior

Discovery

Add a small, testable PHP-discovery service in internal/system that returns candidates with at least:

type PHPInstallation struct {
    Binary  string // absolute/canonical executable path
    Version string // normalized version reported by the executable
    Source  string // e.g. PHP_BINARY, PATH, Homebrew, system package
}

Requirements:

  • Discover local PHP installations cross-platform, following the platform-specific discovery approach from Symfony PHP Store where applicable.
  • Include the configured PHP_BINARY first when it points to an executable.
  • Include the PHP resolved from PATH.
  • Discover versioned installations exposed by common local package managers/platform mechanisms (for example Homebrew PHP formulae on macOS and versioned/system alternatives on Linux; Windows discovery should follow the selected cross-platform strategy).
  • Canonicalize paths, de-duplicate candidates by executable path, execute each candidate to obtain its version, and ignore broken/unexecutable binaries.
  • Sort valid candidates deterministically, preferably newest version first while retaining PHP_BINARY as the preferred/default candidate.
  • Do not download, install, or alter PHP installations.

Interactive project create

After the Shopware version has been resolved and its PHP constraint is known:

  • When --docker is not selected, display a select prompt containing local PHP candidates that satisfy the constraint.
  • Show enough information to choose confidently, for example:
    PHP 8.3.19 — /opt/homebrew/opt/php@8.3/bin/php.
  • Preselect the current PHP_BINARY/PATH default when it is compatible; otherwise preselect the newest compatible candidate.
  • If PHP candidates were found but none satisfy the constraint, keep the existing actionable validation error and list the discovered versions/paths.
  • If discovery finds only one compatible candidate, still present it in the interactive flow so the chosen executable is explicit. The prompt may be skipped only if a deliberate UX decision is documented.
  • Do not show this prompt for Docker projects.

Non-interactive behavior

  • Add --php-binary <path> to select the executable explicitly for local project creation.
  • Validate that its path is executable and satisfies the selected Shopware PHP constraint.
  • In non-interactive local creation without --php-binary, preserve a deterministic fallback: use a valid PHP_BINARY value first, then the compatible PATH/default candidate. If no compatible candidate exists, fail with a clear message and the flag hint.
  • Reject or clearly document --php-binary together with --docker; it must not silently affect Docker image selection.

Persisted configuration and runtime use

Add an optional root-level php_binary field to .shopware-project.yml, for example:

php_binary: /opt/homebrew/opt/php@8.3/bin/php
  • Add the field, documentation, and JSON-schema description to internal/shop.Config / internal/shop/config_schema.json.
  • Write the selected absolute binary path when creating a non-Docker project.
  • Do not write it for Docker projects.
  • Ensure project operations that execute PHP/Composer for that project resolve this setting consistently, so persistence is meaningful beyond the initial Composer install.
  • Preserve precedence for an explicit command-line PHP selection. Define and document the final precedence order; a recommended order is: explicit command flag > project php_binary > PHP_BINARY > PATH/default.
  • Existing projects without php_binary must continue to work exactly as today.

Acceptance criteria

  • A macOS/Linux/Windows-compatible discovery abstraction is covered with platform-appropriate unit tests; tests do not depend on the developer machine's installed PHP versions.
  • Candidate detection canonicalizes, de-duplicates, ignores unusable executables, and reports executable path plus parsed PHP version.
  • Interactive local project create asks for a compatible PHP executable after Shopware version selection.
  • The selected executable is used for dependency validation and the Composer install.
  • The generated local project config contains the selected absolute php_binary path and exposes it through project config-schema.
  • Docker project creation does not prompt for or write php_binary, and continues using its configured Docker PHP image version.
  • project create --no-interaction --php-binary /path/to/php succeeds only for an executable compatible with the selected Shopware version.
  • Non-interactive fallback and all failure messages explain which binary was selected/checked and how to override it.
  • Existing project configuration without php_binary remains backward compatible.
  • Tests cover: multiple versions, duplicate symlinked paths, invalid executable, no compatible version, PHP_BINARY preference, explicit flag precedence, config serialization, and Docker bypass.

Likely implementation areas

  • internal/system/php.go: split the current single-binary resolver into reusable discovery and selection helpers.
  • cmd/project/project_create.go, project_create_form.go, project_create_validate.go, and project_create_install.go: collect and apply the selected PHP binary.
  • internal/shop/config.go and internal/shop/config_schema.json: add the persisted configuration property.
  • Project commands that spawn PHP or Composer: route binary selection through the project-aware resolver.
  • Add focused *_test.go coverage with fake PHP executables, following the existing internal/system/php_test.go pattern.

Out of scope

  • Installing or managing PHP versions.
  • Changing Docker PHP image selection.
  • Migrating existing project configs automatically.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions