elasticsearch-php: the official PHP client, and what changed in 9.x
Official PHP client for Elasticsearch.
At a glance
- What is it?
- The official PHP client for Elasticsearch covers 500+ endpoints and drops Guzzle in favour of PSR-18. Here is how it installs, how it is mocked in tests, and where it stops being the right tool.
- Who is it for?
- Adopt elasticsearch-php when your application is PHP and your search backend is Elasticsearch 9.x, because the version pairing is the whole point: a 9.x client talks to a 9.x server, and the client is released alongside the server.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- elastic/elasticsearch-php5,331 ★ · -6
| Repository | 2026-09-23 | 2026-11-13 |
|---|---|---|
| elastic/elasticsearch-php | 5.3k | 5.3k |
-6 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What elasticsearch-php is for, and who ends up using it
Elasticsearch speaks HTTP and JSON, so in principle any PHP code that can issue a request can talk to it. The official client exists because doing that by hand means rebuilding the same scaffolding for every call: constructing URLs for 500+ endpoints, serialising request bodies, decoding responses, and handling the HTTP transport. elasticsearch-php is the layer that owns that work so application code can call a method instead.
The audience is narrower than "PHP developers". You need a PHP application and an Elasticsearch cluster you intend to query from inside that application. The repository's own examples directory contains a single file, examples/dense_vector_benchmark.php, which is a fair signal of where the project's attention sits: dense vector search, not general web tutorials. Teams writing Symfony or Laravel services that need full-text search, aggregations or vector retrieval behind a PHP API are the natural fit. If your search layer is a separate service in another language, this client gives you nothing.
The 9.x transport change: PSR-18 instead of Guzzle
The most consequential architectural fact in the current release line is that Guzzle is gone from the dependency tree. The README states that 9.0.0 keeps the same architecture as 8.x, supporting PSR-7 for HTTP messages and PSR-18 for HTTP client communication, and that the Guzzle dependency was removed so that the built-in cURL-based HTTP client is used by default when no other PSR-18 compatible client is detected.
That ordering matters. The client does not simply bundle cURL and ignore your stack; it prefers a PSR-18 implementation it can find. This is what makes the mocking story work. Because the transport is an interface, you mock a PSR-18 client rather than the Elasticsearch client itself, and the README demonstrates this with php-http/mock-client and Nyholm\Psr7\Response. You get a real client object, a real request pipeline, and a fake transport underneath.
The trade-off is that transport behaviour is now partly someone else's decision. If you install a PSR-18 client for unrelated reasons, elasticsearch-php may pick it up, and its timeout, retry and connection-pooling behaviour becomes your behaviour. Teams with strict HTTP policy requirements should verify that before upgrading.
Installing the client and running a first search
The README does not inline install commands. It points to the Installation section of the getting started documentation, and the package is published on Packagist as elasticsearch/elasticsearch. The standard Composer invocation is therefore:
composer require elasticsearch/elasticsearchTo get a server to point it at, the README gives a one-line local setup that runs Elasticsearch and Kibana on your machine, or you can sign up for an Elastic Cloud trial:
curl -fsSL https://elastic.co/start-local | shConnecting is also delegated to the documentation's Connecting section, so the README itself shows no connection snippet. What it does show is the mock path, which is the most complete code example in the repository README. It builds a client with an injected PSR-18 transport:
use Elastic\Elasticsearch\ClientBuilder;
use Http\Mock\Client;
use Nyholm\Psr7\Response;
$mock = new Client();
$client = ClientBuilder::create()
->setHttpClient($mock)
->build();After build() returns, you hold a client whose transport is the mock. The README then constructs a PSR-7 Response with status 200 and hands it to the mock, so the next call through the client returns that response instead of hitting a network. That is the pattern to copy in unit tests: assert on the request your code produced, not on a live cluster.
For real usage, the README lists the basic operations it documents in the getting started guide: creating an index, indexing a document, getting a document, searching, updating, deleting a document, and deleting an index. Those are the entry points to read next.
Version pairing is the constraint that catches people out
The README is explicit that this client is versioned and released alongside the Elasticsearch server, and the compatibility table lists main/main, 9.x/9.x and 8.x/8.x. The stated rule is to use the most recent version of the library within the major version of the corresponding implementation: for Elasticsearch 8.16, use 8.16 of the library or above, but not 9.0.
Forward compatibility is defined narrowly and it is worth reading twice. Clients can talk to greater or equal minor versions of Elasticsearch without breaking, but the client does not automatically gain support for new server features. A 8.12 client will not support what 8.13 added; you need the 8.13 client for that. Backwards compatibility, in the other direction, is only offered against default distributions and without guarantees.
In practice this makes the client upgrade a follower of the server upgrade, not a substitute for it. If your platform team rolls the cluster forward, your Composer constraint has to move with it, and the 9.x line is where the merged serverless client and the removal of the Guzzle dependency land, although the README says 9.0.0 keeps the same architecture as 8.x.
Serverless endpoints and the 410 you will hit
The elastic/elasticsearch-serverless client is being deprecated, and its functionality has been merged into this client. The README says this should have zero impact on default behaviour, and the mechanism is a phpdoc attribute: if an endpoint is available in serverless, the PHP function carries @group serverless.
If you call an endpoint that is not available in serverless mode, you get a 410 HTTP error with the message "this endpoint exists but is not available when running in serverless mode". That is a clean failure, but it is a runtime failure, and it will not show up in a test suite that mocks the transport unless you mock the 410 too.
Detection is the interesting part. The client recognises a serverless instance when the URL is managed by Elastic, for example a *.elastic.cloud host. Behind a proxy it cannot know from the hostname, so it infers serverless from the first response. If you need it decided up front, Client::setServerless(true) sets the flag explicitly, and the README notes the default is false. Anyone running a proxy in front of a serverless cluster should set that flag rather than rely on inference, because the first request otherwise goes out under the wrong assumption.
Where elasticsearch-php is the wrong tool
The client is a thin, faithful wrapper over the REST API, and that is the limitation.
It is also a poor fit for interactive work. The README's own contents list includes a section on mocking, and the repository ships phpstan.neon plus several phpunit configuration files (phpunit.xml.dist, phpunit-integration-tests.xml, phpunit-integration-cloud-tests.xml, phpunit-yaml-stack-tests.xml, phpunit-yaml-serverless-tests.xml). That layout tells you the maintainers test against real clusters and against YAML-defined specs. It tells you nothing about a GUI, because there is not one. If you want to poke at a cluster by hand, use Kibana or curl.
Finally, if your PHP application only needs to hand a query to Elasticsearch and return the raw JSON to a browser, the client is overhead. You are paying for a dependency, a transport abstraction and a version-pairing obligation to get back what a single HTTP call would have given you.
Alternatives and how they differ in approach
The obvious alternative is to skip the client and issue HTTP requests directly with whatever HTTP library your framework already provides. The difference is not speed, it is coverage. A hand-rolled integration means you write the URL construction and response decoding for each endpoint you use, and you own the retry and error handling. The client gives you 500+ endpoints and a PSR-18 transport you can swap. If you use three endpoints and never plan to use more, hand-rolling is defensible. If you plan to use aggregations, bulk indexing, index templates and vector search, you are reimplementing the client badly.
Within the PHP ecosystem, the other realistic path is a framework-specific integration layer that wraps this client and adds model mapping, such as the Elasticsearch packages maintained for Laravel and Symfony. The trade-off is that you inherit the wrapper's release cadence and its abstraction, and when an endpoint is not exposed through the wrapper you drop back to the underlying client anyway.
Maintenance, licence and what a 9.x upgrade actually costs
The repository is not archived, and the last push was on 2026-11-02. Releases in the 9.x line have arrived at a steady cadence: v9.3.0 on 2026-02-04, v9.4.0 on 2026-05-06, v9.5.0 on 2026-08-04. The versioning policy ties those releases to Elasticsearch server versions, so the client's release schedule is a function of the server's, not an independent roadmap.
The upgrade cost is concentrated in one release. Moving from 8.x to 9.x means losing the Guzzle dependency in favour of the built-in PSR-18 cURL client by default, which can affect any code that reached into Guzzle directly, and it means accounting for the merged serverless client. The README points to a BREAKING_CHANGES.md file at the repository root for the details; that file is the document to read before planning the upgrade, not this article. The README itself says the maintainers tried to avoid BC breaks for 9.x, which is a statement of intent rather than a guarantee.
Licensing is MIT, and the repository carries both a LICENSE and a NOTICE file. MIT is permissive: it allows commercial and closed-source use with attribution and without copyleft obligations on your application. That covers the client. It does not cover Elasticsearch itself, which is distributed under a different licence with its own terms, and it does not cover Elastic Cloud, which is a paid service with a free trial. Teams that treat "the PHP client is MIT" as a statement about the whole stack are reading the wrong file. This is a description of the licence identifiers in the repository, not legal advice; check the LICENSE and NOTICE files and your own counsel for anything that matters.
Editorial conclusion
Adopt elasticsearch-php when your application is PHP and your search backend is Elasticsearch 9.x, because the version pairing is the whole point: a 9.x client talks to a 9.x server, and the client is released alongside the server. Do not adopt the 9.x line if you are running an Elasticsearch 8.x cluster and cannot upgrade the server, because the README's compatibility table pairs 8.x with the 8.x branch and the 9.x line is the one that merged serverless support and replaced the Guzzle dependency. Do not adopt it as a general-purpose HTTP layer either; it is a client for one product. Before you commit, check three things: that your PHP version is a currently maintained one, that the endpoint you need is marked @group serverless if you run against a serverless instance, and that your PSR-18 client of choice is actually detected, because the built-in cURL client is only the fallback when none is found.
Frequently asked questions
How do I install the elasticsearch-php client?
The README points to the Installation section of the getting started documentation rather than inlining commands. The package is published on Packagist as elasticsearch/elasticsearch, so the usual Composer require against that package name is the install path.
Which version of elasticsearch-php should I use with my Elasticsearch server?
The README's compatibility table pairs 9.x with 9.x and 8.x with 8.x, and states that for Elasticsearch 8.16 you should use 8.16 of the library or above but not 9.0. Clients are forward compatible with greater or equal minor versions, but they do not automatically support new server features without a new client release.
How do I mock the Elasticsearch client in PHP tests?
The README says to mock a PSR-18 HTTP client instead of the Elasticsearch client. It shows ClientBuilder::create()->setHttpClient($mock)->build() using php-http/mock-client, then returning a PSR-7 response from Nyholm\Psr7\Response.
Does elasticsearch-php still depend on Guzzle?
No. The README states that the Guzzle dependency was removed in 9.0.0 and that a built-in cURL-based HTTP client is used by default when no other PSR-18 compatible client is detected.
Why do I get a 410 error from an endpoint that exists?
The README explains that endpoints unavailable in serverless mode return a 410 with the message that the endpoint exists but is not available when running in serverless mode. Endpoints available in serverless carry a @group serverless phpdoc attribute.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/elastic-elasticsearch-php)Star history chart for your README
[](https://hysenlabs.com/projects/elastic-elasticsearch-php)Was this useful?