Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Elasticsearch Thrift Php Laravel Package

munkie/elasticsearch-thrift-php

A PHP Elasticsearch client that talks to the cluster over the Thrift protocol instead of HTTP. It lets PHP applications index, search, and manage data through the Thrift-generated API, making it useful for legacy Elasticsearch setups that still expose Thrift endpoints.

View on GitHub
Deep Wiki
Context7

Getting Started

munkie/elasticsearch-thrift-php is not an Elasticsearch client in the conventional sense — it is a collection of Thrift-generated PHP classes for the long-deprecated elasticsearch-transport-thrift plugin. You don't get a query DSL builder, connection pooling, retries, or a Laravel facade. You open a raw Thrift socket (default :9500), send a RestRequest that mimics an HTTP call, and receive a RestResponse. That's the whole API.

Verify the server side before writing a line of code. The Thrift transport has not shipped with Elasticsearch since the 2.0 era. This package is only viable if you are pinned to an ES 1.x cluster that already has the elasticsearch-transport-thrift plugin installed and listening. If you're on ES 2.x+, OpenSearch, Elastic Cloud, or AWS OpenSearch, stop — the plugin is gone and this package cannot help you. Use elasticsearch/elasticsearch instead.

Assuming you genuinely have an ES 1.x node with the plugin:

composer require munkie/elasticsearch-thrift-php
composer require apache/thrift   # or enable ext-thrift in php.ini

Where to look first, in this order:

  1. src/Elasticsearch/Thrift/RestClient.php — the class you instantiate. Every operation funnels through execute().
  2. src/Elasticsearch/Thrift/RestRequest.php and RestResponse.php — read the property lists. Those DTOs are the API surface.
  3. src/Elasticsearch/Thrift/Method.php — verb constants (Method::GET, Method::POST, Method::PUT, Method::DELETE).
  4. The generated *_types.php / *_args.php files if you hit a TProtocolException — that almost always signals a version mismatch between the generated code and the installed plugin.

First use case — open a socket and ping the cluster:

use Elasticsearch\Thrift\RestClient;
use Elasticsearch\Thrift\RestRequest;
use Elasticsearch\Thrift\Method;
use Thrift\Transport\TSocket;
use Thrift\Transport\TBufferedTransport;
use Thrift\Protocol\TBinaryProtocol;

$socket = new TSocket(config('es-thrift.host'), config('es-thrift.port', 9500));
$socket->setRecvTimeout(5000);
$socket->setSendTimeout(5000);

$transport = new TBufferedTransport($socket);
$transport->open();

$client = new RestClient(new TBinaryProtocol($transport));

$req = new RestRequest();
$req->method = Method::GET;
$req->uri    = '/';
$req->params = [];

$res = $client->execute($req);
// $res->status, $res->body, $res->headers

A status of 200 and a JSON body means you have a working baseline. Everything else from here is building uri/params/body payloads by hand.

Implementation Patterns

Bind the client as a Laravel singleton

The transport is stateful and expensive to open per request. Open it once and reuse it — this mirrors how you'd wrap any low-level socket client.

// app/Providers/ElasticsearchThriftServiceProvider.php
public function register(): void
{
    $this->app->singleton(RestClient::class, function ($app) {
        $cfg = $app['config']->get('es-thrift');

        $socket = new TSocket($cfg['host'], $cfg['port']);
        $socket->setRecvTimeout($cfg['timeout']);
        $socket->setSendTimeout($cfg['timeout']);

        $transport = new TBufferedTransport($socket);
        $transport->open();

        return new RestClient(new TBinaryProtocol($transport));
    });
}

Pair it with a publishable config/es-thrift.php holding only host, port, and timeout. There is no scheme, no auth, no TLS — don't invent config keys the transport cannot honor.

Wrap it in a thin gateway — never call it from controllers

RestRequest is a dumb DTO, so it's dangerously easy to leak Thrift vocabulary into controllers, jobs, and models. Isolate it behind one adapter class and treat that adapter as the only place in your codebase allowed to use Elasticsearch\Thrift\....

class ThriftSearchGateway
{
    public function __construct(private RestClient $client) {}

