Back

Meilisearch PHP v2 (beta) release notes

February 20, 2026

This article summarize all changes coming to the meilisearch-php v2 SDK.

This page lists changes from v1.x to v2.0.0-beta.6.

Highlights

This release focuses on improving the developer experience for SDK users by integrating the newest PHP features. We're updating the minimum requirements to PHP 8.1.

  • Bump requirements to PHP 8.1
  • Improve Tasks, Keys, Stats, and Version APIs with types
  • Improve type safety by adding typehints, and moving from array access to objects with getters
  • Streamline exceptions types (remove wrapping of native exceptions, add types to SDK exceptions)
  • Implement ArrayAccess on Tasks for Symfony and Laravel backward compatibility

Meilisearch engine features are integrated into the v2.x branch by default, but we will backport major features and security updates on request. If we missed something, please open an issue to request backporting a feature.

Migration guide

Ensure you are running PHP 8.1+ and upgrade your meilisearch-php dependency:

composer require meilisearch/meilisearch-php:2.0.0-beta.6

🟠 Task APIs now return typed Task and TaskResults objects

Impact: moderate

Task APIs no longer expose array-based responses. They now return typed Task and TaskResults objects, with enums for task status and type.

Update code that reads task results, waits for tasks, or compares task status/type values.

Task lists now return TaskResults

// Before (v1.x)
$tasks = $client->getTasks();
$results = $tasks['results']; // Accessing as array

// After (v2.x)
$tasks = $client->getTasks(); // Returns TaskResults
$results = $tasks->getResults(); // Returns an array of Task objects

This also applies to $index->getTasks(). The internal task manager all() method now returns an array of Task objects.

Task promises can be awaited directly

// Before (v1.x)
$promise = $this->client->createIndex('new-index', ['primaryKey' => 'objectID']);
$this->index->waitForTask($promise['taskUid']);

// After (v2.x)
$this->client->createIndex('new-index', ['primaryKey' => 'objectID'])->wait();

Task status now uses TaskStatus

// Before (v1.x)
$task = $this->client->getTask($taskUid);
if ($task['status'] === 'succeeded') {
    // do something
}

// After (v2.x)
$task = $this->client->getTask($taskUid);
if ($task->getStatus() === TaskStatus::Succeeded) {
    // do something
}

Task type now uses TaskType

// Before (v1.x)
$task = $this->client->getTask($taskUid);
if ($task['type'] === 'indexCreation') {
    // do something
}

// After (v2.x)
$task = $this->client->getTask($taskUid);
if ($task->getType() === TaskType::IndexCreation) {
    // do something
}

🟠 Keys APIsnow use typed query and result objects

Impact: moderate, introduced in v2.0.0-beta.6

API key APIs no longer accept array-based write payloads or return plain list shapes. They now use CreateKeyQuery and UpdateKeyQuery for requests, KeysResults for lists, and KeyAction for actions.

Update code that creates, updates, lists, or counts API keys.

Create keys with CreateKeyQuery

// Before (v1.x)
$key = $client->createKey([
    'description' => 'tenant token key',
    'actions'     => ['*'],
    'indexes'     => ['tenant*'],
    'expiresAt'   => '2055-10-02T00:00:00Z',
]);

// After (v2.x)
$key = $client->createKey(new CreateKeyQuery(
    actions:    [KeyAction::Any],
    indexes:    ['tenant*'],
    description: 'tenant token key',
    expiresAt:   new DateTimeImmutable('2055-10-02T00:00:00Z'),
));

Update keys with UpdateKeyQuery

// Before (v1.x)
$updated = $client->updateKey($key->getKey(), [
    'description' => 'new description',
]);

// After (v2.x)
$updated = $client->updateKey(new UpdateKeyQuery(
    keyOrUid:    $key->getKey(),   // or $key->getUid()
    name:        'task_manager',
    description: 'manages tasks',
));

You can clear the key name or description by passing null.

List keys with KeysResults

// Before (v1.x)
$keys = $client->getKeys();
foreach ($keys as $k) {
    echo $k->getUid();
}

// After (v2.x)
$keysResults = $client->getKeys();
foreach ($keysResults->getResults() as $k) {
    echo $k->getUid();
}

KeysResults::count() was removed. Use count($keysResults) or count($keysResults->getResults()).

🟢 Remove custom exception for JSON parsing

Impact: minor

Before: catching SDK-specific JSON exceptions

<?php

try {
    $client->index('books')->addDocuments([["title" => "\xB1\x31"]]);
} catch (JsonEncodingException | JsonDecodingException $e) {
    // handle JSON errors
}

After: catching native PHP JsonException

<?php

try {
    $client->index('books')->addDocuments([["title" => "\xB1\x31"]]);
} catch (\JsonException $e) {
    // handle JSON errors
}

🟢 Database stats now return a typed Stats object

Impact: minor

Stats APIs no longer return array-based responses. They now return a typed Stats object.

Update code that reads stats fields to use getter methods.

$stats = $client->stats();

// Before (v1.x) - accessing stats as array
$databaseSize = $response['databaseSize'];
$usedDatabaseSize = $response['usedDatabaseSize'];
// etc...

// After (v2.x) - use getter methods
$databaseSize = $stats->getDatabaseSize();
$usedDatabaseSize = $stats->getUsedDatabaseSize();

🟢 Version now returns a typed Version object

Impact: minor

Version APIs no longer return array-based responses. They now return a typed Version object.

Update code that reads version fields to use getter methods.

$version = $client->version();

// Before (v1.x) - access as array
$commitSha = $version['commitSha'];
$commitDate = $version['commitDate']; // String
$pkgVersion = $version['pkgVersion'];

// After (v2.x) - use getter methods
$commitSha = $version->getCommitSha();
$commitDate = $version->getCommitDate(); // \DateTimeImmutable
$pkgVersion = $version->getPkgVersion();

🟢 Removed legacy MeiliSearch namespace

Impact: minor

We removed MeiliSearch (capital S) namespace.

// Before (v1.x) - MeiliSearch with capital S
use MeiliSearch\Client;

// After (v2.x) - Meilisearch without capital S
use Meilisearch\Client;

🟢 Removed Indexes::parseDate() public method

Impact: minor

This only affects you if you were using this utility method directly.

// Before (v1.x) - If you used this method in your code
$dateTime = \Meilisearch\Endpoints\Indexes::parseDate('2021-01-01T01:23:45.123456Z');

// After (v2.x) - Use native PHP instead
$dateTime = new \DateTimeImmutable('2021-01-01T01:23:45.123456Z');

🟢 Improved exception types DX

Impact: minor

The custom $message property has been removed from custom exceptions. Use the getMessage() method instead:

try {
    // ...
} catch (\Meilisearch\Exceptions\ApiException $e) {
   // Before (v1.x)
    echo $e->message;
    // After (v2.x)
    echo $e->getMessage();
}

🟢 Type-safety improvements

Impact: minor

These changes improve IDE/static-analysis support and usually do not require migration work, unless you extend SDK internals.

The PHPStan type hints are now more precise for all API requests (Query classes) and responses (Results classes).

The following classes are now final:

  • TaskResults
  • SimilarDocumentsQuery
  • FacetSearchResult
  • SearchResult
  • Exception classes

The results classes now come with a getRaw() method:

  • SimilarDocumentsSearchResult
  • FacetSearchResult

Additionally, exception have been refactored:

  • Exception public properties are now read-only and strictly typed.
  • The rethrowWithHint static method now returns a \RuntimeException instead of a generic \Exception.

More information

You can read more by taking a look at the open-source repository:


Shout-out to @norkunas for his massive contributions to this release. 💙