Search by

phuze / php-cosmos

phuze

A lightweight PHP client for Azure Cosmos DB, supporting PHP 7.0 and later

Package info

github.com/phuze/php-cosmos

pkg:composer/phuze/php-cosmos

Statistics

Installs: 2 132

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

4.1.0 2026-09-29 16:20 UTC

This package is auto-updated.

Last update: 2026-09-29 16:23:13 UTC


README

Tests Latest Version PHP Version Guzzle Version Total Downloads License

A lightweight PHP client for Azure Cosmos DB, supporting PHP 7.0 and later.

  • PHP compatibility: PHP 7.0 through 8.x, with Guzzle 6, 7 or 8
  • Tested: Every supported PHP and Guzzle combination is tested on each push
  • No SDK required: Uses the Cosmos DB REST API directly
  • Query builder: Supports cross-partition queries and automatic pagination
  • Document operations: Read, insert, replace, upsert, patch and delete documents
  • Connection handling: Reuses connections, times out slow requests, retries dropped connections, and optionally retries rate-limited (429) requests
  • Logging: Supports any PSR-3 logger, such as Monolog, for request and retry logging

Installation

Install phuze/php-cosmos in your project:

composer require phuze/php-cosmos

To stay on 3.x, pin the version:

composer require phuze/php-cosmos:^3.0

Requirements

  • PHP 7.0 or later, with the curl and json extensions
  • Guzzle 6, 7 or 8, whichever Composer picks for your PHP version:
PHP version Guzzle version
7.0 to 7.2.4 6
7.2.5 to 7.3 6 or 7
7.4 and later, including 8.x 6, 7 or 8

Usage

use Phuze\PhpCosmos\CosmosDb;
use Phuze\PhpCosmos\QueryBuilder;

# connect, and select a database and collection
$conn = new CosmosDb('https://myaccount.documents.azure.com', 'your-key');
$database = $conn->selectDB('databaseName');
$collection = $database->selectCollection('Users', '/country');

# find the users in Canada who are over 30
$users = QueryBuilder::instance()
    ->setCollection($collection)
    ->setPartitionValue('Canada')
    ->where("c.age > @age")
    ->params(['@age' => 30])
    ->findAll()
    ->toArray();

See the documentation for more examples:

Changelog

v4.1.0

  • Added new UPSERT support. upsert() creates a document, or replaces it if one with the same id already exists, so you no longer need a document's _rid to update it. Triggers can run on upserts too
  • Added single-document reads. $collection->getDocument() fetches a document by its _rid, which is cheaper and faster than a query
  • Added a way to skip the database and collection lookups. Save the _rids once, and later requests no longer need the two extra calls that selectDB() and selectCollection() make

v4.0.1

  • Renamed variables and some method parameters for clarity, using camelCase consistently
  • Cleaned up the doc blocks for a better IDE experience

v4.0.0

This release changes some existing behavior. See Upgrading from v3.

  • Connections are now reused between requests, which makes them faster
  • Requests now time out after 60 seconds
  • Safe requests (reads, queries, creates, replaces and deletes) are retried once if the connection drops, but PATCH requests and stored procedures never are, since they aren't always safe to repeat
  • New PATCH support, for updating individual properties of a document
  • New setRetryOptions() to automatically retry rate-limited (429) requests (off by default)
  • New setLogger() for logging requests and retries
  • Added support for Guzzle 8
  • Added a test suite
  • Cross-partition queries now return all of their results, not just the first page
  • Query parameters keep their type, so true, false, null and arrays work
  • Partition key values with quotes, and numeric partition keys, now work
  • Fixed delete() and deleteAll() with slashed partition keys, such as /form/type or /vendorName
  • Fixed save() failing when no partition key is set
  • Fixed whereContains() missing its closing parenthesis
  • whereContains(), whereStartsWith(), whereEndsWith(), whereIn() and whereNotIn() now escape quotes for you
  • Fixed setPartitionValue('0') being ignored
  • Fixed selectCollection() when the partition key has no leading slash
  • Fixed debug mode emptying responses
  • Fixed deprecation warnings on newer versions of PHP and Guzzle
  • Fixed a warning on PHP 7.0 to 7.2 when a query returns no documents
  • Fixed support for PHP 7.0 and 7.1

Upgrading from v3