    public function search(string $index, array $query, int $size = 10): array
    {
        $req = new RestRequest();
        $req->method = Method::POST;
        $req->uri    = "/{$index}/_search";
        $req->params = [];
        $req->body   = json_encode([
            'query' => $query,
            'size'  => $size,
        ]);

        $res = $this->client->execute($req);

        if ((int) $res->status >= 400) {
            throw new ThriftSearchException("ES {$res->status}: {$res->body}");
        }

        return json_decode($res->body, true, 512, JSON_THROW_ON_ERROR);
    }
}

The key fact your whole design hinges on: execute() does not throw on an ES 4xx/5xx. It returns a RestResponse with a non-200 status and you must inspect it. Centralizing that check in the gateway is the single highest-value thing you can do here.

Indexing via the bulk API

The Thrift transport sends an arbitrary body per call, so the NDJSON _bulk endpoint works — but the client gives you zero help. You must build the NDJSON yourself, enforce a trailing newline (ES 1.x is strict about it), and inspect per-item results inside the returned JSON.

public function bulk(string $index, array $docs): array
{
    $body = '';
    foreach ($docs as $doc) {
        $body .= json_encode(['index' => ['_index' => $index, '_type' => 'doc', '_id' => $doc['id'] ?? null]]) . "\n";
        $body .= json_encode($doc['source']) . "\n";
    }
    // trailing newline is already present from the last append

    $req = new RestRequest();
    $req->method = Method::POST;
    $req->uri    = '/_bulk';
    $req->params = [];
    $req->body   = $body;

    $res = $this->client->execute($req);
    $decoded = json_decode($res->body, true);

    if (!empty($decoded['errors'])) {
        // individual items failed; do not assume a 200 means success
        throw new ThriftBulkException($decoded['items']);
    }

    return $decoded;
}

Batch requests per job, not per row

Because each execute() is a synchronous socket round-trip with no pipelining, per-document indexing in a loop is slow. Batch inside a queued job and flush every N docs (a few hundred is typical for ES 1.x). Wrap the flush in a try/finally that closes and reopens the transport on socket errors, since a crashed Thrift connection will not recover on its own.

Health check + fallback

Add a /health proxy that performs a GET / via the gateway and returns 503 if it fails or status >= 400. This is what lets you fail over or alert when the legacy plugin node goes away. Combine with a circuit breaker around the gateway so a dead Thrift port doesn't cascade into timeouts across the app.

Gotchas and Tips

  • Plugin removed in ES 2.0. If your cluster upgraded, every call will fail with connection refused on :9500. Verify the plugin is actually loaded on every target node — it's often left out of newer AMIs or container images.
  • execute() never throws for ES-level errors. A 404/500 comes back as a normal RestResponse with status: 404/500. Check $res->status every single call. Missing this makes bugs invisible.
  • No retries, no backoff, no pool. A transient socket error becomes your exception. Wrap execute() in your own retry helper with backoff, or accept the failure loudly — do not silently swallow.
  • Sockets are not reentrant. A RestClient bound as a singleton will deadlock or throw if two code paths use it concurrently (e.g., queue workers sharing a process). Either bind it scoped or ensure each worker opens its own.
  • Version skew produces opaque errors. TProtocolException / TApplicationException during execute() almost always means the generated Thrift classes and the installed plugin are on incompatible versions. Pin apache/thrift in composer.json and match it to the plugin version on the server — do not float.
  • Timeouts default to infinite. TSocket will happily block forever if you forget setRecvTimeout / setSendTimeout. Always set both; a hung Thrift call ties up a PHP-FPM worker.
  • ES 1.x strictness. Bulk NDJSON requires a trailing \n. Mappings and analyzers differ from ES 7+/OpenSearch; queries copied from modern docs will behave differently or be rejected.
  • No TLS, no auth. The Thrift transport has no encryption and no credential story. Keep the cluster on a private network. If you need auth, that's a signal to migrate off Thrift entirely.
  • Generated code is a maintenance liability. No dependents, no upstream activity, and no PHP 8 support signals. Isolate behind the gateway interface, add contract tests, and treat the whole package as replaceable — the intent should be to migrate to elasticsearch/elasticsearch (HTTP/JSON) as soon as the cluster can move.
  • Debugging tip: when you're unsure what URI/params/body a client sends, log the RestRequest fields before execute() and compare against what curl -XGET 'http://node:9200/index/_search?pretty' returns. The Thrift transport is just an HTTP shim over a binary socket, so the wire semantics are the same.
  • Extension point: there isn't really one. If you need middleware, tracing, or metrics, put them around the gateway — do not subclass RestClient. The generated code is meant to be treated as a black box.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel