Skip to content

fix: strip shebang from mock server router for php -S#15

Merged
Fahl-Design merged 2 commits into
mainfrom
fix/mock-server-router-shebang
Jul 3, 2026
Merged

fix: strip shebang from mock server router for php -S#15
Fahl-Design merged 2 commits into
mainfrom
fix/mock-server-router-shebang

Conversation

@Fahl-Design

@Fahl-Design Fahl-Design commented Jul 3, 2026

Copy link
Copy Markdown
Member

Problem

CI failed on 4 acceptance tests (run 28681355179) with:

Fatal error: strict_types declaration must be the very first statement in the script in
vendor/webproject-xyz/php-openapi-mock-server/bin/openapi-mock-server on line 4

Root cause

startMockServer() passed the dependency's bin/openapi-mock-server directly to php -S as the router script. That bin is a CLI entrypoint prefixed with #!/usr/bin/env php.

PHP strips shebangs for CLI scripts but not reliably for built-in-server router scripts across all PHP builds — CI's PHP 8.3 does not strip it (local 8.3.31 does, which is why it passed locally). The leaked shebang becomes body output, so the following declare(strict_types=1) is no longer the first statement → fatal error. Responses became HTML error pages instead of JSON, failing every seeResponseIsJson assertion.

Regression surfaced after the recent dep bump (ec025b2) to php-openapi-mock-server 1.3.5, which added the shebang.

public/index.php cannot be used instead: its autoload path (__DIR__/../vendor/autoload.php) is standalone-only and breaks when installed as a dependency.

Fix

resolveRouterScript() writes a shebang-less sibling router (.openapi-mock-server.router.php) next to the bin — preserving __DIR__ so the bin's relative autoload/config paths keep resolving — serves that via php -S, and removes it in _afterSuite. No-op when the bin has no shebang.

Verification

  • Full suite green locally (14/14).
  • Confirmed generated shim has <?php/declare as first statements and serves /users → 200.
  • GrumPHP (php-cs-fixer, phpstan, codeception) passed on commit.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

