meilisearch-php icon indicating copy to clipboard operation
meilisearch-php copied to clipboard

Bump to PHP 8.1, improve type safety for Tasks

Open norkunas opened this issue 8 months ago β€’ 24 comments

Pull Request

Related issue

Fixes #703 Closes #739

What does this PR do?

  • Bumps PHP requirement to 8.1;
  • Adds missing types where there were none at all, or was impossible to use because of unions;
  • Adds Task object for type safety to be returned instead of undocumented array shapes;
  • Fully enables trailing_comma_in_multiline cs fixer rule;
  • Disables php_unit_strict cs fixer rule, because it breaks tests;

~~This introduces a BC break for someone who is extending meilisearch classes, but not for those who consumes the data, as Task object was added implementing \ArrayAccess, so all raw data can still be accessed.~~

This introduces a BC break for task data access and for someone who is extending meilisearch classes.

Migration guide

Before

$promise = $this->client->createIndex('new-index', ['primaryKey' => 'objectID']);

$this->index->waitForTask($promise['taskUid']);

After

$task = $this->client->createIndex('new-index', ['primaryKey' => 'objectID']);

$this->index->waitForTask($task->getTaskUid());

Before

$task = $this->client->getTask($taskUid);

if ($task['status'] === 'succeeded') {
    // snip...
}

After

use MeiliSearch\Contracts\TaskStatus;

$task = $this->client->getTask($taskUid);

if ($task->getStatus() instanceof TaskStatus::Succeeded) {
    // snip...
}

Before

$task = $this->client->getTask($taskUid);

if ($task['type'] === 'indexCreation') {
    // snip...
}

After

use MeiliSearch\Contracts\TaskType;

$task = $this->client->getTask($taskUid);

if ($task->getType() instanceof TaskType::IndexCreation) {
    // snip...
}

PR checklist

Please check if your PR fulfills the following requirements:

  • [x] Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)?
  • [x] Have you read the contributing guidelines?
  • [x] Have you made sure that the title is accurate and descriptive of the changes?

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive Task object with detailed status and type enums and multiple task detail classes for clearer task management.
    • Added a new partial function for partial application of callables.
    • Added extensive tests for the Task object and pagination settings.
  • Breaking Changes

    • Raised minimum PHP version requirement to 8.1.
    • Updated many endpoints to return Task objects instead of arrays for asynchronous operations (documents, indexes, settings, dumps, snapshots, tasks).
    • Enforced stricter and more explicit type declarations across the codebase.
  • Improvements

    • Enhanced type safety with explicit parameter and return types.
    • Streamlined exception classes with clearer type annotations.
    • Simplified and unified method signatures and return types for better consistency.
    • Updated test code to work with Task objects instead of raw arrays, improving clarity and type safety.
    • Simplified asynchronous task handling in tests by directly awaiting Task objects.
    • Improved internal code consistency by removing deprecated or unused methods and variables.
  • Bug Fixes

    • Improved reliability and consistency of method signatures and expected outputs.
  • Tests

    • Expanded and refined test coverage reflecting new Task object usage.
    • Removed redundant assertions and outdated test methods.
    • Updated tests to use method calls on Task objects rather than array access.
    • Corrected test method names for typos and improved test clarity.
  • Chores

    • Updated CI workflows to run tests only on PHP 8.1 and above.
    • Updated coding style configurations and dependency version constraints.
    • Reformatted PHPUnit configuration for detailed error and deprecation reporting.

norkunas avatar Apr 09 '25 12:04 norkunas

Codecov Report

Attention: Patch coverage is 97.15100% with 10 lines in your changes missing coverage. Please review.

Project coverage is 90.40%. Comparing base (368c73f) to head (cfed046). Report is 73 commits behind head on main.

Files with missing lines Patch % Lines
src/Contracts/TaskDetails/DumpCreationDetails.php 0.00% 6 Missing :warning:
src/Endpoints/Tasks.php 80.00% 2 Missing :warning:
src/Endpoints/Delegates/HandlesTasks.php 66.66% 1 Missing :warning:
src/Search/FacetSearchResult.php 0.00% 1 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #735      +/-   ##
==========================================
- Coverage   90.74%   90.40%   -0.34%     
==========================================
  Files          52       73      +21     
  Lines        1340     1595     +255     
==========================================
+ Hits         1216     1442     +226     
- Misses        124      153      +29     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov[bot] avatar Apr 10 '25 03:04 codecov[bot]

YAY!

