Download the PHP package rasuvaeff/clickhouse-toolkit without Composer
On this page you can find all versions of the php package rasuvaeff/clickhouse-toolkit. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download rasuvaeff/clickhouse-toolkit
More information about rasuvaeff/clickhouse-toolkit
Files in rasuvaeff/clickhouse-toolkit
Package clickhouse-toolkit
Short Description Framework-agnostic ClickHouse toolkit for PHP: parameterized query builder, data reader, batch writer, DDL builder, partition manager, mutation builder, and migration runner.
License BSD-3-Clause
Homepage https://github.com/rasuvaeff/clickhouse-toolkit
Informations about the package clickhouse-toolkit
ClickHouse Toolkit
Lightweight, framework-agnostic ClickHouse helpers for PHP applications.
ClickHouseClientFactory+ClickHouseConfig— build a configured client over any PSR-18 HTTP client (auto-discovered or injected; HTTP/HTTPS).ClickHouseQueryBuilder— turnyiisoft/datafilters and sort into safe, parameterized SQL.ClickHouseFilterVisitor+ClickHouseSqlFilterVisitor— extensible visitor for SQL generation per filter type.ClickHouseDataReader— an immutableDataReaderInterfaceready for yiisoft/data paginators.ClickHouseBatchWriter— buffered, batched inserts.ClickHouseTableBuilder— fluentCREATE TABLEDDL.ClickHousePartitionManager— list / drop / detach / attach / move / freeze partitions.ClickHouseMutationBuilder— asyncALTER … UPDATE/DELETEwith mutation tracking.ClickHouseMigrationRunner— idempotent, checksum-verified*.sqlmigrations.ClickHouseDataType— type-name constants and factories for parametric/nested types.
Built on top of simpod/clickhouse-client. The query/reader pieces integrate with the yiisoft/data reader abstractions, so they slot naturally into Yii3 admin grids and paginated APIs, but nothing here requires the full framework.
Using an AI coding assistant?
llms.txtis a compact, self-contained reference of the whole public API plus copy-paste recipes — drop it into the model's context. Contributors: seeAGENTS.md.
Table of contents
- Requirements
- Installation
- Quick start
- Components
- ClickHouseConfig & ClickHouseClientFactory
- ClickHouseQueryBuilder & WhereClause
- ClickHouseFilterVisitor
- ClickHouseDataReader
- ClickHouseBatchWriter
- ClickHouseTableBuilder
- ClickHousePartitionManager
- ClickHouseMutationBuilder
- ClickHouseDataType
- ClickHouseMigrationRunner
- Interfaces
- Timezone handling
- Dependency injection
- Security notes
- What is intentionally not included
- Examples
- Development
- License
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.3 |
| A PSR-18 HTTP client + PSR-17 factories | any implementation |
| ClickHouse server | tested against 23.x – 26.x over the HTTP interface (port 8123) |
The toolkit depends only on interfaces (psr/http-client, psr/http-factory, psr/log, php-http/discovery, simpod/clickhouse-client, yiisoft/data) — not on any concrete HTTP client. It auto-discovers an installed PSR-18 client/PSR-17 factories via php-http/discovery, or you can inject your own.
Installation
You also need a PSR-18 client and PSR-17 factories if your project doesn't already ship one, e.g.:
Quick start
Components
ClickHouseConfig & ClickHouseClientFactory
ClickHouseConfig holds connection settings; ClickHouseClientFactory turns it into a SimPod\ClickHouseClient\Client\PsrClickHouseClient. The HTTP client and PSR-17 factories are auto-discovered (or injected). The endpoint is an absolute URI built from the config; authentication and database are sent via X-ClickHouse-* headers (an AuthenticatingHttpClient decorator), so credentials never appear in the URL.
To control timeouts, retries or TLS, build your own PSR-18 client and inject it (along with the PSR-17 factories you want):
ClickHouseQueryBuilder & WhereClause
Translates yiisoft/data filters and sort into parameterized ClickHouse SQL. The builder is the security boundary: only fields present in allowedFields are emitted in WHERE and ORDER BY; anything else is silently dropped. Comparison values become bound parameters with unique keys (p0, p1, …), so the same field may appear multiple times without collisions.
| Method | Returns | Description |
|---|---|---|
buildWhere(FilterInterface $filter) |
WhereClause |
{sql, params}; sql is empty when nothing matched. |
buildOrderBy(?Sort $sort) |
string |
ORDER BY fragment (allow-list-checked), or defaultSort; empty string means no ORDER BY. |
buildSelect(string $table, array $columns = [], string $where = '', ?string $orderBy = null, ?int $limit = 20, int $offset = 0) |
string |
columns empty → SELECT *; empty order → no ORDER BY; limit null → no LIMIT/OFFSET. |
buildCount(string $table, string $where = '') |
string |
SELECT count() AS cnt FROM .... |
buildDistinct(string $table, string $column) |
string |
SELECT DISTINCT col FROM ... ORDER BY col. |
WhereClause is a small DTO: public string $sql, public array $params, and isEmpty(): bool.
Supported filters
yiisoft/data filter |
Rendered as | Notes |
|---|---|---|
All |
empty WHERE |
|
None |
0 |
matches nothing |
Equals |
field = {p0:Type} |
|
GreaterThan / GreaterThanOrEqual |
field > / >= {p0:Type} |
|
LessThan / LessThanOrEqual |
field < / <= {p0:Type} |
|
EqualsNull |
field IS NULL |
no params |
In |
field IN ({p0:Type}, {p1:Type}, …) |
empty values → 0 (match nothing) |
Between |
field BETWEEN {p0:Type} AND {p1:Type} |
|
Like |
field ILIKE {p0:String} (or LIKE if caseSensitive) |
non-string fields are wrapped in toString(field); empty values are dropped; value bound + wildcard-escaped; honours LikeMode Contains/StartsWith/EndsWith |
Not |
NOT (...) |
dropped if the inner filter is empty |
AndX / OrX |
(a AND/OR b …) |
empty sub-filters skipped |
DateTimeInterface values are normalized to Y-m-d H:i:s; bool to 0/1.
Mandatory filters (tenant / owner / ACL)
The builder is fluent and immutable. withMandatoryFilter() attaches an
always-applied filter that is AND-combined with the user filter and bypasses
the allow-list (its fields need not be in allowedFields; identifiers are still
validated). This is the safe way to enforce access constraints — the user filter can
only narrow within it.
Raw expressions
ClickHouseRawFilter is a FilterInterface that emits a raw SQL fragment for things
the typed filters can't express. The SQL is trusted (never from user input); values
go in $params using {name:Type} placeholders whose names must not clash with the
builder's auto keys (p0, p1, …).
Full read + count cycle
ClickHouseFilterVisitor
The query builder delegates SQL generation to a visitor. ClickHouseFilterVisitor is the interface with a visit*() method per filter type; ClickHouseSqlFilterVisitor is the default implementation. Use dispatch(FilterInterface $filter, int &$index, bool $trusted) to route any filter to the right method.
Implement ClickHouseFilterVisitor and inject via withVisitor() to customise SQL generation:
ClickHouseDataReader
An immutable Yiisoft\Data\Reader\DataReaderInterface backed by a ClickHouse table. Filtering, sorting and pagination are delegated to the query builder; rows are mapped to your value type by a supplied mapper. It plugs straight into yiisoft/data paginators (OffsetPaginator, KeysetPaginator).
Implements read(), readOne(), count(), getIterator(), and the immutable withFilter/withSort/withLimit/withOffset (+ getters). With no limit set, read() omits LIMIT and returns the full result.
ClickHouseBatchWriter
Buffers rows and inserts them in fixed-size batches. Each row is projected onto the declared columns (extra keys dropped, missing keys → null), so loosely-shaped associative rows are fine. Failures are wrapped in ClickHouseWriteException.
Implements ClickHouseWriterInterface (write(iterable $rows): void).
ClickHouseTableBuilder
Fluent CREATE TABLE builder. build() returns the SQL; execute() runs it via
the client. The table name and column names are validated identifiers; column
types, the engine, and the ORDER BY / PARTITION BY / PRIMARY KEY expressions are
emitted verbatim — DDL is developer-authored, so keep them trusted.
build()/execute() throw if no columns or no engine were set.
ClickHousePartitionManager
Manages MergeTree partitions through ALTER TABLE … PARTITION. Partition
operations can't use bound parameters, so a partition is addressed by its id
(from getPartitions()) and emitted as an escaped PARTITION ID '…'; table and
column names are validated identifiers.
ClickHouseMutationBuilder
Submits and tracks mutations — ALTER TABLE … UPDATE/DELETE, the only way to
modify or delete existing rows. Mutations are asynchronous. The $set and
$condition fragments are trusted (developer-authored); pass user values as
bound {name:Type} parameters (ClickHouse supports parameters in ALTER).
ClickHouseMigrationRunner
Applies *.sql files from a directory in filename order, recording each applied file with a content checksum in a _migrations table.
- Idempotent — already-applied files are skipped.
- Tamper-evident — if an already-applied file's contents changed, a
ClickHouseMigrationExceptionis thrown instead of silently diverging. - One statement per file — contents are sent as a single query (no naive
;splitting). - Optional PSR-3 logging — pass a
LoggerInterfaceto log applied/skipped files.
Tracking table (created automatically):
Name files so lexicographic order equals execution order, e.g. 001_create_events.sql, 002_add_index.sql.
Concurrency & partial failure. ClickHouse has no transactions and the runner uses no distributed lock: the applied-list is read, then each file is executed and recorded separately. Two runners started at once may both run the same pending file, and if a file's DDL succeeds but the
_migrationsinsert does not, the next run repeats it. Run migrations from a single deploy step, prefer idempotent DDL (CREATE TABLE IF NOT EXISTS,ALTER TABLE ... ADD COLUMN IF NOT EXISTS), and wraprun()in an external lock if you need stronger guarantees.
ClickHouseDataType
Type-name constants and factories so type definitions are self-documenting and
typo-proof. Types are plain strings, usable anywhere one is expected
(ClickHouseTableBuilder columns, ClickHouseQueryBuilder field types).
Composite types (Enum, timezone-qualified DateTime) are for column definitions, not query-parameter types.
Interfaces
| Interface | Method(s) | Purpose |
|---|---|---|
ClickHouseMigrationRunnerInterface |
run(): list<string> |
Implemented by ClickHouseMigrationRunner. |
ClickHouseWriterInterface |
write(iterable $rows): void |
Implemented by ClickHouseBatchWriter. |
ClickHouseReaderInterface |
findByFilters(...), countByFilters(...) |
A simpler reader contract than DataReaderInterface; implement it per table when you don't need the full reader (see examples/EventReader.php). |
ClickHouseFilterVisitor |
visit*() per filter type |
SQL generation for each filter type. Implemented by ClickHouseSqlFilterVisitor. Inject a custom implementation via withVisitor(). |
Timezone handling
ClickHouseQueryBuilder accepts an optional serverTimezone (IANA name, e.g. "UTC", "Europe/Moscow"). When set, DateTimeInterface filter values are converted to that timezone before being formatted as Y-m-d H:i:s. This applies to filters whose value is a DateTimeInterface object (Equals, comparisons, Between); In values are scalar/string values and are passed as provided. Without serverTimezone, the object's own timezone is used (backward compatible).
Fluent: $qb->withServerTimezone('UTC') returns a new instance.
Dependency injection
Any PSR-11 container works. Example using Yiisoft DI definitions (Yii3):
See examples/di-container.php for a runnable plain-PHP container wiring.
Security notes
- Allow-list enforcement.
ClickHouseQueryBuilderonly emits allow-listed fields inWHEREandORDER BY(eachallowedFieldsentry is validated as an identifier at construction). Pass user-controlled filter/sort objects straight through — unknown fields are dropped. - Disallowed user filters are silently dropped (widening, not narrowing). For mandatory tenant/owner/ACL constraints do not rely on user filters — use
withMandatoryFilter(), which is always applied and AND-combined so the user filter can only narrow within it. - Bound parameters. All comparison/
In/Between/Likevalues are passed as ClickHouse bound parameters ({pN:Type}) with unique keys; values are never concatenated into SQL. Likeescaping.Likevalues are wildcard-escaped (addcslashes($value, '%_\\')) and bound as a parameter — the quote is not escaped (it lives in the parameter, not the SQL). EmptyLikevalues are dropped. Non-string fields are compared astoString(field) LIKE/ILIKE {pN:String}so user filters cannot make ClickHouse reject numeric/date columns.- Table/column names passed to
buildSelect/buildCount/buildDistinctand thecolumnsprojection are not escaped, but they are validated as plain SQL identifiers (db.tableallowed); a malformed identifier throwsInvalidArgumentException. Still pass trusted, plain identifiers — the validator rejects raw expressions (toDate(x) AS d), so build those yourself. - Pagination.
buildSelectrejects negativelimit/offsetwithInvalidArgumentException. orderBypassed tobuildSelect, and the constructor's non-emptydefaultSort, are trusted raw ORDER BY fragments — not validated. UsebuildOrderBy()output (allow-list-checked) or a hard-coded constant; never build them from untrusted input. The defaultdefaultSortis empty, so generic builders do not assume anidcolumn; set one explicitly for stable pagination.fieldTypestype tokens are validated (allowing parametric types likeArray(Nullable(String))) so they can't break out of the{name:Type}placeholder. They are developer configuration, not user input.- Credentials travel in
X-ClickHouse-*headers, not the URL.
What is intentionally not included
- Concrete readers/writers for specific tables (row shapes are app-specific — use
ClickHouseDataReaderwith a mapper, or implementClickHouseReaderInterface). - A migration generator or rollback/down migrations.
- Connection pooling or retries.
- Framework bootloaders/service providers (wire it in your app — see Dependency injection).
Examples
Runnable, self-contained examples live in examples/:
| File | Server? | Shows |
|---|---|---|
query-builder.php |
no | Every supported filter/sort/select/count/distinct — prints the generated SQL. |
di-container.php |
no | Wiring the toolkit into a PSR-11 container. |
client.php |
yes | Building a client and running a query. |
migrations/ |
yes | Applying *.sql migrations idempotently. |
batch-writer.php |
yes | Batched inserts via ClickHouseBatchWriter. |
EventReader.php |
yes | A ClickHouseReaderInterface implementation with row mapping. |
data-reader.php |
yes | Immutable ClickHouseDataReader (paginator-ready). |
See examples/README.md for how to run them.
Development
Integration tests in tests/Integration/ run end-to-end against a real server and are skipped unless CLICKHOUSE_HOST is set:
CI runs composer build on PHP 8.3, 8.4, and 8.5.
License
BSD-3-Clause. See LICENSE.md.
All versions of clickhouse-toolkit with dependencies
php-http/discovery Version ^1.20
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
psr/http-message Version ^2.0
psr/log Version ^3.0
simpod/clickhouse-client Version ^0.8.3
symfony/console Version ^7.2
yiisoft/data Version ^2.0