Couchbase PHP SDK v4 brought a lot of improvements to the API. Some of the changes unfortunately break compatibility with v3. This guide helps upgrade applications, and highlights the most important differences.
API Reference: https://docs.couchbase.com/sdk-api/couchbase-php-client
The following diagram illustrates the architecture of PHP SDK v3:
block-beta
columns 3
developer("Developer")
block:user:2
columns 2
Applications
ODMs
Frameworks
Integrations
end
low_level("Extension\n(exposes high-level API)")
extension("PHP Binary Extension\n(PECL: couchbase)"):2
dependencies("System Dependencies")
block:deps:2
lcb("libcouchbase")
libevent("libevent")
boring_ssl("OpenSSL")
end
The following diagram illustrates the architecture of PHP SDK v4:
block-beta
columns 3
developer("Developer")
block:user:2
columns 2
Applications
ODMs
Frameworks
Integrations
end
high_level("High-Level API")
library("PHP Library\n(Composer: couchbase/couchbase)"):2
low_level("Extension")
extension("PHP Binary Extension\n(PECL: couchbase)"):2
dependencies("Static Dependencies")
block:deps:2
cxx_core("C++ Core SDK")
boring_ssl("BoringSSL")
end
So let us point out immediate differences:
-
SDKv4 uses Composer to deliver high-level API classes, while SDKv3 defines everything in the extension. While it adds an extra step for the developer, it really simplifies maintenance, reduces potential memory issues, allows to keep documentation consistent and improves IDE integration.
It is really easy add classes to the application using Composer.
composer require couchbase/couchbase
the resulting
composer.json
needs only two lines to be added:diff --git i/composer.json w/composer.json index b743a66..88e69da 100644 --- i/composer.json +++ w/composer.json @@ -24,6 +24,8 @@ "require": { "php": "^7.4 || ^8.0", "ext-json": "*", + "ext-couchbase": "^4.2", + "couchbase/couchbase": "^4.2", "monolog/monolog": "^2.3", "php-di/php-di": "^6.3", "slim/psr7": "^1.5",
In case Composer cannot be used, we also have autoloader script
Couchbase\autoload.php
, that sets up a hook to resolve and automatically require SDK classes, but to use this autoloader, the headers must be installed somewhere in the PHP include path, which could be found using one of the following commands, or in thephpinfo()
output.$ pecl config-get php_dir /usr/share/pear
$ php -r 'printf("%s\n", ini_get("include_path"));' .:/usr/share/pear:/usr/share/php
-
Another important observation is that SDKv4 does not expect any system libraries or headers to be installed to work and uses a statically compiled core implementation (just like all other wrappers). Additionally, it statically links the TLS library to the extension, which, again, simplifies deployment on the Windows platform.
SDKv3 used a non-standard way of specifying an encoder and decoder for the document objects for the KV API.
This API was inherited from SDKv2, where the developer needed to specify a decoder or
encoder function in the options. SDKv4 fixes this and instead defines the \Couchbase\Transcoder
interface which encapsulates all the logic. Additionally, it provides four implementations of it
and uses \Couchbase\JsonTranscoder
by default.
Let's say we want to read/write documents without any conversion, just as a binary streams. To do so, we would need to override the transcoder, because otherwise our byte strings will be serialized as JSON strings.
Here is how it is done with SDKv3:
$options = new \Couchbase\ClusterOptions();
$options->credentials("Administrator", "password");
$cluster = new \Couchbase\Cluster("couchbase://localhost", $options);
$collection = $cluster->bucket("default")->defaultCollection();
$passThruDecoder = function($bytes, $flags, $datatype) {
// do not check anything just return bytes as passed by core
return $bytes;
};
$passThruEncoder = function($value) {
// do not do conversion, and return zeroes for datatype and flags
return [$value, 0x00, 0x00];
};
// Mutation operations in SDKv3 explicitly ask for encoder callable
$options = new \Couchbase\UpsertOptions();
$options->encoder($passThruEncoder);
$collection->upsert("foo", "BINARY DATA: \xDE\xAD\xBE\xEF", $options);
// Retrieval operation in SDKv3 only allow decoder callable
$options = new \Couchbase\GetOptions();
$options->decoder($passThruDecoder);
$res = $collection->get("foo", $options);
var_dump($res->content());
With SDKv4 we ship \Couchbase\RawBinaryTranscoder
, which could be reimplemented as the following:
class PassThruTranscoder implements \Couchbase\Transcoder
{
public function encode($value): array
{
return [
$value,
(new \Couchbase\TranscoderFlags(\Couchbase\TranscoderFlags::DATA_FORMAT_BINARY))->encode(),
];
}
public function decode(string $bytes, int $flags)
{
return $bytes;
}
}
// RawBinaryTranscoder like any other implementation has static method getInstance() returning
// singleton object.
$passThruTranscoder = new PassThruTranscoder();
$options = new \Couchbase\UpsertOptions();
$options->transcoder($passThruTranscoder);
$collection->upsert("foo", "BINARY DATA: \xDE\xAD\xBE\xEF", $options);
$options = new \Couchbase\GetOptions();
$options->transcoder($passThruTranscoder);
$res = $collection->get("foo", $options);
var_dump($res->content());
SDKv4 moved exceptions into the \Couchbase\Exception
namespace, so if the application used to catch
and handle exceptions from the SDK, those places should update to use the new names.
SDKv3 exception | SDKv4 exception |
---|---|
\Couchbase\AuthenticationException | \Couchbase\Exception\AuthenticationFailureException |
\Couchbase\BadInputException | \Couchbase\Exception\InvalidArgumentException |
|
\Couchbase\Exception\BucketNotFoundException |
\Couchbase\CasMismatchException | \Couchbase\Exception\CasMismatchException |
\Couchbase\CollectionMissingException | \Couchbase\Exception\CollectionNotFoundException |
\Couchbase\DurabilityException |
One of the more specific exceptions.
|
\Couchbase\DmlFailureException |
The SDK will detect the underlying error that caused the query to fail and throw
the specific exception.
|
\Couchbase\DocumentNotFoundException | \Couchbase\Exception\DocumentNotFoundException |
\Couchbase\IndexFailureException | \Couchbase\Exception\IndexFailureException |
\Couchbase\IndexNotFoundException | \Couchbase\Exception\IndexNotFoundException |
\Couchbase\InvalidRangeException | \Couchbase\Exception\DeltaInvalidException |
\Couchbase\KeyDeletedException | Removed |
\Couchbase\KeyExistsException | \Couchbase\Exception\DocumentExistsException |
\Couchbase\KeyLockedException | \Couchbase\Exception\DocumentLockedException |
\Couchbase\ParsingFailureException | \Couchbase\Exception\ParsingFailureException |
\Couchbase\PartialViewException | Removed |
\Couchbase\PathExistsException | \Couchbase\Exception\PathExistsException |
\Couchbase\PathNotFoundException | \Couchbase\Exception\PathNotFoundException |
\Couchbase\PlanningFailureException | \Couchbase\Exception\PlanningFailureException |
\Couchbase\PreparedStatementFailureException | \Couchbase\Exception\PreparedStatementFailureException |
|
Rate and Quota limit exceptions redesigned, and the SDK will not use them. |
\Couchbase\RequestCanceledException | \Couchbase\Exception\RequestCanceledException |
\Couchbase\ScopeMissingException | \Couchbase\Exception\ScopeNotFoundException |
\Couchbase\ServiceNotAvailableException | \Couchbase\Exception\ServiceNotAvailableException |
\Couchbase\TempFailException | \Couchbase\Exception\TemporaryFailureException |
\Couchbase\TimeoutException | \Couchbase\Exception\TimeoutException |
\Couchbase\ValueTooBigException | \Couchbase\Exception\ValueTooLargeException |
|
All generic exceptions mapped to \Couchbase\Exception\CouchbaseException or to one
of the new, more specific exceptions.
|
In SDKv4 various enumerations and constants changed their types from int
to string
to reduce
potential issues. In general nothing has to be changed here, but in some cases names of the types
slightly changed to be more consistent with other SDKs.
SDKv3 | SDKv4 |
---|---|
\Couchbase\ViewScanConsistency::NOT_BOUNDED (0) | \Couchbase\ViewConsistency::NOT_BOUNDED ("notBounded") |
\Couchbase\ViewScanConsistency::REQUEST_PLUS (1) | \Couchbase\ViewConsistency::REQUEST_PLUS ("requestPlus") |
\Couchbase\ViewScanConsistency::UPDATE_AFTER (2) | \Couchbase\ViewConsistency::UPDATE_AFTER ("updateAfter") |
SDKv3 | SDKv4 |
---|---|
\Couchbase\ViewOrdering::ASCENDING (0) | \Couchbase\ViewOrdering::ASCENDING ("ascending") |
\Couchbase\ViewOrdering::DESCENDING (1) | \Couchbase\ViewOrdering::DESCENDING ("descending") |
SDKv3 | SDKv4 |
---|---|
\Couchbase\QueryScanConsistency::NOT_BOUNDED (1) | \Couchbase\QueryScanConsistency::NOT_BOUNDED ("notBounded") |
\Couchbase\QueryScanConsistency::REQUEST_PLUS (2) | \Couchbase\QueryScanConsistency::REQUEST_PLUS ("requestPlus") |
\Couchbase\QueryScanConsistency::STATEMENT_PLUS (3) | Removed |
SDKv3 | SDKv4 |
---|---|
\Couchbase\QueryProfile::OFF (1) | \Couchbase\QueryProfile::OFF ("off") |
\Couchbase\QueryProfile::PHASES (2) | \Couchbase\QueryProfile::PHASES ("phases") |
\Couchbase\QueryProfile::TIMINGS (3) | \Couchbase\QueryProfile::TIMINGS ("timings") |
SDKv3 | SDKv4 |
---|---|
"" | \Couchbase\AnalyticsScanConsistency::NOT_BOUNDED ("notBounded") |
"request_plus" | \Couchbase\AnalyticsScanConsistency::REQUEST_PLUS ("requestPlus") |
SDKv3 did not allow to set timeouts through \Couchbase\ClusterOptions
, and the application have to
rely on connection string (that is handled by libcouchbase
eventually), or use setter methods on
the \Couchbase\Bucket
instance.
The table below shows correspondence of timeout values between Bucket
object setters and
ClusterOptions
in the new API.
SDKv3 (in microseconds) | SDKv4 (in milliseconds) |
---|---|
Bucket::operationTimeout() | ClusterOptions::keyValueTimeout() |
Bucket::viewTimeout() | ClusterOptions::viewTimeout() |
Bucket::n1qlTimeout() | ClusterOptions::queryTimeout() |
Bucket::durabilityInterval() | Removed |
Bucket::durabilityTimeout() | ClusterOptions::keyValueDurableTimeout() |
Bucket::configTimeout() | ClusterOptions::bootstrapTimeout() |
Bucket::configDelay() | Removed |
Bucket::configNodeTimeout() | Removed |
Bucket::htconfigIdleTimeout() | Removed |
Bucket::configPollInterval() | ClusterOptions::configPollInterval() |
Bucket::httpTimeout() |
One of the service-related should be used:
|
SDKv3 | SDKv4 |
---|---|
couchbase.log_level
|
couchbase.log_level
|
couchbase.encoder.format | Removed |
couchbase.encoder.compression | Removed |
couchbase.encoder.compression_threshold | Removed |
couchbase.encoder.compression_factor | Removed |
couchbase.decoder.json_arrays | Removed |
Sets the maximum time that a persistent instance can be idle before being cleaned up. The default is 60 seconds. The SDK associates a reference counter with the instance uniquely identified by the connection string and credentials. When the reference counter reaches zero, the SDK records the current time into the idle timestamp of the instance. At the end of each request, the SDK runs a cleanup process that will sweep all idle instances at that point in time. Persistent instances are useful for cases when the PHP worker process is not destroyed after serving the request, so that next request will not need to wait for the instance to bootstrap if it uses the same connection string, bucket and credentials. |
SDKv4 is based on C++SDK. It has similar behavior when it comes to persistent
instances. But instead of running cleanup at the end of each request, it does
cleanup only when the library needs to create a new connection and the number of
existing connection have reached By default both This behavior was not possible in SDKv3 because libcouchbase does not have any background threads that can perform service tasks (like tracking configuration changes). So if The expiration time for the instance is recorded when the instance is created
or pulled from the cache, and is set to the current time plus
Note that by setting both |
couchbase.allow_fallback_to_bucket_connection | Removed |