If these are to be deprecated, are they going to be replaced with direct access to the object?

    public function getData(): array
    {
        return $this->data;
    }

    // @todo: deprecate
    public function offsetExists(mixed $offset): bool
    {
        return \array_key_exists($offset, $this->data);
    }

    // @todo: deprecate
    public function offsetGet(mixed $offset): mixed
    {
        return $this->data[$offset] ?? null;
    }

    // @todo: deprecate
    public function offsetSet(mixed $offset, mixed $value): void
    {
        throw new \LogicException(\sprintf('Setting data on "%s::%s" is not supported.', get_debug_type($this), $offset));
    }

    // @todo: deprecate
    public function offsetUnset(mixed $offset): void
    {
        throw new \LogicException(\sprintf('Unsetting data on "%s::%s" is not supported.', get_debug_type($this), $offset));
    }

tacman avatar Apr 10 '25 09:04 tacman

@tacman if this PR will get merged, then you'll get access to both ways: $task->getError() or $task['error'] but imho previous way should be deprecated, because it does not provide types

norkunas avatar Apr 10 '25 09:04 norkunas

That makes sense. I imagine there will be other things in 2.0 as well.

tacman avatar Apr 10 '25 10:04 tacman

Hi, thanks for your PR.

Do we want to release this in a v2.x?

Can you add a "migration guide" to your first comment to highlight the required implementation changes?

Strift avatar Apr 14 '25 02:04 Strift

@Strift

Do we want to release this in a v2.x?

Based on semver, it should be in v2.x, but if you want to be in line with meilisearch/meilisearch repository, I think it's reasonable to release this in v1.

Can you add a "migration guide" to your first comment to highlight the required implementation changes?

Can you link to some previous comment where I could see how this looks like?

norkunas avatar Apr 14 '25 04:04 norkunas

Hi @norkunas, no need to follow a specific format. A simple before/after of the relevant parts of the code is enough. Here's an example for meilisearch JS: https://github.com/meilisearch/meilisearch-js/releases/tag/v0.48.0

It's mainly so I have examples to refer to when writing the release changelog. Double-checking with the actual code changes is better, but the code samples are a good resource too for me, especially if I write the changelog a long time after reviewing the PR.

Strift avatar Apr 14 '25 06:04 Strift

@Strift I've updated description, is it ok for you? Also it should be decided if we deprecate the array access or not.

norkunas avatar Apr 14 '25 07:04 norkunas

I also have a side idea for another improvement: Add wait(int $timeoutInMs, int $intervalInMs) method to task. So instead of:

$task = $this->client->createIndex('new-index', ['primaryKey' => 'objectID']);
$task = $this->index->waitForTask($task->getTaskUid());

we could write:

$task = $this->client->createIndex('new-index', ['primaryKey' => 'objectID'])->wait();

But not sure, as then we'd need for this method to work at least inject in in the Task object the Meilisearch\Endpoints\Tasks endpoint, or the http client, or the meilisearch client itself..

norkunas avatar Apr 14 '25 07:04 norkunas

Hello here! Thank you for your PR @norkunas! With @brunoocasali and the PHP community, we originally chose to make the PHP SDK follow the same version number as Meilisearch's one Here, if we merge this branch, we will break this useful convention forever. Is it what we want?

curquiza avatar Apr 15 '25 08:04 curquiza

Hi, I'm for merging to v1 :) I think this is an acceptable BC break to provide type safety

norkunas avatar Apr 15 '25 08:04 norkunas

I also think that it would be good to have typed task details based on https://www.meilisearch.com/docs/reference/api/tasks#details, because now it's always array<mixed>, so static analyzers will always complain.

norkunas avatar May 10 '25 21:05 norkunas

Regarding the other topics of discussion in this thread:

I also have a side idea for another improvement: Add wait(int $timeoutInMs, int $intervalInMs) method to task.

This could be a welcome addition, but I prefer having it in a separate PR to keep this one's scope minimal.

Strift avatar May 14 '25 04:05 Strift

Thanks @Strift for review, I'm currently preparing for details data to be typed.

norkunas avatar May 14 '25 04:05 norkunas

[!NOTE]

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Convert many async task return values from arrays to a strongly-typed, immutable Task object (with enums and a wait() helper), add TaskDetail types, broaden PHP 8.1+ typing across the codebase, remove legacy PHP matrix CI jobs, and update tests to use the new Task-based API.

Changes

