Skip to content

Make the PHP built-in web server step work without server-command - #363

Merged
swissspidy merged 4 commits into
mainfrom
fix/php-server-router-fallback
Sep 14, 2026
Merged

swissspidy merged 4 commits into
mainfrom
fix/php-server-router-fallback

Conversation

@swissspidy

@swissspidy swissspidy commented Sep 14, 2026

Copy link
Copy Markdown
Member

Problem

Given a PHP built-in web server (and the to serve :subdir variant) always passes {RUN_DIR}/vendor/wp-cli/server-command/router.php as the router script to php -S. No built-in step ever puts wp-cli/server-command into the run directory: Given a WP installation with Composer only requires WordPress core. PHP therefore fails every request with a fatal error while trying to open the router, and because CI runs with display_errors=On, that error page is served with a 200 status.

This surfaced in wp-cli/package-command#250: scenarios that serve a ZIP fixture from an empty directory received the PHP error page instead of the file ("Not a zip archive"), and a request for a missing file never produced a 404. The only in-tree consumer of the step, in wp-cli/wp-cli's bootstrap.feature, is tagged @broken, likely for the same reason.

Separately, Then the HTTP status code should be :code still called the Requests v1 Requests class, which WP-CLI 3 no longer autoloads, so the step died with Class "Requests" not found before making the request.

Changes

  • start_php_server() only passes a router when one exists. It checks the run directory's Composer vendor directory first (honoring a custom vendor-dir in its composer.json), then the vendor directory of the project under test. Without a router, PHP's built-in web server serves the directory as-is, which is what a plain directory of static files needs.
  • then_the_http_status_code_should_be() uses RequestsLibrary so it works with whichever Requests version WP-CLI ships.
  • Two new scenarios in behat-steps.feature: one serves a plain directory and asserts the status and the served body, the other asserts a real 404 for a missing file. Both fail against the old behavior of serving PHP's fatal-error page with a 200.

Testing

🤖 Generated with Claude Code

https://claude.ai/code/session_019g8sd6U4w59qiQW7LTt1jG

`start_php_server()` always passed `{RUN_DIR}/vendor/wp-cli/server-command/router.php`
as the router script, but no built-in step puts wp-cli/server-command into
the run directory (`Given a WP installation with Composer` only requires
WordPress core). PHP then fails every request with a fatal error while
trying to open the router, and with `display_errors=On` that error page is
even served with a 200 status, so a scenario serving static files gets an
error page instead of its fixture and a missing file never yields a 404.

Only pass the router when it actually exists, looking in the run directory
first and then in the vendor directory of the project under test. Without a
router, PHP's built-in web server serves the directory as-is, which is what
a plain directory of files needs.

The `Then the HTTP status code should be` step relied on the Requests v1
class, which WP-CLI no longer autoloads, so it failed with a fatal error
before the request was made. Use whichever Requests version WP-CLI ships.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019g8sd6U4w59qiQW7LTt1jG
@swissspidy
swissspidy requested a review from a team as a code owner September 14, 2026 08:15
Copilot AI lite review requested due to automatic review settings September 14, 2026 08:15
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: de289624-1bd0-4bc8-9bb8-2bce554288fb

📥 Commits

Reviewing files that changed from the base of the PR and between 902de26 and c8a6e1d.

📒 Files selected for processing (3)
  • features/behat-steps.feature
  • src/Context/FeatureContext.php
  • src/Context/ThenStepDefinitions.php

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The PHP built-in server step now supports plain directories without a router package. Router discovery is conditional. HTTP status checks load the bundled Requests library dynamically. A Behat scenario verifies a plain directory returns status 200.

Changes

PHP server support

Layer / File(s) Summary
Conditional PHP server router
src/Context/FeatureContext.php
The server command adds a router only when one exists in the run directory or project vendor directory.
HTTP status validation and scenario coverage
src/Context/ThenStepDefinitions.php, features/behat-steps.feature
The status check registers and resolves the bundled Requests library. A new scenario checks that a plain directory with an index.html file returns status 200.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: schlessera

Merge Risk: ⚪ Minimal · up to c8a6e

The PHP server steps retain router-backed behavior while supporting static directories without the optional router package. No actionable merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: making the PHP built-in web server step work when server-command is unavailable.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/php-server-router-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added automated-pr bug Something isn't working scope:testing Related to testing labels Sep 14, 2026
@codecov

codecov Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 32 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Context/FeatureContext.php 0.00% 28 Missing ⚠️
src/Context/ThenStepDefinitions.php 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Member Author

On the red codecov/patch check: the changed lines are in the Behat step definitions, which run inside the Behat process. Coverage in this repository is only collected from PHPUnit and from the wp subprocesses that the functional tests launch, so step-definition code is never instrumented. src/Context/FeatureContext.php sits at 6.57% on main for that reason, and every recent merged PR that touched src/Context (#362, #360, #357, #356, #354, #353) reported 0% patch coverage as well. The new scenario in behat-steps.feature exercises the changed code functionally; there is nothing within this PR that can make that check pass.


Generated by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Two moderate issues remain regarding custom vendor directories and insufficient server-response coverage.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR fixes PHP built-in server routing and HTTP status checks for WP-CLI 3 compatibility.

Changes:

  • Conditionally discovers available router scripts.
  • Uses RequestsLibrary for HTTP requests.
  • Adds plain-directory server coverage.
File summaries
File Summary Final review note
src/Context/ThenStepDefinitions.php Uses the supported Requests abstraction. No unresolved finding.
src/Context/FeatureContext.php Resolves optional PHP server routers. Moderate: Hard-coded /vendor misses custom Composer vendor directories (2 votes).
features/behat-steps.feature Adds plain-directory server coverage. Moderate: The assertion can pass on PHP’s fatal-error page; verify the response body or a 404 (3 votes).
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread features/behat-steps.feature
Comment thread src/Context/FeatureContext.php Outdated
swissspidy and others added 3 commits September 14, 2026 08:25
Look up the server-command router in the vendor directory configured in
the run directory's composer.json, so that a WP installation with a custom
vendor directory is covered as well.

Extend the scenarios to check the response body and a real 404, so that
they fail against the old behavior of serving PHP's fatal-error page with
a 200 status. PHP's built-in web server falls back to index.html for
unknown paths when it exists, so the 404 case lives in its own scenario
without an index file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019g8sd6U4w59qiQW7LTt1jG
PHPStan flags `$argv` as possibly undefined in the PHP blocks of feature
files, so read the argument from `$_SERVER['argv']` instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019g8sd6U4w59qiQW7LTt1jG
PHP 8.5 deprecates the `$http_response_header` variable, and the
deprecation notice on STDERR fails the `I run` step.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019g8sd6U4w59qiQW7LTt1jG
@swissspidy swissspidy added this to the 5.3.2 milestone Sep 14, 2026
@swissspidy
swissspidy merged commit 16f77f8 into main Sep 14, 2026
64 of 65 checks passed
@swissspidy
swissspidy deleted the fix/php-server-router-fallback branch September 14, 2026 09:23
swissspidy added a commit to wp-cli/package-command that referenced this pull request Sep 14, 2026
wp-cli/wp-cli-tests v5.3.2 makes `a PHP built-in web server` work in a
plain directory (wp-cli/wp-cli-tests#363), so the manual `php -S` launch
is no longer needed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019g8sd6U4w59qiQW7LTt1jG
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated-pr bug Something isn't working scope:testing Related to testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants