{"id":51138717,"url":"https://github.com/mock-server/mockserver-client-php","last_synced_at":"2026-06-25T20:30:42.728Z","repository":{"id":365217407,"uuid":"1266083202","full_name":"mock-server/mockserver-client-php","owner":"mock-server","description":"PHP client for MockServer — read-only mirror of mockserver-client-php/ from github.com/mock-server/mockserver-monorepo (do not edit here; source lives in the monorepo)","archived":false,"fork":false,"pushed_at":"2026-06-16T10:29:06.000Z","size":53,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-16T12:22:09.057Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mock-server.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-11T09:59:41.000Z","updated_at":"2026-06-16T10:29:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mock-server/mockserver-client-php","commit_stats":null,"previous_names":["mock-server/mockserver-client-php"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/mock-server/mockserver-client-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mock-server%2Fmockserver-client-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mock-server%2Fmockserver-client-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mock-server%2Fmockserver-client-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mock-server%2Fmockserver-client-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mock-server","download_url":"https://codeload.github.com/mock-server/mockserver-client-php/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mock-server%2Fmockserver-client-php/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34792208,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-25T20:30:41.991Z","updated_at":"2026-06-25T20:30:42.719Z","avatar_url":"https://github.com/mock-server.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MockServer PHP Client\n\nPHP client for [MockServer](https://www.mock-server.com) - enables easy mocking of any system you integrate with via HTTP or HTTPS.\n\n## Requirements\n\n- PHP 8.1+\n- Composer\n\n## Installation\n\n```bash\ncomposer require mock-server/mockserver-client\n```\n\n## Quick Start\n\n```php\n\u003c?php\n\nuse MockServer\\MockServerClient;\nuse MockServer\\HttpRequest;\nuse MockServer\\HttpResponse;\nuse MockServer\\VerificationTimes;\n\n// Connect to MockServer\n$client = new MockServerClient('localhost', 1080);\n\n// Create an expectation\n$client-\u003ewhen(\n    HttpRequest::request()-\u003emethod('GET')-\u003epath('/hello')\n)-\u003erespond(\n    HttpResponse::response()\n        -\u003estatusCode(200)\n        -\u003eheader('Content-Type', 'application/json')\n        -\u003ebody('{\"message\":\"world\"}')\n);\n\n// Verify the request was received\n$client-\u003everify(\n    HttpRequest::request()-\u003epath('/hello'),\n    VerificationTimes::atLeast(1)\n);\n\n// Reset all expectations\n$client-\u003ereset();\n```\n\n## API\n\n### Creating Expectations\n\n```php\nuse MockServer\\Times;\nuse MockServer\\TimeToLive;\nuse MockServer\\Delay;\nuse MockServer\\HttpForward;\n\n// Respond with a delay\n$client-\u003ewhen(\n    HttpRequest::request()-\u003emethod('POST')-\u003epath('/api/data')\n        -\u003eheader('Content-Type', 'application/json')\n        -\u003ejsonBody(['key' =\u003e 'value'])\n)-\u003erespond(\n    HttpResponse::response()\n        -\u003estatusCode(201)\n        -\u003ebody('{\"id\": 1}')\n        -\u003edelay(Delay::milliseconds(500))\n);\n\n// Match only 3 times, with priority\n$client-\u003ewhen(\n    HttpRequest::request()-\u003epath('/limited'),\n    Times::exactly(3),\n    TimeToLive::exactly('SECONDS', 60),\n    priority: 10\n)-\u003erespond(\n    HttpResponse::response()-\u003estatusCode(200)\n);\n\n// Forward to another server\n$client-\u003ewhen(\n    HttpRequest::request()-\u003epath('/proxy')\n)-\u003eforward(\n    HttpForward::forward()-\u003ehost('backend.local')-\u003eport(8080)-\u003escheme('HTTP')\n);\n```\n\n### Class Callbacks\n\nA **class callback** references a server-side class (already on MockServer's\nclasspath) that implements one of MockServer's callback interfaces, and lets\nMockServer compute the response — or the request to forward — dynamically. It is\npure JSON over the control plane, so it works fully from the PHP client.\n\n```php\nuse MockServer\\Expectation;\nuse MockServer\\HttpClassCallback;\nuse MockServer\\Delay;\n\n// Respond using a server-side ExpectationResponseCallback class\n$client-\u003eupsertExpectation(\n    (new Expectation())\n        -\u003ehttpRequest(HttpRequest::request()-\u003emethod('GET')-\u003epath('/dynamic'))\n        -\u003ehttpResponseClassCallback('com.example.MyResponseCallback')\n);\n\n// Optionally add a delay / mark the connection primary\n$client-\u003eupsertExpectation(\n    (new Expectation())\n        -\u003ehttpRequest(HttpRequest::request()-\u003epath('/delayed'))\n        -\u003ehttpResponseClassCallback(\n            HttpClassCallback::callback('com.example.MyResponseCallback')\n                -\u003edelay(Delay::milliseconds(250))\n                -\u003eprimary(true)\n        )\n);\n\n// Forward using a server-side ExpectationForwardCallback class\n$client-\u003eupsertExpectation(\n    (new Expectation())\n        -\u003ehttpRequest(HttpRequest::request()-\u003epath('/proxy'))\n        -\u003ehttpForwardClassCallback('com.example.MyForwardCallback')\n);\n```\n\n\u003e **Object / closure callbacks are not available in PHP.** MockServer also\n\u003e supports *object* (closure) callbacks, where the callback runs in your own\n\u003e process: the client opens a callback **WebSocket**, the server hands it a\n\u003e `clientId`, and on each match it streams the request to your code, which\n\u003e returns the response. The PHP client is **REST-only and does not implement\n\u003e that callback WebSocket**, so object/closure callbacks cannot be used from\n\u003e PHP — use a **class callback** (a class on the MockServer classpath) instead.\n\u003e This is a transport limitation of the PHP client, not of MockServer.\n\nSee [`examples/php/callback`](../examples/php/callback) for a runnable example.\n\n### Verification\n\n```php\nuse MockServer\\VerificationTimes;\n\n// Verify at least once\n$client-\u003everify(\n    HttpRequest::request()-\u003epath('/hello'),\n    VerificationTimes::atLeast(1)\n);\n\n// Verify exactly 3 times\n$client-\u003everify(\n    HttpRequest::request()-\u003emethod('POST')-\u003epath('/api'),\n    VerificationTimes::exactly(3)\n);\n\n// Verify sequence\n$client-\u003everifySequence(\n    HttpRequest::request()-\u003epath('/first'),\n    HttpRequest::request()-\u003epath('/second')\n);\n```\n\n### Retrieving Recorded Data\n\n```php\n// Retrieve recorded requests\n$requests = $client-\u003eretrieveRecordedRequests(\n    HttpRequest::request()-\u003epath('/api')\n);\n\n// Retrieve active expectations\n$expectations = $client-\u003eretrieveActiveExpectations();\n\n// Retrieve log messages\n$logs = $client-\u003eretrieveLogMessages();\n```\n\n### Control Operations\n\n```php\n// Clear specific expectations/logs\n$client-\u003eclear(HttpRequest::request()-\u003epath('/old'));\n$client-\u003eclear(null, 'EXPECTATIONS');  // type: EXPECTATIONS, LOG, or ALL\n$client-\u003eclearById('my-expectation-id');\n\n// Reset everything\n$client-\u003ereset();\n\n// Check server status\n$status = $client-\u003estatus();  // ['ports' =\u003e [1080]]\n\n// Bind additional ports\n$client-\u003ebind(1081, 1082);\n\n// Check if server is running\nif ($client-\u003ehasStarted()) {\n    echo \"MockServer is ready\";\n}\n```\n\n### SRE Control Plane\n\nMethods for resilience verification — load generation, fault injection, SLO\nverdicts, preemption (cordon/drain) and scheduled chaos experiments. Some are\ngated behind a server start-up flag and raise `FeatureNotEnabledException`\n(HTTP 403) until that flag is set.\n\n```php\nuse MockServer\\LoadScenario;\nuse MockServer\\LoadProfile;\nuse MockServer\\LoadStage;\n\n// Load generation registry: register scenarios (always allowed), then start\n// them (requires loadGenerationEnabled=true). Each scenario is an SLI producer.\n$scenario = LoadScenario::scenario('checkout-load')\n    -\u003emaxRequests(5000)\n    -\u003eprofile(LoadProfile::of(\n        LoadStage::vuRamp(1, 10, 10000),   // warm up\n        LoadStage::vuHold(10, 30000),      // steady state\n        LoadStage::pause(5000),            // cool down\n    ))\n    -\u003eaddStep(\n        HttpRequest::request()-\u003emethod('GET')-\u003epath('/api/item/$iteration.index'),\n        Delay::milliseconds(20),\n    );\n\n$client-\u003eloadScenario($scenario);            // PUT — register (does not run)\n$client-\u003estartLoadScenarios('checkout-load');// PUT /start — drive load (name or array)\n$client-\u003eloadScenarios();                    // GET — list all + state\n$client-\u003egetLoadScenario('checkout-load');   // GET /{name}\n$client-\u003estopLoadScenarios();                // PUT /stop — stop all (or pass names)\n$client-\u003edeleteLoadScenario('checkout-load');// DELETE /{name}\n$client-\u003eclearLoadScenarios();               // DELETE — remove all\n\n// Or register-and-start in one call:\n$client-\u003erunLoadScenario($scenario);\n\n// Service-scoped HTTP chaos for a downstream host (optional TTL dead-man's switch)\n$client-\u003esetServiceChaos('payments.internal:8443', [\n    'errorStatus' =\u003e 503,\n    'errorProbability' =\u003e 0.3,\n    'latency' =\u003e ['timeUnit' =\u003e 'MILLISECONDS', 'value' =\u003e 200],\n], 60000);\n\n// SLO verdict (requires sloTrackingEnabled=true). FAIL raises VerificationException;\n// PASS / INCONCLUSIVE return the verdict array.\n$verdict = $client-\u003everifySlo([\n    'name' =\u003e 'checkout-slo',\n    'window' =\u003e ['type' =\u003e 'LOOKBACK', 'lookbackMillis' =\u003e 60000],\n    'minimumSampleCount' =\u003e 20,\n    'objectives' =\u003e [\n        ['sli' =\u003e 'LATENCY_P95', 'comparator' =\u003e 'LESS_THAN', 'threshold' =\u003e 250.0],\n        ['sli' =\u003e 'ERROR_RATE', 'comparator' =\u003e 'LESS_THAN_OR_EQUAL', 'threshold' =\u003e 0.01],\n    ],\n]);\n\n// Preemption — cordon and drain the server (Kubernetes node drain / spot reclaim)\n$client-\u003esetPreemption(['mode' =\u003e 'both', 'drainMillis' =\u003e 10000, 'ttlMillis' =\u003e 60000]);\n$client-\u003epreemptionStatus();   // GET\n$client-\u003eclearPreemption();    // DELETE — uncordon\n\n// Scheduled multi-stage chaos experiment\n$client-\u003estartChaosExperiment([\n    'name' =\u003e 'gradual-degradation',\n    'stages' =\u003e [\n        ['durationMillis' =\u003e 10000, 'profiles' =\u003e ['api.example.com' =\u003e ['errorStatus' =\u003e 500, 'errorProbability' =\u003e 0.1]]],\n        ['durationMillis' =\u003e 10000, 'profiles' =\u003e ['api.example.com' =\u003e ['errorStatus' =\u003e 500, 'errorProbability' =\u003e 0.5]]],\n    ],\n]);\n```\n\n## Mocking LLM APIs\n\nFluent builders under the `MockServer\\Llm` namespace mock provider-agnostic LLM\ncompletions, embeddings, multi-turn conversations and provider failover. The\nwire JSON they produce is identical to the Java, Node and Python clients, so a\nmock scripted from PHP behaves the same as one scripted anywhere else.\n\n```php\n\u003c?php\n\nuse MockServer\\Llm\\Completion;\nuse MockServer\\Llm\\IsolationSource;\nuse MockServer\\Llm\\LlmConversationBuilder;\nuse MockServer\\Llm\\LlmFailoverBuilder;\nuse MockServer\\Llm\\LlmMockBuilder;\nuse MockServer\\Llm\\Provider;\nuse MockServer\\Llm\\Role;\nuse MockServer\\Llm\\Usage;\n\n// Single completion mock\nLlmMockBuilder::llmMock('/v1/chat/completions')\n    -\u003ewithProvider(Provider::OPENAI)\n    -\u003ewithModel('gpt-4o')\n    -\u003erespondingWith(\n        Completion::completion()\n            -\u003ewithText('Hello from a mocked model!')\n            -\u003ewithStopReason('stop')\n            -\u003ewithUsage(Usage::usage()-\u003ewithInputTokens(12)-\u003ewithOutputTokens(8))\n    )\n    -\u003eapplyTo($client);\n\n// Multi-turn conversation (advances MockServer scenario state per turn),\n// isolated per session by a request header\nLlmConversationBuilder::conversation()\n    -\u003ewithPath('/v1/chat/completions')\n    -\u003ewithProvider(Provider::ANTHROPIC)\n    -\u003eisolateBy(IsolationSource::header('x-session-id'))\n    -\u003eturn()\n        -\u003ewhenLatestMessageRole(Role::USER)\n        -\u003erespondingWith(Completion::completion()-\u003ewithText('Hi! How can I help?'))\n    -\u003eturn()\n        -\u003erespondingWith(Completion::completion()-\u003ewithText('Goodbye!'))\n    -\u003eapplyTo($client);\n\n// Provider failover: fail twice, then succeed (consecutive identical failures\n// are coalesced; default JSON error bodies are supplied per status code)\nLlmFailoverBuilder::llmFailover()\n    -\u003ewithPath('/v1/chat/completions')\n    -\u003ewithProvider(Provider::OPENAI)\n    -\u003efailWith(429)\n    -\u003efailWith(503, 2)\n    -\u003ethenRespondWith(Completion::completion()-\u003ewithText('Recovered'))\n    -\u003eapplyTo($client);\n```\n\n## Mocking MCP Servers\n\n`MockServer\\Mcp\\McpMockBuilder` emulates a Streamable-HTTP MCP (Model Context\nProtocol) server speaking JSON-RPC 2.0. It generates the full set of\nexpectations — `initialize`, `ping`, `notifications/initialized`, plus\n`tools/list` + `tools/call`, `resources/list` + `resources/read` and\n`prompts/list` + `prompts/get` for any tools, resources and prompts declared.\n\n```php\n\u003c?php\n\nuse MockServer\\Mcp\\McpMockBuilder;\nuse MockServer\\Llm\\Role;\n\nMcpMockBuilder::mcpMock('/mcp')\n    -\u003ewithServerName('WeatherServer')\n    -\u003ewithTool('get_weather')\n        -\u003ewithDescription('Get the weather for a city')\n        -\u003ewithInputSchema('{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}}}')\n        -\u003erespondingWith('72F and sunny')\n    -\u003eand()\n    -\u003ewithResource('file:///config.json')\n        -\u003ewithName('config')\n        -\u003ewithMimeType('application/json')\n        -\u003ewithContent('{\"debug\":true}')\n    -\u003eand()\n    -\u003ewithPrompt('greeting')\n        -\u003ewithArgument('name', 'Who to greet', true)\n        -\u003erespondingWith(Role::ASSISTANT, 'Hello there!')\n    -\u003eand()\n    -\u003eapplyTo($client);\n```\n\nEvery builder also exposes `build()` to obtain the raw `Expectation`\nobject(s) without registering them (a single `Expectation` for `llmMock`,\nor an array of `Expectation` for conversations, failover and MCP).\n\n## Start / Launch MockServer\n\nThe PHP client does not include a binary launcher (PHP lacks the native WebSocket and subprocess management required for embedded launch). To start MockServer, use one of the following approaches:\n\n- **Docker:** `docker run -d -p 1080:1080 mockserver/mockserver`\n- **Executable JAR:** `java -jar mockserver-netty-no-dependencies-\u003cversion\u003e.jar -serverPort 1080`\n- **Homebrew:** `brew install mockserver \u0026\u0026 mockserver -serverPort 1080`\n- **Another client's launcher:** The Node, Python, Ruby, Go, .NET, and Rust clients can each download and launch MockServer automatically without Java or Docker.\n\nSee the [Running MockServer](https://www.mock-server.com/mock_server/running_mock_server.html) documentation for all available options.\n\n## Building\n\n```bash\ncomposer install\n```\n\n## Using in tests (PHPUnit)\n\n`MockServer\\Testing\\MockServerTestTrait` provides a `MockServerClient` and resets\nthe server before and after each test, so recorded requests, expectations and\nlogs never leak between tests. Mix it into your PHPUnit test case and call the\nlifecycle helpers from `setUp()` / `tearDown()`:\n\n```php\nuse MockServer\\Testing\\MockServerTestTrait;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class MyTest extends TestCase\n{\n    use MockServerTestTrait;\n\n    protected function setUp(): void    { $this-\u003esetUpMockServer(); }\n    protected function tearDown(): void { $this-\u003etearDownMockServer(); }\n\n    public function testSomething(): void\n    {\n        // $this-\u003emockServer is a reset MockServerClient ready to use\n        $this-\u003emockServer-\u003ereset();\n    }\n}\n```\n\nThe server URL is read from the `MOCKSERVER_URL` environment variable (for\nexample `http://localhost:1080`); when it is unset the test is skipped.\n\n## Running Tests\n\nUnit tests (no server required):\n\n```bash\nvendor/bin/phpunit --testsuite Unit\n```\n\nIntegration tests (requires a running MockServer):\n\n```bash\nMOCKSERVER_URL=http://localhost:1080 vendor/bin/phpunit --testsuite Integration\n```\n\n## License\n\nApache 2.0 - see [LICENSE](../LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmock-server%2Fmockserver-client-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmock-server%2Fmockserver-client-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmock-server%2Fmockserver-client-php/lists"}