Cohort / File(s) Change Summary
Workflows
.github/workflows/meilisearch-beta-tests.yml, .../pre-release-tests.yml, .../tests.yml
Remove PHP 7.4/8.0 from CI matrices, delete legacy PHP7 Guzzle job, and adjust Symfony-HttpClient exclusions.
Composer / tooling
composer.json, .php-cs-fixer.dist.php, phpunit.xml.dist
Raise PHP requirement to ^8.1, update dependencies, add files autoload entry, toggle php-cs-fixer options, and update PHPUnit config/display settings.
Core Task model & enums
src/Contracts/Task.php, src/Contracts/TaskStatus.php, src/Contracts/TaskType.php, src/Contracts/TaskError.php
Add immutable Task class with getters, wait() and fromArray(); add TaskStatus and TaskType enums; add TaskError.
Task details types
src/Contracts/TaskDetails.php, src/Contracts/TaskDetails/*.php
Add TaskDetails interface and multiple concrete detail classes (DocumentAdditionOrUpdateDetails, DocumentDeletionDetails, DocumentEditionDetails, DumpCreationDetails, IndexCreationDetails, IndexDeletionDetails, IndexSwapDetails, IndexUpdateDetails, SettingsUpdateDetails, TaskCancelationDetails, TaskDeletionDetails).
Endpoints β€” API return types
src/Endpoints/*.php, src/Endpoints/Delegates/*
Change many endpoint/mutator return types from array to Task (and list<Task> for batch ops); tighten parameter types (int
Tasks endpoint logic
src/Endpoints/Tasks.php, src/Endpoints/TasksResults.php
Make get/cancel/delete return Task, make waitTask static and Task-returning, map tasks results to Task instances.
Documents / Settings / Indexes changes
src/Endpoints/Delegates/HandlesDocuments.php, HandlesSettings.php, HandlesIndex.php, Indexes.php, Dumps.php, Snapshots.php, Batches.php, Keys.php, TenantToken.php
Convert document/settings/index/dump/snapshot operations to return Task objects; adjust signatures, PHPDoc and internal handling accordingly.
HTTP & serialization
src/Http/Client.php, src/Http/Serialize/*.php, src/Contracts/Http.php
Small refactors (trailing commas, query empty check), add stronger serialize/unserialize type hints, type post/put/patch $body as mixed.
Contracts & misc typing
src/Contracts/Data.php, src/Contracts/SimilarDocumentsQuery.php
Add property and method return typing (e.g., array, mixed), tighten constructor/props.
Exceptions & new LogicException
src/Exceptions/*.php, src/Exceptions/LogicException.php
Add return/type declarations, remove obsolete factory, and add new LogicException implementing ExceptionInterface.
Helpers
src/functions.php
Add internal partial() helper for partial application.
Search JSON output
src/Search/SearchResult.php, src/Search/FacetSearchResult.php
Use JSON_THROW_ON_ERROR when encoding to raise JsonException on failure.
Tests β€” Task usage
tests/Contracts/TaskTest.php, tests/MockTask.php, tests/**/
Add Task tests and MockTask; update many tests to expect Task objects and use ->wait() chaining; remove legacy promise helpers and assertIsValidPromise.
Test harness
tests/TestCase.php
Remove promise validator, adjust setup/teardown to work with Task objects and inline waits.

Sequence Diagram(s)

%%{init: {"themeVariables":{"actorBackground":"#E6F7FF","actorBorder":"#7FCFE6","noteBackground":"#FFF4E6"}}}%%
sequenceDiagram
    participant Client
    participant Endpoint
    participant HTTP
    participant Task

    Client->>Endpoint: call mutating API (e.g., addDocuments)
    Endpoint->>HTTP: send HTTP request
    HTTP-->>Endpoint: response array (task data)
    Note right of Endpoint: wrap response into Task
    Endpoint->>Task: Task::fromArray(response, partial(Tasks::waitTask, $http))
    Endpoint-->>Client: return Task object
    Client->>Task: ->wait(timeout?, interval?)
    Task->>HTTP: poll task status via Tasks::waitTask
    HTTP-->>Task: updated task data
    Task-->>Client: resolved Task (finished or timed out)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas to focus during review:

  • Task::fromArray mapping and all TaskDetails factory conversions for edge-case/missing keys.
  • wait()/waitTask polling logic and timeout/error behavior (static vs. instance usage).
  • Backwards-compatibility surface: any public methods that changed return types (may be semver-impacting).
  • Tests that were adjusted β€” ensure mocks and MockTask create method match Task constructor and await semantics.
  • CI/workflow removals to ensure other dependent jobs or matrix entries are not expected elsewhere.

