The Polypheny PHP Driver executes SQL and MongoQL over the Prism Query Interface and reads namespace and entity metadata. Use its Polypheny\Connection and Polypheny\Cursor classes in your application. See the PHP integration guide for a runnable application example.
Installation and compatibility
Use 64-bit PHP 8.2 or later and Composer. Install the package in your project:
composer require polypheny/php-driver
Composer installs the Protocol Buffers dependency. The driver uses PHP streams for TCP and Unix sockets. Deploy a Prism Query Interface on the server and configure its listening address. Plain TCP carries credentials and queries without TLS. Use it on a trusted network or through a secure tunnel.
This reference describes driver version 1.0.0. The driver advertises Prism 2.2 and opens the connection if the server reports compatibility. Feature availability depends on the server. For example, document collection metadata requires a server that implements it.
Connection
Import Polypheny\Connection and load Composer’s autoloader. The default TCP settings below are for a local development instance. Supply the host, port and credentials configured for your instance.
<?php
require __DIR__ . '/vendor/autoload.php';
use Polypheny\Connection;
$connection = Connection::connectTcp(host: '127.0.0.1', port: 20590);
try {
echo 'Prism version: ', $connection->serverVersion(), PHP_EOL;
$cursor = $connection->cursor();
$cursor->execute('SELECT 1');
echo $cursor->fetchOne()[0], PHP_EOL;
$connection->commit();
} finally {
$connection->close();
}
| Method | Behavior |
|---|---|
Connection::connectTcp(string $host = '127.0.0.1', int $port = 20590, ?string $username = 'pa', ?string $password = '', bool $autoCommit = false, float $timeout = 30.0): Connection |
Opens a plain TCP connection. Pass a hostname or IP address. The timeout is in seconds and applies to connecting and stream I/O. A null username or password omits that field from the Prism request. |
Connection::connectUnix(?string $path = null, ?string $username = null, ?string $password = null, bool $autoCommit = false, float $timeout = 30.0): Connection |
Opens a Unix domain socket. The PHP runtime must support Unix streams and have access to the socket file. The default path is $HOME/.polypheny/polypheny-prism.sock. Supply a path if HOME is unset. The credentials are omitted by default. |
serverVersion(): string |
Returns the Prism API version reported by the server, for example 2.2. |
cursor(): Cursor |
Creates a cursor for executing and reading statements on this connection. |
commit(): void |
Commits the current transaction. |
rollback(): void |
Rolls back the current transaction. |
searchNamespaces(?string $pattern = null, ?string $type = null): NamespacesResponse |
Returns namespaces matching the optional filters. |
searchEntities(string $namespace, ?string $pattern = null): EntitiesResponse |
Returns entities in the named namespace matching the optional pattern. |
isClosed(): bool |
Reports whether the underlying transport is closed. It does not send a request to the server. |
close(): void |
Closes open cursors, rolls back pending work and disconnects. It closes the transport even if cleanup fails and can throw a cleanup error. Repeated calls are safe. |
The constants Connection::PRISM_MAJOR and Connection::PRISM_MINOR are the client protocol version numbers, 2 and 2 in this release. Closing or destroying the connection does not commit pending work. Call commit() explicitly with the default autoCommit: false. With auto-commit enabled, finish reading a result and explicitly call commit() or rollback() if it spanned multiple Prism frames. The server can leave that transaction open.
To use a local Unix socket, start a Prism interface configured for Unix sockets and set POLYPHENY_SOCKET to its path. The snippet uses the driver’s default path when the variable is unset.
<?php
require __DIR__ . '/vendor/autoload.php';
use Polypheny\Connection;
$connection = Connection::connectUnix(path: getenv('POLYPHENY_SOCKET') ?: null);
try {
$row = $connection->cursor()->execute('SELECT 1')->fetchOne();
echo $row[0], PHP_EOL;
$connection->commit();
} finally {
$connection->close();
}
Cursor
A cursor holds one current statement and result. Executing a new statement on it releases the previous statement. Create another cursor if you need to keep reading the previous result. Transactions belong to the connection and are shared by its cursors.
| Method | Behavior |
|---|---|
execute(string $sql, ?array $parameters = null, ?int $fetchSize = null): Cursor |
Executes SQL and returns the same cursor. Use a PHP list for positional ? parameters or a string-keyed array for named :name parameters. The array key is name, without the colon. Pass null to execute an unparameterized statement. |
executeAny(string $language, string $query, ?array $parameters = null, ?int $fetchSize = null, ?string $namespace = null): Cursor |
Executes a statement in a language registered on the server, for example sql or mongo for MongoQL. Select the target namespace explicitly when needed. Binding also depends on the language’s server-side support. |
executeMany(string $sql, iterable $parameters): Cursor |
Executes the SQL once per parameter array, in order. Each execution prepares its own statement. Results and rowCount() apply to the last execution. An empty input clears the previous result. If an execution fails, earlier ones may already have taken effect. Use a transaction and roll back on failure when the writes must succeed together. |
fetchOne(): array|stdClass|null |
Consumes one row or document, requesting another frame when needed. Returns null at exhaustion. Throws DriverException if there is no result set to fetch. |
fetchMany(?int $size = null): array |
Consumes up to $size remaining rows or documents. Defaults to arraySize(). Zero returns an empty array. |
fetchAll(): array |
Consumes and materializes all remaining rows or documents. |
getIterator(): Generator |
Yields remaining rows or documents from the current position. foreach ($cursor as $row) uses this method. |
setArraySize(int $size): Cursor |
Changes the default fetchMany() size to a positive integer and returns the cursor. |
arraySize(): int |
Returns that default, initially 1. |
description(): ?array |
Returns column metadata for a relational result. Returns null before execution and for document or scalar results. |
rowCount(): int |
Returns the server’s scalar result, such as the affected row count for a write. Returns -1 before execution or for result sets. |
close(): void |
Releases the current statement and closes the cursor. It can be called repeatedly and does not close the connection. |
Relational rows are zero-based PHP arrays in projection order. Document rows are stdClass objects. A SQL NULL is a value within a row, so a row containing only NULL is still an array. Graph result frames raise DriverException.
A positive fetchSize requests that many rows per Prism frame. null and 0 use the server default, and -1 requests the entire result in one frame. fetchMany() can span several frames and its size is independent of fetchSize. Iterate or fetch in batches when you need to limit how many rows you keep in PHP memory.
Each description() entry contains the following keys:
| Key | Meaning |
|---|---|
name |
Column label (including an alias), falling back to the column name. |
type |
Prism type name, a numeric type ID as a string for an unrecognized type, or null when no type metadata is supplied. |
display_size, precision, scale |
Length, precision and scale reported by the server. |
nullable |
Whether the server reports that the column permits null values. |
original_name, namespace, entity |
Source column, namespace and entity reported by the server. Computed columns may lack source information. |
This example uses both binding styles and reads one result across several frames. It creates and drops a temporary example table in the connection’s default namespace. Run it on a test instance with permission to create tables.
<?php
require __DIR__ . '/vendor/autoload.php';
use Polypheny\Connection;
$connection = Connection::connectTcp();
$table = 'php_driver_example_' . bin2hex(random_bytes(4));
try {
$cursor = $connection->cursor();
$cursor->execute("CREATE TABLE $table (id INTEGER NOT NULL, label VARCHAR(40), PRIMARY KEY (id))");
try {
$cursor->executeMany("INSERT INTO $table (id, label) VALUES (?, ?)", [
[1, 'alpha'], [2, 'beta'], [3, 'gamma'], [4, 'delta'],
]);
$connection->commit();
$cursor->execute("UPDATE $table SET label = :label WHERE id = :id", [
'label' => 'temporary',
'id' => 2,
]);
echo 'Rows updated: ', $cursor->rowCount(), PHP_EOL;
$connection->rollback();
$cursor->execute("SELECT id, label FROM $table ORDER BY id", fetchSize: 1);
echo implode(', ', array_column($cursor->description(), 'name')), PHP_EOL;
print_r($cursor->fetchOne());
$cursor->setArraySize(2);
print_r($cursor->fetchMany());
foreach ($cursor as $row) {
print_r($row);
}
$connection->commit();
} finally {
$connection->rollback();
$cursor->execute("DROP TABLE $table");
$cursor->close();
}
} finally {
$connection->close();
}
Namespace and entity metadata
The metadata methods return generated Protocol Buffers messages in Org\Polypheny\Prism. Iterate the repeated fields directly. Their type is Google\Protobuf\Internal\RepeatedField.
NamespacesResponse::getNamespaces()containsPBNamespaceobjects. UsegetNamespaceName()for the name andhasNamespaceType()andgetNamespaceType()for the optional model. Name patterns accept%for any sequence of characters and_for one character. Supported model filter names areRELATIONAL,DOCUMENTandGRAPH.EntitiesResponse::getEntities()containsEntityobjects. The optional entity pattern uses the same wildcards. UsegetTable()orgetDocument()to get the relevant message. They returnnullif that variant is absent. Graph entity metadata is a placeholder in Prism 2.2. Polypheny’s Prism interface does not support listing graph entities.- For a
Table, usegetNamespaceName(),getTableName()andgetTableType()to identify the table.getColumns()lists columns, whose getters includegetColumnName(),getTypeName()andgetIsNullable().hasPrimaryKey()checks forgetPrimaryKey().getForeignKeys(),getExportedKeys()andgetIndexes()expose references and indexes when provided by the server.
For a Document, the following getters describe its collection:
| Getter | Meaning |
|---|---|
getNamespaceName(), getCollectionName() |
The namespace and collection name. |
getFields() |
DocumentField entries. getFieldPath() returns a key or a dotted path such as profile.active. Literal dots and backslashes in keys are escaped. getTypeName() returns the inferred type, such as string, int, double, bool, array or object. A field observed only as null has type null. Integers and doubles can combine into double. Other conflicting types yield mixed. getIsOptional() reports whether the field was absent or null in the inspected documents. |
hasSampleDocumentJson(), getSampleDocumentJson() |
Checks for and reads an optional sample document as a JSON string. The sample may contain actual data. |
getDocumentIndexes() |
DocumentIndex entries for indexes exposed by the server. Each contains getFieldPath(), getIndexType() and getUnique(). For a compound index the individual paths do not imply single-field uniqueness. |
Field information is inferred from a bounded sample of documents. It describes the data the server inspected and may omit fields found elsewhere in the collection. An empty collection can have no inferred fields or sample document. Metadata can reflect uncommitted writes made in the same connection.
This example creates a disposable document namespace, queries a collection and inspects its metadata. Run it with permission to create document namespaces and collections.
<?php
require __DIR__ . '/vendor/autoload.php';
use Polypheny\Connection;
$connection = Connection::connectTcp();
$namespace = 'php_driver_example_' . bin2hex(random_bytes(4));
try {
$cursor = $connection->cursor();
$cursor->execute("CREATE DOCUMENT NAMESPACE $namespace");
try {
$cursor->executeAny('mongo', 'db.createCollection("events")', namespace: $namespace);
$cursor->executeAny(
'mongo',
'db.events.insertOne({"_id": "welcome", "profile": {"active": true}})',
namespace: $namespace,
);
$connection->commit();
foreach ($connection->searchNamespaces($namespace, 'DOCUMENT')->getNamespaces() as $entry) {
echo $entry->getNamespaceName(), ' ', $entry->getNamespaceType(), PHP_EOL;
}
$cursor->executeAny('mongo', 'db.events.find({})', fetchSize: 1, namespace: $namespace);
foreach ($cursor->fetchAll() as $document) {
echo $document->_id, PHP_EOL;
}
$connection->commit();
foreach ($connection->searchEntities($namespace, 'ev%')->getEntities() as $entity) {
$collection = $entity->getDocument();
if ($collection === null) {
continue;
}
printf("%s.%s\n", $collection->getNamespaceName(), $collection->getCollectionName());
foreach ($collection->getFields() as $field) {
printf("%s %s (optional: %s)\n", $field->getFieldPath(), $field->getTypeName(),
$field->getIsOptional() ? 'yes' : 'no');
}
if ($collection->hasSampleDocumentJson()) {
print_r(json_decode($collection->getSampleDocumentJson(), flags: JSON_THROW_ON_ERROR));
}
foreach ($collection->getDocumentIndexes() as $index) {
printf("%s %s (unique: %s)\n", $index->getFieldPath(), $index->getIndexType(),
$index->getUnique() ? 'yes' : 'no');
}
}
} finally {
$connection->rollback();
$cursor->execute("DROP NAMESPACE $namespace");
$cursor->close();
}
} finally {
$connection->close();
}
Values and type conversions
The cursor uses Polypheny\ValueCodec automatically when binding parameters and decoding results. PHP values are mapped as follows:
| PHP value | Prism conversion |
|---|---|
null, bool, string |
Null, boolean and UTF-8 text. Use Binary for uninterpreted bytes. |
int |
Encodes as a Prism integer in the signed 32-bit range and as a signed 64-bit long otherwise. Both decode to PHP int. |
float |
Encodes as a double. Both Prism float and double decode to PHP float. |
Polypheny\Binary |
Encodes binary values. Binary and file results both decode to this wrapper. |
Polypheny\Decimal |
Encodes and decodes exact decimal values. Construct from a string to preserve precision. |
Polypheny\Date, Polypheny\Time |
Calendar date and time of day, respectively. Neither carries a time zone. Time has millisecond precision. |
DateTimeInterface |
Encodes a timestamp at millisecond precision, dropping finer precision. Timestamp results are UTC DateTimeImmutable instances. |
Polypheny\Interval |
Encodes a Prism interval and represents interval results. Some server versions do not accept bound interval values. SQL interval literals can produce interval results. |
| PHP list | A Prism list. A PHP empty array [] is an empty list. |
Associative array or stdClass |
Encodes as a document. Document results decode to stdClass, including nested objects. Use new stdClass() for an empty document. |
The wrappers are immutable. They expose the following constructors, factories and properties:
| Class | API |
|---|---|
Binary |
new Binary(string $bytes). Read the raw bytes from $bytes. |
Decimal |
new Decimal(string $value) accepts a finite decimal string, optionally with a base-10 exponent. Decimal::fromUnscaled(string $unscaled, int $scale): Decimal creates the value unscaled × 10^(-scale). Read the signed integer string from $unscaled and the integer scale from $scale. __toString() returns the exact value, using scientific notation for some scales. |
Date |
new Date(string $value) accepts YYYY-MM-DD in years 0001 through 9999. Date::fromEpochDays(int $days): Date uses a day offset from 1970-01-01. Read it from $daysSinceEpoch. __toString() returns YYYY-MM-DD. |
Time |
new Time(string $value) accepts 24-hour HH:MM:SS with an optional fraction of one to three digits. Time::fromMilliseconds(int $milliseconds): Time accepts 0 through 86,399,999. Read the offset from $millisecondsSinceMidnight. __toString() returns HH:MM:SS or HH:MM:SS.mmm. |
Interval |
new Interval(int $months, int $milliseconds). Read the calendar and elapsed components separately from $months and $milliseconds. |
For applications working directly with Prism messages, ValueCodec::encode(mixed $value): ProtoValue encodes a PHP value, ValueCodec::decode(ProtoValue $value): mixed decodes a value, and ValueCodec::decodeDocument(ProtoDocument $document): stdClass decodes a document frame entry. These methods handle nested lists and documents up to the driver’s nesting limit of 100.
<?php
require __DIR__ . '/vendor/autoload.php';
use Polypheny\Binary;
use Polypheny\Date;
use Polypheny\Decimal;
use Polypheny\Interval;
use Polypheny\Time;
use Polypheny\ValueCodec;
$document = (object) [
'amount' => new Decimal('19.90'),
'day' => Date::fromEpochDays(0),
'time' => new Time('12:30:00.250'),
'payload' => new Binary("\x00\x01"),
'period' => new Interval(2, 1500),
];
$value = ValueCodec::encode($document);
$decoded = ValueCodec::decodeDocument($value->getDocument());
echo $decoded->amount, ' ', $decoded->day, ' ', $decoded->time, PHP_EOL;
echo bin2hex($decoded->payload->bytes), ' ', $decoded->period->months, PHP_EOL;
$amount = ValueCodec::decode(ValueCodec::encode(Decimal::fromUnscaled('1990', 2)));
echo $amount, PHP_EOL;
Errors and cleanup
| Exception | When to expect it |
|---|---|
Polypheny\DriverException |
Base driver exception, extending PHP’s RuntimeException. Examples include a transport failure, a closed cursor or fetching without a result set. |
Polypheny\ServerException |
Subclass of DriverException for an error reported by Polypheny, such as invalid SQL. Read the server’s message with getMessage(). Roll back failed work before reusing the connection. |
Polypheny\ProtocolException |
Subclass of DriverException for an unsupported handshake or an invalid or unexpected Prism response. Check isClosed() before attempting further database work. |
InvalidArgumentException, UnexpectedValueException |
PHP exceptions that can surface for invalid input (such as a bad date or unsupported parameter value) or an unsupported value in a result. |
Use try/finally to close a cursor or connection when you finish with it. Destructors attempt cleanup when the objects are collected and suppress errors during that cleanup. Explicit close() lets the application detect and handle cleanup errors.