CHANGELOG.md
June 10, 2026 · View on GitHub
PHP ClickHouse wrapper - Changelog
======================
Unreleased
Breaking Changes
- Minimum PHP raised to 8.1 —
composer.jsonnow requiresphp: ^8.1and the CI matrix drops 8.0. PHP 8.0 users should pin1.26.4(last 8.0-compatible release); the typed>= 1.24.406line already targets 8.1+
Documentation
- Fixed the version-compatibility table in
README.mdand the Pages site (docs/index.md) — the8.0+row was contradictory (it implied 8.0 supports the latest release while>= 1.24.406requires 8.1+). Now8.0 → 1.6.0 – 1.26.4and8.1+ → >= 1.24.406 (current)
2026-06-10 [Release 1.26.610]
Bug Fixes
- Distributed DDL (
ON CLUSTER) formatting reworked (#241, #263, #262) —Http::prepareWrite()no longer appendsFORMAT JSONto the SQL ofON CLUSTERqueries. Instead the response format is requested via thedefault_format=JSONquery setting:CREATE USER / GRANT / ALTER ... ON CLUSTERno longer fail withSYNTAX_ERRORfrom a strayFORMAT JSON(#241)CREATE / ALTER ... ON CLUSTERresults are now parseable JSON again — no moreCan't find metawhen reading the per-host execution status (#263)- a bare
ON CLUSTERsubstring insideINSERTdata (e.g.'REGION CLUSTER') is no longer mistaken for the keyword —\bword boundaries match the real keyword only (#262)
- False-positive error detection on large JSON responses (#261) —
Statement::hasErrorClickhouse()now confirms a tail-regex match withjson_validate()(PHP 8.3+) before flagging an error, so valid JSON whose data legitimately contains ClickHouse exception text is not reported as a failure (@jradtilbrook)
Testing
- New
tests/ClickHouse26/OnClusterTest.php— distributed DDL regression tests (CREATE/ALTER/CREATE USER ... ON CLUSTER); skips gracefully when no Keeper/ZooKeeper backend is configured - Embedded ClickHouse Keeper added to the test environment (
tests/clickhouse-latest-config/keeper.xml, mounted indocker-compose.yamland copied in CI) so the single-nodedefaultcluster can executeON CLUSTERqueries testInsertWithOnClusterInDataadded to both CH 21 and CH 26 client tests (#262)
Merged PRs
- #261 — Fix false positive error detection when JSON data contains ClickHouse exception text (@jradtilbrook)
Closed Issues
- #241 — ALTER / CREATE / DROP / RENAME queries fail when using ON CLUSTER
- #262 — extra
FORMAT JSONin the query when inserting a record (superseded by thedefault_formatfix) - #263 — "Can't find meta" error since 1.24.406
2026-04-23 [Release 1.26.423]
Bug Fixes
- Escape single quotes and backslashes in scalar string native params (#258, #259) —
convertParamValue()now escapes\→\\and'→\'for scalarStringparams inselectWithParams()/writeWithParams(). Previously strings likehello\causedCANNOT_PARSE_ESCAPE_SEQUENCEin ClickHouse (@sander-hash, @tom-on-the-internet)
Testing
- 5 new native-params tests — scalar strings with trailing backslash, embedded backslash, single quote, injection attempt, and
writeWithParamswith backslash
Merged PRs
- #259 — Added escape for scalar string (@sander-hash)
Closed Issues
- #258 — Scalar string params are not escaped, causing
CANNOT_PARSE_ESCAPE_SEQUENCE
2026-04-17 [Release 1.26.417]
Bug Fixes
- Correctly encode arrays for native param binding (#257) —
convertParamValue()now produces ClickHouse-compatible array literals['foo','bar'](single quotes) instead of JSON format["foo","bar"]. Previously anyArray(String)parameter inselectWithParams()/writeWithParams()failed with a parse error (@sander-hash) - Escape single quotes and backslashes in array strings — follow-up to #257. Strings containing
'or\are now properly escaped (e.g."it's"→'it\'s'), preventing query errors and array-element injection
Testing
- 6 new native-params tests — cover
Array(UInt32),Array(String), empty arrays, strings with single quotes, strings with backslashes, and injection attempts
Merged PRs
- #257 — Correctly encode array for native param binding (@sander-hash)
2026-04-12 [Release 1.26.412]
Bug Fixes
- Fix bindParams() TypeError with numeric keys (#256) —
bindParams()now casts integer array keys to string before passing tobindParam(string $column), restoring backward compatibility with numerically-indexed arrays. Affected downstream packages likephpclickhouse-laravel(@yehezkel-fullpath)
Testing
- Flaky tests excluded from default runs —
testConnectTimeout,testStreamInsert,testStreamInsertFormatJSONEachRowmarked@group flakyand excluded via PHPUnit config. Run separately with./vendor/bin/phpunit --group flaky - 2 new binding tests —
testBindParamsWithNumericKeys,testBindParamsWithMixedKeyscovering #256 regression
Closed Issues
- #256 — Breaking change: strict string type on
Bindings::bindParam()rejects integer keys
2026-04-10 [Release 1.26.410]
New Features
readWithParams()(#254) — stream query results with native ClickHouse typed parameters ({name:Type}syntax), combining server-side parameter binding with streaming output (@sander-hash)
Code Quality
- Stringable interfaces (#252) — all Type classes now explicitly implement
Stringablefor consistency (@sander-hash) - PHPStan test fixes (#253) — removed redundant assertions in static tests, improved assertion quality (@sander-hash)
- Documentation sync — updated GitHub Pages (docs/) with
readWithParams()in native-params, streaming, and per-query-settings guides
Merged PRs
- #252 — Add Stringable interfaces for types (@sander-hash)
- #253 — Fix phpstan errors in test files (@sander-hash)
- #254 — Added native params for read function (@sander-hash)
2026-04-06 [Release 1.24.406]
New Features
- Boolean Type (#251) — new
ClickHouseDB\Type\Booleanclass withfromString(),fromBool(),getValue(),__toString()(@sander-hash)
Code Quality
- PHP 8.1+ native type declarations — full migration across all 33 src/ files: property types, parameter types, return types. Interfaces (
Degeneration,IStream,Type) unchanged for backward compatibility - PR Security Review rules — added supply-chain attack detection checklist to CLAUDE.md (outbound network calls, data exfiltration, obfuscation, credential leakage patterns)
- Security audit — full code audit of src/, results saved in
secure-scan.md. All clean: no backdoors, exfiltration, or obfuscation found
Testing
- 153 new static unit tests — run without ClickHouse server:
ValueFormatterTest(28) — SQL escaping, types, DateTimeInterface, ExpressionStrictQuoteLineTest(32) — CSV/TSV/Insert formatting, encodeStringSettingsTest(24) — get/set/is, sessions, timeouts, readonlyWriteToFileTest(15) — file validation, formats, gzipTypesTest(26+5) — UInt64, DateTime64, MapType, TupleType, Boolean and moreBindingsUnitTest(16) — compile_binds, Conditions {if}/{else}CurlerRequestResponseTest(35) — headers, auth, URL, JSON, dump
- Total: 313+ tests (CH 21.9 + CH 26.3 + static), all passing
Merged PRs
- #251 — feat: Add Boolean type (@sander-hash)
2026-04-04 [Release 1.26.4]
New Features
Native Query Parameters — server-side typed parameter binding, SQL injection impossible at protocol level:
Client::selectWithParams()— SELECT with{name:Type}placeholdersClient::writeWithParams()— INSERT/DDL with{name:Type}placeholders- Parameters passed as
param_*in URL, server handles type conversion - Supports: int, float, string, bool, null, DateTime, arrays, and all custom Type classes
Per-Query Settings Override — override ClickHouse settings for individual queries:
- New
$querySettingsparameter (last, default[]) inselect(),selectAsync(),write() - Also in
selectWithParams(),writeWithParams() - Per-query settings merge with global; global settings stay unchanged after query
Generator Support — memory-efficient iteration for large resultsets (#166):
Client::selectGenerator()— streams from ClickHouse via JSONEachRow, yields one row at a timeStatement::rowsGenerator()— yields rows from already-fetched data- Supports bindings and per-query settings
ClickHouse Type Classes — 9 new types in src/Type/:
Int64— large signed integers (string-based, no PHP overflow)Decimal— exact decimal numbersUUID— UUID valuesIPv4,IPv6— IP address typesDateTime64— sub-second precision (fromString(),fromDateTime($dt, $precision))Date32— extended date range (1900–2299)MapType—Map(K, V)composite typeTupleType—Tuple(T1, T2, ...)composite type- All types work with
insert(), bindings (:param), and native parameters ({name:Type})
Structured Exceptions — enriched error information from ClickHouse:
DatabaseException::getClickHouseExceptionName()— e.g.UNKNOWN_TABLE,SYNTAX_ERROR(CH 22+)DatabaseException::getQueryId()— fromX-ClickHouse-Query-Idresponse headerDatabaseException::fromClickHouse()— factory method- Parses both old (
e.what() = DB::Exception) and new ((EXCEPTION_NAME) (version ...)) error formats
INSERT Statistics via X-ClickHouse-Summary (#233):
Statement::summary()— readsX-ClickHouse-Summaryresponse header (written_rows, written_bytes, etc.)Statement::statistics()— falls back to summary for INSERT queries (was always null before)
IPv6 Support — getUri() correctly wraps bare IPv6 addresses in brackets
AUTH_METHOD_NONE (0) — skip authentication for trusted/proxy setups
Custom curl Options — pass arbitrary CURLOPT_* via config curl_options or Http::setCurlOptions()
GitHub Actions CI — replaces Travis CI (#176):
- Matrix: PHP 8.0, 8.1, 8.2, 8.3, 8.4
- Two ClickHouse versions: 21.9 + 26.3.3.20
- PHPStan and PHPCS jobs
Bug Fixes
- Fix streaming OOM (#234) —
hasErrorClickhouse()no longer callsjson_decode()on large response bodies. Bodies > 4KB: only tail checked for error patterns. Prevents OOM when usingstreamRead()with large JSON resultsets - Fix progressFunction for write/insert (#191) — added
wait_end_of_query=1setting, required for ClickHouse to send progress headers during write operations - Fix null content_type (#243) —
hasErrorClickhouse()handles null content_type from curl without TypeError - Fix FORMAT JSON in DDL (#242) — FORMAT JSON no longer appended to CREATE, DROP, ALTER, RENAME statements
- Fix URL bindings with large inserts (#240) —
isUseInUrlBindingsParams()checks original SQL before degeneration, preventing false matches from data containing{foo:bar}patterns - Fix ping() timeout (#246) —
ping()now respectssetTimeout()value (was ignoring CURLOPT_TIMEOUT) - Remove deprecated curl_close() (#244) — no-op since PHP 8.0, deprecated in PHP 8.5
- Fix PHPStan errors —
$paramsundefined inQuery::getUrlBindingsParams(), missing return inCurlerResponse::dump() - Fix docblock (#247) —
$bindparam typestring[]→array<string, mixed>instreamRead()/streamWrite()
Code Quality
- PHPStan level 1 → 5 — with baseline for 60 pre-existing errors in curl layer. All new code must pass level 5
- PHPStan upgraded from 0.12 to 2.1 (supports PHP 8.4)
- phpVersion: 80406 set in PHPStan config
Testing
- Two ClickHouse test targets: 21.9 (port 8123) + 26.3.3.20 (port 8124)
- Two PHPUnit configs:
phpunit-ch21.xml(original tests) +phpunit-ch26.xml(adapted for CH 26 behavioral changes) - CH 26 adapted tests in
tests/ClickHouse26/: ClientTest, SessionsTest, StatementTest, UInt64Test - New test files: NativeParamsTest, PerQuerySettingsTest, StructuredExceptionTest, GeneratorTest, LargeStreamTest, TypesTest, SummaryTest, NullContentTypeTest, IPv6UriTest, AuthMethodNoneTest, CurlOptionsTest, ProgressWriteTest, PingTimeoutTest, QueryTest
- 160 tests (CH 21) + 146 tests (CH 26) — all passing
Documentation
- README.md restructured — concise overview with links to doc/
- doc/ — 13 documentation files:
basics.md— connection, select, insert, Statement APIasync.md— parallel queries, batch insertsbindings.md— parameter binding, SQL templatessettings.md— timeouts, HTTPS, auth, sessionsstreaming.md— streamRead/Write, closures, gzipcluster.md— multi-node setup, replicasadvanced.md— partitions, table sizes, progress, debugtypes.md— all 9 type classes with examplesnative-params.md— server-side{name:Type}parametersper-query-settings.md— per-query settings overridegenerators.md— selectGenerator(), rowsGenerator()progress.md— progressFunction for SELECT and INSERTexceptions.md— structured exceptionssummary.md— INSERT statistics via X-ClickHouse-Summary
- CLAUDE.md — project guidelines, architecture, contribution rules
- todo.md — development roadmap
Merged PRs
- #246 — fix: respect custom query timeout in ping() method (@abodnar)
- #244 — remove deprecated curl_close() call (@yehezkel-fullpath)
- #243 — fix: handle null content_type in hasErrorClickhouse() (@nzsakib)
- #242 — fix: remove FORMAT JSON from DDL statements (@jspeedz)
- #240 — fix: bindings in URL with large insert queries (@sniek-ie)
- #238 — fix: metadata parsing for distributed RENAME queries (@iTearo)
- #236 — support Enums in ValueFormatter.php (@wlkns)
Closed Issues
#234, #233, #227, #225, #223, #215, #209, #208, #201, #197, #196, #195, #194, #193, #191, #181, #176, #166, #150, #144, #136
2025-01-14 [Release 1.6.0]
- Support PHP 8.4
2024-01-18 [Release 1.5.3]
- Fix release 1.5.2
- Support php 7
- Update Statement.php #204
- fix(#202): Fix converting boolean when inserting into int and fix(#194): Fix unexpected readonly mode with specific string in query #203
- Update README.md #199
- remove dev files for --prefer-dist #192
2024-01-16 [Release 1.5.2]
- Update Statement.php #204
- fix(#202): Fix converting boolean when inserting into int and fix(#194): Fix unexpected readonly mode with specific string in query #203
- Update README.md #199
- remove dev files for --prefer-dist #192
May 25, 2023 [ 1.5.1 ]
- BREAKING CHANGES Post type bindings support
2022-12-20 [Release 1.5.0]
- Change exceptionCode in Clickhouse version 22.8.3.13 (official build) #180
- Fix Docker for tests, Change the correct Docker image name #177
- Some type fix
- Fix types: max_execution_time & setConnectTimeOut, undo: Support floats in timeout and connect_timeout #173
- add mbstring to composer require #183
- fixed progressFunction #182
- Add allow_plugins setting since Composer 2.2.x #178
2022-04-23 [Release 1.4.4]
- Fix ping() for windows users
- ping(true) throw TransportException if can`t connect/ping
2022-04-20 [Release 1.4.3]
- Fix: prevent enable_http_compression parameter from being overridden #164
- For correct work with utf-8 . I am working on server with PHP 5.6.40 Update CurlerRequest.php #158
- Add curl setStdErrOut, for custom StdOutErr.
- Fix some test for check exceptions
2022-02-11 [Release 1.4.2]
- Fixed issue with non-empty raw data processing during init() on every fetchRow() and fetchOne() call - PR #161
2021-01-19 [Release 1.4.1]
- Add support php 7.3 & php 8
2019-09-29 [Release 1.3.10]
- Add two new types of authentication #139
- Fixed typo in streamRead exception text #140
- fix the exception(multi-statement not allow) when sql end with ';' #138
- Added more debug info for empty response with error #135
2020-02-03 [Release 1.3.9]
- #134 Enhancement: Add a new exception to be able to distinguish that ClickHouse is not available.
2020-01-17 [Release 1.3.8]
- #131 Fix: async loop breaks after 20 seconds
- #129 Add client certificate support to able to work with Yandex ClickHouse cloud hosting
- Delete
dropOldPartitions - Fix error : The error of array saving #127
- More test
2019-09-20 [Release 1.3.7]
- #125 WriteToFile: support for JSONEachRow format
2019-08-24 [Release 1.3.6]
- #122 Add function fetchRow()
- Use X-ClickHouse-User by headers
- Add setDirtyCurler() in HTTP
- Add more tests
2019-04-29 [Release 1.3.5]
- Reupload 1.3.4
2019-04-29 [Release 1.3.4]
- #118 Fix Error in Conditions & more ConditionsTest
- Fix phpStan warnings in getConnectTimeOut() & max_execution_time()
2019-04-25 [Release 1.3.3]
- fix clickhouse release 19.5.2.6 error parsing #117
- chore(Travis CI): Enable PHP 7.3 testing #116
2019-03-18 [Release 1.3.2]
- fix: add CSVWithNames to supported formats #107
- Upgraded Expression proposal #106 -> UUIDStringToNum
- Correct query format parsing #108
- Can not use numbers() function in read requests #110
- #109 Do not use keep-alive or reuse them across requests
2018-09-25 [Release 1.3.1]
- Pull request #94 from simPod: Uint64 values
- Pull request #95 from simPod: Bump to php 7.1
2018-09-11 [Release 1.2.4]
- Fix #91 ,Does not work inserting with the database name in the table
- pull request #90 from simPod: Refactor partitions()
2018-08-30 [Release 1.2.3]
- Escape values in arrays, pull request #87 from simPod/fix-escape
- fix-bindings: pull request #84 from simPod/fix-bindings
- Added quotes arount table and column names in the insert wrapper.
- Docker Compose in tests
2018-07-24 [Release 1.2.2]
- Connection without port
2018-07-16 [Release 1.2.1]
- New
$client->getServerVersion() - Rewrite method
$client->ping() - Fix
include.php- ClickHouseException before exceptions - Add CHANGELOG.md
- New
interface ClickHouseException
2018-07-06 [Release 1.2.0]
- Fix
SelectAsync() & executeAsync(), some task freeze
2018-07-04 [Release 1.1.2]
- Republic 1.1.1
2018-07-02 [Release 1.1.1]
- #47 Bindings wrong work - fix
2018-07-02 [Release 1.1.0]
New:
$client->getServerUptime()Returns the server's uptime in seconds.$client->getServerSystemSettings()Read system.settings table and return array$client->streamWrite()function$client->streamRead()function
Warning:
- Now default enable
HttpCompressionset true - Deprecated
StreamInsertclass
Fix:
- Fix
rawData()result inJSONCompact & JSONEachRowformat - Fix Statement - unnecessary memory usage
- Fix support php5.6
2018-06-29 [Release 1.0.1]
- Do not convert int parameters in array to string in Bindings pull 67
2018-06-25 [Release 1.0.0]
- Use Semantic versioning
2018-06-22
- Fix
tableSize('name')andtablesSize()
2018-06-19
- Add DataTime Interface for Bind
- Fix phpDoc
Composer->require->"php": ">=5.6"
2018-05-09
- Move
\ClickHouseDB\WhereInFileto\ClickHouseDB\Query\WhereInFile - Move
\ClickHouseDB\QueryExceptionto\ClickHouseDB\Exception\QueryException - Move
\ClickHouseDB\DatabaseExceptiontoClickHouseDB\Exception\DatabaseException - Move
\ClickHouseDB\FormatLineto\ClickHouseDB\Quote\FormatLine - Move
\ClickHouseDB\WriteToFiletoClickHouseDB\Query\WriteToFile - Move
\Curler\Requestto\ClickHouseDB\Transport\CurlerRequest - Move
\Curler\CurlerRollingto\ClickHouseDB\Transport\CurlerRolling - Up to php 7.2 & phpunit 7.1 for Dev & Prs4 Autoloading
2018-03-26
- Fix StreamInsert : one stream work faster and safe than loop #PR43
- Fix cluster->clientLike()
2017-12-28
- Fix
FORMAT JSONif set FORMAT in sql - GetRaw() - result raw response if not json
SELECT number as format_id FROM system.numbers LIMIT 3 FORMAT CSVWithNames
2017-12-22
- progressFunction()
- Escape values
2017-12-12
- Not set
FORMAT JSONif set FORMAT in sql
2017-11-22
- Add insertAssocBulk
2017-08-25
- Fix tablesSize(), use database filter
- Fix partitions(), use database filter
2017-08-14
- Add session_id support
2017-02-20
- Build composer 0.17.02
2016-12-09
- for ReadOnly users need set :
client->setReadOnlyUser(true);or$confi['readonly'], see exam19_readonly_user.php
2016-11-25
client->truncateTable('tableName')cluster->getMasterNodeForTable('dbName.tableName') // node have is_leader=1cluster->getSizeTable('dbName.tableName')cluster->getTables()cluster->truncateTable('dbName.tableName')- See example cluster_06_truncate_table.php
2016-11-24
- add
cluster->setSoftCheck() - insertBatchFiles() support
$file_names- string or array ,$columns_array- array or null - add insertBatchStream() return
\Curler\Requestno exec - writeStreamData() return
\Curler\Request - fix httpCompression(false)
- getHeaders() as array from
\Curler\Request setReadFunction( function() )inRequest- Add class StreamInsert, direct read from stream_resource to clickhouse:stream
2016-11-04
- add
$db->insertBatchTSVFiles(), - add format param in
$db->insertBatchFiles(,,,format), - deprecated class CSV
- Add static class
\ClickHouseDB\FormatLine:CSV(),\ClickHouseDB\FormatLine:TSV(),\ClickHouseDB\FormatLine:Insert() - CSV RFC4180 -
\ClickHouseDB\FormatLine::CSV(Array))."\n" - Update exam12_array.php + unit tests
2016-11-03
$db->enableLogQueries(true)- write to system.query_log$db->enableExtremes(true);- default extremes now, disabled$db->isExists($database,$table)
2016-10-27
- add Connect timeout , $db->setConnectTimeOut(5);
- change default ConnectTimeOut = 5 seconds. before 1 sec.
- change DNS_CACHE default to 120 seconds
2016-10-25 Release 0.16.10
- fix timeout error and add test
2016-10-23
- client->setTimeout($seconds)
- cluster->clientLike(ip_addr_like)
- Delete all migration code from driver, move to https://github.com/smi2/phpMigrationsClickhouse
2016-09-20 Release 0.16.09
- Version/Release names: [ zero dot year dot month]
- Support cluster: new class Cluster and ClusterQuery
- output_format_write_statistics, for clickhouse version > v1.1.54019-stable
- WriteToFile in select,selectAsync
- Degeneration for Bindings & Conditions
- $db->select(new Query("Select..."));
- remove findActiveHostAndCheckCluster , clusterHosts , checkServerReplicas
- Add cleanQueryDegeneration(),addQueryDegeneration()
- Need $db->enableQueryConditions(); for use Conditions ; default Conditions - disabled;
- float in CurlerRequest->timeOut(2.5) = 2500 ms
- tablesSize() - add
sizebytes
2016-08-11 Release 0.2.0
- exception on error write
2016-08-06 Release 0.1.0
- init