Most apps only need to change phuze/php-cosmos to ^4.0 in composer.json. Check each of these in case it applies to you:

  • Timeouts: requests now give up after 60 seconds (v3 waited forever), so if you run queries that take longer, raise the limit:

    $conn = new CosmosDb($host, $key);
    $conn->setHttpClientOptions(['timeout' => 120]); # in seconds
  • Partition values: Cosmos DB treats the number 5 and the string "5" as different partition key values. v3 always sent a string, but v4 sends whatever you pass, so if your partition key values are strings and you pass an integer (e.g. an ID from another database), cast it:

    $res = QueryBuilder::instance()
        ->setCollection($collection)
        ->setPartitionValue((string)$id)
        ->findAll()
        ->toArray();
  • Query parameters: v3 turned true, false and null parameters into the strings "1", "" and "", so this query looked for the string "1" and never matched a document with "active": true. v4 sends the actual values, so it now works:

    $res = QueryBuilder::instance()
        ->setCollection($collection)
        ->where("c.active = @active")
        ->params(['@active' => true]) # v3 sent "1", v4 sends true
        ->findAll(true)
        ->toArray();

    If your documents store flags as strings like "1", pass a string instead:

        ->params(['@active' => '1'])
  • Escaped values: whereContains(), whereStartsWith(), whereEndsWith(), whereIn() and whereNotIn() now escape quotes and backslashes for you, so pass values as they are, or they'll be escaped twice:

    # v3 needed quotes escaped by hand
    ->whereStartsWith('c.name', "O\\'Brien")
    
    # v4 escapes them for you
    ->whereStartsWith('c.name', "O'Brien")
  • Subclasses: to support numeric partition keys, $partitionValue (called $partitionKey in v3) in createDocument(), replaceDocument() and deleteDocument(), and $document in findPartitionValue(), no longer have a type. If you extend CosmosDb or QueryBuilder and override any of these, remove the type from your override too, or PHP will throw a fatal error:

    - public function createDocument(..., string $partitionKey = null, ...)
    + public function createDocument(..., $partitionValue = null, ...)
  • Creating documents: if the connection drops after Cosmos DB has saved a new document, the automatic retry fails with a 409 Conflict, so if you handle errors from save(), treat a 409 as "this document may already exist" rather than a plain failure

v3.0.6

  • Fixed a warning on PHP 7.2 when a query returns no documents

v3.0.5

  • Fixed whereContains() missing its closing parenthesis
  • Fixed save() throwing a TypeError when no partition key is set, a regression in 3.0.4
  • Fixed delete() and save() with slashed partition keys, such as /form/type or /vendorName
  • Fixed selectCollection() when the partition key has no leading slash
  • Fixed debug mode emptying responses
  • Fixed deprecation warnings on PHP 8.2+ and Guzzle 7.11+

v3.0.4

  • save() and deleteAll() now use the value from setPartitionValue(), even when no partition key is set

v3.0.3

  • Removed typed class properties, which need PHP 7.4, so the library loads on older PHP 7 versions

v3.0.2

  • Fixed every request failing with "Call to a member function getBody() on string"

v3.0.1

  • Fixed composer.json requiring PHP 8.0, which stopped PHP 7 from installing the library

v3.0.0

  • Restored support for PHP 7.x, so this library can be used with both 7.x and 8.x
  • Improved how nested partition keys are handled
  • Improved how partition values are matched
  • Fixed an issue that prevented document deletion when a container used a nested partition key
  • Fixed an issue with partitionkeyrangeid headers when a cross-partition query needs to be retried with PK ranges

Notes

  • Uses version 2018-12-31 of the Cosmos DB REST API
  • Based on AzureDocumentDB-PHP and CosmosDb
  • Some cross-partition queries (e.g. those with ORDER BY, TOP or aggregates) can't be served by the Cosmos DB gateway, so they're run against each partition key range in turn, and ORDER BY and TOP apply within each range rather than across the whole result

Development

composer install
composer test

The tests send their requests to a fake Cosmos DB, so no Azure account is needed. They're plain PHP rather than PHPUnit, so the same tests run on every supported PHP version. Each file in tests/cases covers one area, and any notice, warning or deprecation raised by the library fails the run.

GitHub Actions runs the tests on every push and pull request, on each PHP version from 7.0 to 8.5, with every Guzzle version that PHP version supports. Releases are only tagged from a commit that passes.

Contributing

Bug reports, fixes and improvements are welcome.

Reporting a bug or requesting a feature: open an issue with your PHP and Guzzle versions, what you did, and what you expected to happen.

Submitting a change:

  1. Fork the repository and create a branch from main
  2. Make your change, keeping it compatible with PHP 7.0 (no typed properties, nullable types, arrow functions or other syntax newer than PHP 7.0)
  3. Add or update a test in tests/cases that covers it
  4. Run composer test and make sure everything passes
  5. Open a pull request against main, explaining what changed and why

GitHub Actions runs the tests on your pull request, and it's only merged once they pass. If your change affects how the library is used, please also update the changelog above and the relevant page in docs/.