phuze / php-cosmos
A lightweight PHP client for Azure Cosmos DB, supporting PHP 7.0 and later
Requires
- php: >=7.0
- ext-curl: *
- ext-json: *
- guzzlehttp/guzzle: ^6.0 || ^7.0 || ^8.0
- psr/log: ^1.0 || ^2.0 || ^3.0
Requires (Dev)
None
Suggests
- monolog/monolog: a PSR-3 logger implementation to pass to CosmosDb::setLogger()
Provides
None
Conflicts
None
Replaces
None
README
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
curlandjsonextensions - 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:
- Connecting
- Inserting, Updating and Upserting
- Querying
- Patching
- Deleting
- Rate Limiting (429)
- Logging and Debugging
Changelog
v4.1.0
- Added new UPSERT support.
upsert()creates a document, or replaces it if one with the sameidalready exists, so you no longer need a document's_ridto 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 thatselectDB()andselectCollection()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,nulland arrays work - Partition key values with quotes, and numeric partition keys, now work
- Fixed
delete()anddeleteAll()with slashed partition keys, such as/form/typeor/vendorName - Fixed
save()failing when no partition key is set - Fixed
whereContains()missing its closing parenthesis whereContains(),whereStartsWith(),whereEndsWith(),whereIn()andwhereNotIn()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
5and 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,falseandnullparameters 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()andwhereNotIn()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$partitionKeyin v3) increateDocument(),replaceDocument()anddeleteDocument(), and$documentinfindPartitionValue(), no longer have a type. If you extendCosmosDborQueryBuilderand 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 aTypeErrorwhen no partition key is set, a regression in 3.0.4 - Fixed
delete()andsave()with slashed partition keys, such as/form/typeor/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()anddeleteAll()now use the value fromsetPartitionValue(), 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.jsonrequiring 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
partitionkeyrangeidheaders when a cross-partition query needs to be retried with PK ranges
Notes
- Uses version
2018-12-31of the Cosmos DB REST API - Based on AzureDocumentDB-PHP and CosmosDb
- Some cross-partition queries (e.g. those with
ORDER BY,TOPor aggregates) can't be served by the Cosmos DB gateway, so they're run against each partition key range in turn, andORDER BYandTOPapply 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:
- Fork the repository and create a branch from
main - 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)
- Add or update a test in
tests/casesthat covers it - Run
composer testand make sure everything passes - 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/.