Possibly related PRs

  • meilisearch/meilisearch-php#758 β€” adjusts CI matrix and removes legacy PHP7 Guzzle job (closely related to workflow changes).
  • meilisearch/meilisearch-php#753 β€” adds disableOnNumbers typo-tolerance field and related tests (overlaps testing/type changes in typo-tolerance).
  • meilisearch/meilisearch-php#759 β€” modifies src/Contracts/Http.php method parameter typing (related to mixed body parameter changes).

Suggested labels

CI

Suggested reviewers

  • brunoocasali
  • norkunas
  • curquiza

Poem

"I hopped through code with tiny paws,
Tasks now wear enums, neat as laws.
No arrays lost in rabbit holes,
Waits return as tidy goals.
A carrot for types β€” swift and bright! πŸ‡βœ¨"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
βœ… Passed checks (4 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title 'Bump to PHP 8.1, improve type safety for Tasks' accurately summarizes the main changes: PHP version bump and type safety improvements for Task handling.
Linked Issues check βœ… Passed The PR comprehensively addresses both linked issues: #703 implements Task object with typed accessors replacing array returns, and #739 adds wait() method for chaining task completion waits.
Out of Scope Changes check βœ… Passed All changes align with the stated objectives: PHP 8.1 requirement, Task typing, trailing commas, php-cs-fixer updates, test refactoring, and Task-based API returns are all in-scope enhancements.
✨ Finishing touches
  • [ ] πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

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 and usage tips.

coderabbitai[bot] avatar May 14 '25 04:05 coderabbitai[bot]

@norkunas After further discussing the subject with @curquiza, we decided it would be more prudent to release it as v2 since it introduces a few breaking changes, especially the deprecation of PHP 7.

If that becomes a burden for our users later, we can introduce a table to showcase which SDK version supports which Meilisearch version. In any case, I think that's better, the engine and their SDKs are independent pieces of software that should be able to evolve independently and should remain adherent to semver rules :)

So, since we are doing a v2, feel free to suggest other potential breaking changes so we can release it in the same package!

Thanks a lot for the PR by the way, loved it!

CC: @Strift

brunoocasali avatar May 14 '25 20:05 brunoocasali

@norkunas After further discussing the subject with @curquiza, we decided it would be more prudent to release it as v2 since it introduces a few breaking changes, especially the deprecation of PHP 7.

If that becomes a burden for our users later, we can introduce a table to showcase which SDK version supports which Meilisearch version. In any case, I think that's better, the engine and their SDKs are independent pieces of software that should be able to evolve independently and should remain adherent to semver rules :)

So, since we are doing a v2, feel free to suggest other potential breaking changes so we can release it in the same package!

Thanks a lot for the PR by the way, loved it!

CC: @Strift

Sounds good πŸ˜‰

norkunas avatar May 15 '25 03:05 norkunas

Then I think we can remove array access of task data here?

norkunas avatar May 15 '25 03:05 norkunas

Then I think we can remove array access of task data here?

If you think we have more benefits by removing it than by keeping, I agree in removing it.

brunoocasali avatar May 15 '25 13:05 brunoocasali

@Strift i should switch to v2 branch target here?

norkunas avatar Jun 04 '25 04:06 norkunas

@Strift can you upmerge to v2, because it requires changes here that are already done on v1.15

norkunas avatar Jun 10 '25 04:06 norkunas

Hi @norkunas, yes! Thanks for updating the target branch to v2.

Strift avatar Jun 12 '25 07:06 Strift

So, I think we need to make the tests pass and merge this branch :)

Can you do this @norkunas then ask for my review again, please?

brunoocasali avatar Jun 19 '25 22:06 brunoocasali

@brunoocasali that's not up to me. v2 needs to be upmerged, because main has some pushed changes

norkunas avatar Jun 20 '25 04:06 norkunas

Apologies, I may have made a mistake.

I merged the changes from v2 into this branch, but maybe I should have rebased the PR instead.

Strift avatar Sep 16 '25 03:09 Strift

I never do merges

norkunas avatar Sep 16 '25 03:09 norkunas

Can we revert these 4 commits so we can start again?

I don't want to force push over your branch

Strift avatar Sep 16 '25 07:09 Strift

@Strift i've removed those commits. i can now rebase, but the PR target is still v2, i should change it to main, which is now v2, right?

norkunas avatar Sep 16 '25 11:09 norkunas

Let's us know when we can start testing V2. I'm eager to use the new return types.

tacman avatar Sep 16 '25 14:09 tacman

@norkunas yes, you can target main directly

Strift avatar Sep 18 '25 06:09 Strift