PHPackages                             k-kinzal/ztd-query-core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. k-kinzal/ztd-query-core

ActiveLibrary[Database &amp; ORM](/categories/database)

k-kinzal/ztd-query-core
=======================

Zero Table Dependencies query layer for PHP

v0.1.1(2w ago)016↓100%2MITPHPPHP ^8.1

Since Mar 6Pushed 2w agoCompare

[ Source](https://github.com/k-kinzal/ztd-query-core)[ Packagist](https://packagist.org/packages/k-kinzal/ztd-query-core)[ RSS](/packages/k-kinzal-ztd-query-core/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (6)Versions (3)Used By (2)

ZTD Query PHP
=============

[](#ztd-query-php)

[![License: MIT](https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667)](https://opensource.org/licenses/MIT)[![PHP Version](https://camo.githubusercontent.com/7535257ca228724c93658bd52583d4e47a9bab02c356abf6e54c1d575f2151e6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e312532422d626c75652e737667)](https://www.php.net/)

A Zero Table Dependency testing library for PHP 8.1+ that enables SQL unit testing without modifying physical databases.

Overview
--------

[](#overview)

ZTD Query PHP wraps PDO to intercept and transform SQL queries using CTE (Common Table Expression) shadowing. This allows you to:

- Test SQL queries against fixture data without migrations, data seeding, or cleanup
- Use the real MySQL engine for query execution (not mocks)
- Run tests in parallel with complete isolation
- Treat SQL as pure functions: input (fixtures) -&gt; output (results)

### How It Works

[](#how-it-works)

**CTE Shadowing** - Table references in SELECT queries are replaced with CTEs containing your fixture data:

```
-- Original query
SELECT email FROM users WHERE id = 1

-- Transformed query (with fixture data)
WITH users AS (
  SELECT 1 AS id, 'alice@example.com' AS email
  UNION ALL
  SELECT 2 AS id, 'bob@example.com' AS email
)
SELECT email FROM users WHERE id = 1
```

**Result Select Query** - INSERT/UPDATE/DELETE statements are converted to SELECT queries that return the affected rows:

```
-- Original
UPDATE users SET name = 'Alice' WHERE id = 1

-- Transformed (returns rows that would be affected)
WITH users AS (...fixture data...)
SELECT id, 'Alice' AS name FROM users WHERE id = 1
```

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- MySQL 5.6 - 9.1
- PDO extension

Installation
------------

[](#installation)

```
composer require --dev k-kinzal/ztd-query-php
```

Usage
-----

[](#usage)

### Basic Example

[](#basic-example)

```
use ZtdQuery\Adapter\Pdo\ZtdPdo;

// Create ZTD-wrapped PDO connection
$pdo = new ZtdPdo('mysql:host=localhost;dbname=test', 'user', 'password');

// Define schema and insert fixture data
$pdo->exec('CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255), email VARCHAR(255))');
$pdo->exec("INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.com')");
$pdo->exec("INSERT INTO users (id, name, email) VALUES (2, 'Bob', 'bob@example.com')");

// Execute queries against fixture data (no physical table access)
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([1]);
$result = $stmt->fetchAll();
// Returns: [['id' => 1, 'name' => 'Alice', 'email' => 'alice@example.com']]
```

### Wrapping Existing PDO

[](#wrapping-existing-pdo)

```
use ZtdQuery\Adapter\Pdo\ZtdPdo;

$existingPdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');

// Wrap without creating a new connection
$ztdPdo = ZtdPdo::fromPdo($existingPdo);
```

### Testing Write Operations

[](#testing-write-operations)

```
$pdo->exec('CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))');
$pdo->exec("INSERT INTO users (id, name) VALUES (1, 'Alice')");

// INSERT returns the inserted row data
$stmt = $pdo->prepare('INSERT INTO users (id, name) VALUES (?, ?)');
$stmt->execute([2, 'Bob']);
$inserted = $stmt->fetchAll();
// Returns: [['id' => 2, 'name' => 'Bob']]

// UPDATE returns the updated row data
$stmt = $pdo->prepare('UPDATE users SET name = ? WHERE id = ?');
$stmt->execute(['Alice Updated', 1]);
$updated = $stmt->fetchAll();
// Returns: [['id' => 1, 'name' => 'Alice Updated']]

// DELETE returns the deleted row data
$stmt = $pdo->prepare('DELETE FROM users WHERE id = ?');
$stmt->execute([1]);
$deleted = $stmt->fetchAll();
// Returns: [['id' => 1, 'name' => 'Alice']]
```

### Enabling/Disabling ZTD Mode

[](#enablingdisabling-ztd-mode)

```
$pdo = new ZtdPdo($dsn, $user, $password);

// Disable ZTD to execute against physical database
$pdo->disableZtd();
$pdo->exec('CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))');

// Re-enable ZTD for testing
$pdo->enableZtd();
```

Configuration
-------------

[](#configuration)

```
use ZtdQuery\Adapter\Pdo\ZtdPdo;
use ZtdQuery\Config\ZtdConfig;
use ZtdQuery\Config\UnsupportedSqlBehavior;
use ZtdQuery\Config\UnknownSchemaBehavior;

$config = new ZtdConfig(
    // How to handle unsupported SQL statements (default behavior)
    unsupportedBehavior: UnsupportedSqlBehavior::Exception, // or Ignore, Notice

    // How to handle references to unknown tables
    unknownSchemaBehavior: UnknownSchemaBehavior::Exception, // or Passthrough

    // Per-pattern behavior rules (first match wins)
    behaviorRules: [
        // Prefix-based rules (case-insensitive)
        'BEGIN' => UnsupportedSqlBehavior::Ignore,
        'COMMIT' => UnsupportedSqlBehavior::Ignore,
        'ROLLBACK' => UnsupportedSqlBehavior::Ignore,

        // Regex-based rules (patterns starting with '/')
        '/^SET\s+SESSION/i' => UnsupportedSqlBehavior::Ignore,
        '/^SET\s+/i' => UnsupportedSqlBehavior::Notice,
    ],
);

$pdo = new ZtdPdo($dsn, $user, $password, config: $config);
```

### Configuration Options

[](#configuration-options)

OptionValuesDescription`unsupportedBehavior``Ignore`, `Notice`, `Exception`Default behavior when unsupported SQL is executed`unknownSchemaBehavior``Passthrough`, `Exception`Behavior when unknown table is referenced`behaviorRules``array`Per-pattern behavior overrides (first match wins)SQL Support
-----------

[](#sql-support)

### Fully Supported

[](#fully-supported)

- **SELECT**: All clauses including JOIN, GROUP BY, HAVING, ORDER BY, LIMIT, UNION, subqueries, CTEs, window functions
- **INSERT**: VALUES, SELECT, ON DUPLICATE KEY UPDATE, IGNORE
- **REPLACE**
- **UPDATE**: Single/multi-table with ORDER BY/LIMIT
- **DELETE**: Single/multi-table with ORDER BY/LIMIT
- **TRUNCATE**
- **DDL**: CREATE TABLE, ALTER TABLE, DROP TABLE (virtual schema)
- **WITH**: CTE and recursive CTE

### Ignored (No-op)

[](#ignored-no-op)

- Transaction control: BEGIN, COMMIT, ROLLBACK, SAVEPOINT

### Unsupported

[](#unsupported)

- Stored procedures, triggers, functions, views
- Database/schema operations
- User/permission management
- Server operations (FLUSH, RESET, etc.)

Development
-----------

[](#development)

```
# Run tests
composer test

# Run unit tests
composer test:unit

# Run linter (PHP-CS-Fixer + PHPStan level max)
composer lint

# Fix code style
composer format
```

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

38

—

LowBetter than 86% of packages

Maintenance97

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

16d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4458a22db62c885006248e08715301bcb73e78c1057e2bbf686af001729165d5?d=identicon)[kinzal](/maintainers/kinzal)

---

Top Contributors

[![k-kinzal](https://avatars.githubusercontent.com/u/1281825?v=4)](https://github.com/k-kinzal "k-kinzal (2 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/k-kinzal-ztd-query-core/health.svg)

```
[![Health](https://phpackages.com/badges/k-kinzal-ztd-query-core/health.svg)](https://phpackages.com/packages/k-kinzal-ztd-query-core)
```

###  Alternatives

[doctrine/orm

Object-Relational-Mapper for PHP

10.2k280.5M5.6k](/packages/doctrine-orm)[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k114.4M86](/packages/jdorn-sql-formatter)[illuminate/database

The Illuminate Database package.

2.8k51.5M8.2k](/packages/illuminate-database)[mongodb/mongodb

MongoDB driver library

1.6k62.7M499](/packages/mongodb-mongodb)[spatie/laravel-translatable

A trait to make an Eloquent model hold translations

2.4k22.1M350](/packages/spatie-laravel-translatable)[kirschbaum-development/eloquent-power-joins

The Laravel magic applied to joins.

1.5k23.2M26](/packages/kirschbaum-development-eloquent-power-joins)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