The dependency's bin/openapi-mock-server is a CLI entrypoint prefixed
with a `#!/usr/bin/env php` shebang. It was passed directly to
`php -S` as the router script. PHP strips shebangs for CLI scripts but
not reliably for built-in-server router scripts across all PHP builds
(CI's PHP 8.3 does not). The unstripped shebang leaks into the response
body, so the following `declare(strict_types=1)` is no longer the first
statement and triggers a fatal error. Responses become HTML error pages
instead of JSON, failing every acceptance test via seeResponseIsJson.

public/index.php cannot be used instead: its autoload path is
standalone-only and breaks when installed as a dependency.

Emit a shebang-less sibling router next to the bin (preserving __DIR__
so relative autoload/config paths keep resolving), serve that, and
remove it in _afterSuite. No-op when the bin has no shebang.
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16d9826b-0e72-4e1c-9bd9-f34d8b5086e9

📥 Commits

Reviewing files that changed from the base of the PR and between 6db0e28 and 04414c8.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • composer.json

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved local mock server startup to work reliably with the PHP built-in server, including cases where the launcher file starts with a shebang.
    • Added automatic cleanup of temporary router/startup scripts after the test suite completes.
  • Chores
    • Updated dependency versions (library and development tooling) to the latest compatible patch releases.

Walkthrough

OpenApiServerMock now resolves a shebang-free router script for PHP’s built-in server, cleans it up after the suite, and composer dependency constraints are bumped.

Changes

Router Script Generation and Cleanup

Layer / File(s) Summary
Imports and state property
src/OpenApiServerMock.php
Adds filesystem/regex function imports and a private property to track the generated router script path.
Router script resolution logic
src/OpenApiServerMock.php
resolveRouterScript() reads the bin file, strips a leading shebang, writes a sibling router script, stores its path, and returns it; startMockServer() uses the resolved path in the php -S command.
Suite teardown cleanup
src/OpenApiServerMock.php
_afterSuite() deletes the generated router script file if present and resets the stored path.
Dependency Version Updates
Layer / File(s) Summary
Composer constraint bumps
composer.json
Updates the main library requirement and three require-dev tool version constraints.

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

Sequence Diagram(s)

sequenceDiagram
    participant Suite as Codeception Suite
    participant Mock as OpenApiServerMock
    participant FS as Filesystem
    participant PHP as PHP Built-in Server

    Suite->>Mock: startMockServer()
    Mock->>Mock: resolveRouterScript()
    Mock->>FS: read bin file
    Mock->>Mock: strip leading shebang
    Mock->>FS: write sibling router script
    Mock->>PHP: php -S with resolved router script
    Suite->>Mock: _afterSuite()
    Mock->>FS: unlink router script
    Mock->>Mock: clear stored path
Loading

Poem

A shebang slipped away with care,
A router script was born nearby there.
The server ran, then came the night,
And cleanup nibbled files from sight.
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main fix: stripping the shebang from the mock server router for php -S.
Description check ✅ Passed The description is directly related to the changeset and explains the bug, root cause, fix, and verification.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mock-server-router-shebang

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.

Bump 26 locked packages including
webproject-xyz/php-openapi-mock-server 1.3.5 => 1.3.6 and
symfony/* 7.4.14, and raise composer.json constraints to match.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/OpenApiServerMock.php (1)

186-193: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard the write of the generated router script.

file_put_contents() can fail (unwritable vendor bin dir, disk full) and returns false. Currently a failed write still stores $routerPath and returns it, so php -S is launched against a missing/empty router and only surfaces later as an opaque "failed to start" error from waitForServer(). Fail fast with a clear message instead.

♻️ Proposed change
         $stripped     = (string) preg_replace('/^#![^\n]*\n/', '', $source, 1);
         $routerPath   = dirname($binPath) . '/.openapi-mock-server.router.php';
-        file_put_contents($routerPath, $stripped);
-        $this->routerScript = $routerPath;
+        if (false === file_put_contents($routerPath, $stripped)) {
+            throw new RuntimeException("Failed to write mock server router script to '{$routerPath}'.");
+        }
+        $this->routerScript = $routerPath;
 
         return $routerPath;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OpenApiServerMock.php` around lines 186 - 193, Guard the generated router
write in OpenApiServerMock::getRouterPath() so a failed file_put_contents() does
not leave a bogus router path behind. Check the return value when writing
$routerPath, and if it is false, throw a clear exception with context instead of
returning the path; keep the logic around $stripped, $routerPath, and the
shebang-removal flow unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/OpenApiServerMock.php`:
- Around line 186-193: Guard the generated router write in
OpenApiServerMock::getRouterPath() so a failed file_put_contents() does not
leave a bogus router path behind. Check the return value when writing
$routerPath, and if it is false, throw a clear exception with context instead of
returning the path; keep the logic around $stripped, $routerPath, and the
shebang-removal flow unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f19b089a-c7b4-43de-9c12-1cc9d1f74776

📥 Commits

Reviewing files that changed from the base of the PR and between ec025b2 and 6db0e28.

📒 Files selected for processing (1)
  • src/OpenApiServerMock.php

@Fahl-Design

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Fahl-Design Fahl-Design merged commit 7c69516 into main Jul 3, 2026
7 checks passed
@Fahl-Design Fahl-Design deleted the fix/mock-server-router-shebang branch July 3, 2026 22:56
webproject-bot Bot pushed a commit that referenced this pull request Jul 3, 2026
## [1.0.2](1.0.1...1.0.2) (2026-07-03)

### Bug Fixes

* **deps:** composer up and bump ([a3d8c03](a3d8c03))
* strip shebang from mock server router for php -S ([#15](#15)) ([7c69516](7c69516))
@webproject-bot

webproject-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.0.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant