1. Go to this page and download the library: Download hiblaphp/mysql library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
hiblaphp / mysql example snippets
use Hibla\Mysql\MysqlClient;
use function Hibla\await;
// The client is lazy by default and no connections are opened until the first query.
$client = new MysqlClient('mysql://test_user:[email protected]/test');
// Simple query — short, stays on one line
$users = await($client->query('SELECT * FROM users WHERE active = ?', [true]));
echo $users->rowCount;
// Named parameters — args are long, so wrap the call
$user = await(
$client->query(
'SELECT * FROM users WHERE email = :email AND status = :status',
['email' => '[email protected]', 'status' => 'active']
)
);
// Prepared statement (recommended for repeated execution)
$stmt = await($client->prepare('SELECT * FROM users WHERE email = :email'));
$result = await($stmt->execute(['email' => '[email protected]']));
$stmt->close();
// Streaming large result sets
$stream = await($client->stream('SELECT * FROM logs ORDER BY id DESC'));
foreach ($stream as $row) {
processLog($row);
}
use function Hibla\await;
use Hibla\Promise\Promise;
// Three queries run concurrently. Connections are borrowed from the pool
// (and created on demand) only as each query starts.
[$users, $orders, $stats] = await(Promise::all([
$client->query('SELECT * FROM users'),
$client->query('SELECT * FROM orders'),
$client->query('SELECT COUNT(*) FROM stats'),
]));
use Hibla\Sql\SqlClientInterface;
use Hibla\Sql\Transaction;
// Your service depends on the contract, not the MySQL-specific implementation.
class UserRepository
{
public function __construct(private readonly SqlClientInterface $db) {}
}
$base = new MysqlConfig(
host: 'db.internal',
username: 'app',
password: 'secret',
charset: 'utf8mb4',
ssl: true,
sslVerify: true,
sslCa: '/etc/ssl/ca.pem',
);
// One client per database, sharing all connection-level settings
$userDb = new MysqlClient(MysqlConfig::fromArray([...(array) $base, 'database' => 'users']), maxConnections: 10);
$reportDb = new MysqlClient($base->withQueryCancellation(true), maxConnections: 2);
use Hibla\Mysql\MysqlClient;
// From DSN string — lazy, no connections opened yet
$client = new MysqlClient('mysql://user:pass@localhost:3306/mydb');
// From array
$client = new MysqlClient([
'host' => '127.0.0.1',
'port' => 3306,
'username' => 'test_user',
'password' => 'test_password',
'database' => 'test',
'charset' => 'utf8mb4',
]);
// With explicit pool settings
$client = new MysqlClient(
config: 'mysql://...',
minConnections: 0,
maxConnections: 20,
idleTimeout: 300,
maxLifetime: 3600,
statementCacheSize: 512,
enableStatementCache: true,
maxWaiters: 100,
acquireTimeout: 10.0,
enableServerSideCancellation: true,
resetConnection: true,
multiStatements: false,
onConnect: function (ConnectionSetup $setup) {
await($setup->execute("SET SESSION time_zone = '+00:00'"));
},
);
$result = await($client->query('SELECT * FROM users LIMIT 10'));
$result = await(
$client->query(
'SELECT id, name, email FROM users WHERE created_at > ? AND status = ?',
[$since, 'active']
)
);
// Named params in query()
$result = await(
$client->query(
'SELECT * FROM users WHERE status = :status AND created_at > :since',
['status' => 'active', 'since' => $since]
)
);
// Order of keys does not matter
$result = await(
$client->query(
'INSERT INTO orders (user_id, total, status) VALUES (:userId, :total, :status)',
['status' => 'pending', 'total' => 99.99, 'userId' => 42]
)
);
// Named params via prepare() — most useful when executing the same statement repeatedly
$stmt = await(
$client->prepare(
'SELECT * FROM products WHERE category_id = :categoryId AND price > :minPrice'
)
);
$electronics = await($stmt->execute(['categoryId' => 1, 'minPrice' => 50.00]));
$clothing = await($stmt->execute(['categoryId' => 2, 'minPrice' => 25.00]));
$stmt->close();
// Returns affected row count
$count = await(
$client->execute(
'UPDATE users SET last_login = NOW() WHERE id = :id',
['id' => $userId]
)
);
// Returns last insert ID
$lastId = await(
$client->executeGetId(
'INSERT INTO users (name, email) VALUES (:name, :email)',
['name' => 'Alice', 'email' => '[email protected]']
)
);
// Returns first row as associative array, or null
$user = await($client->fetchOne('SELECT * FROM users WHERE id = :id', ['id' => $userId]));
// Returns value of first column (or named column) from first row
$name = await($client->fetchValue('SELECT name FROM users WHERE id = :id', ['id' => $userId]));
// Positional placeholders
$stmt = await($client->prepare('SELECT * FROM products WHERE category_id = ? AND price > ?'));
$result1 = await($stmt->execute([1, 50.00]));
$result2 = await($stmt->execute([2, 100.00]));
$stmt->close();
// Named placeholders — order of keys in execute() does not matter
$stmt = await(
$client->prepare(
'SELECT * FROM products WHERE category_id = :categoryId AND price > :minPrice'
)
);
$result1 = await($stmt->execute(['categoryId' => 1, 'minPrice' => 50.00]));
$result2 = await($stmt->execute(['minPrice' => 100.00, 'categoryId' => 2]));
$stmt->close();
$stream = await($client->stream('SELECT * FROM large_table ORDER BY id', bufferSize: 200));
// Inspect stream metadata before iterating
echo $stream->columnCount;
print_r($stream->columns);
foreach ($stream as $row) {
processRow($row);
}
$stmt = await(
$client->prepare(
'SELECT * FROM logs WHERE created_at > :since AND level = :level'
)
);
$stream = await($stmt->executeStream(['since' => $since, 'level' => 'error']));
echo $stream->columnCount;
print_r($stream->columns);
foreach ($stream as $row) {
processRow($row);
}
$result = await(
$client->transaction(function (TransactionInterface $tx) use ($from, $to) {
await($tx->execute(
'UPDATE accounts SET balance = balance - :amount WHERE id = :id',
['amount' => 100, 'id' => $from]
));
await($tx->execute(
'UPDATE accounts SET balance = balance + :amount WHERE id = :id',
['amount' => 100, 'id' => $to]
));
return 'Transfer completed';
})
);
await(
$client->transaction(
function (TransactionInterface $tx) use ($from, $to) {
await($tx->execute(
'UPDATE accounts SET balance = balance - :amount WHERE id = :id',
['amount' => 100, 'id' => $from]
));
await($tx->execute(
'UPDATE accounts SET balance = balance + :amount WHERE id = :id',
['amount' => 100, 'id' => $to]
));
},
TransactionOptions::default()
->withAttempts(3)
->withIsolationLevel(IsolationLevel::REPEATABLE_READ)
)
);
use Hibla\Sql\TransactionOptions;
use Hibla\Sql\IsolationLevel;
$options = TransactionOptions::default()
->withAttempts(5)
->withIsolationLevel(IsolationLevel::SERIALIZABLE)
->withRetryableExceptions([MyOptimisticLockException::class]);
class MyOptimisticLockException extends \RuntimeException
implements \Hibla\Sql\Exceptions\RetryableException {}
// Retry by class list
$options = TransactionOptions::default()
->withAttempts(3)
->withRetryableExceptions([ThirdPartyConflictException::class]);
// Retry by predicate
$options = TransactionOptions::default()
->withAttempts(3)
->withRetryableExceptions(
fn(\Throwable $e) => $e instanceof ThirdPartyConflictException && $e->getCode() === 409
);
$tx = await($client->beginTransaction());
try {
await($tx->execute(
'UPDATE accounts SET balance = balance - :amount WHERE id = :id',
['amount' => 100, 'id' => $from]
));
await($tx->execute(
'UPDATE accounts SET balance = balance + :amount WHERE id = :id',
['amount' => 100, 'id' => $to]
));
await($tx->commit());
} catch (\Throwable $e) {
await($tx->rollback());
throw $e;
}
$tx = await($client->beginTransaction());
try {
await($tx->savepoint('before_risky'));
try {
await($tx->execute(
'INSERT INTO external_refs (id) VALUES (:id)',
['id' => $externalId]
));
} catch (\Throwable $e) {
// Rolling back to the savepoint also clears the tainted state,
// so queries after this point are allowed to continue.
await($tx->rollbackTo('before_risky'));
}
await($tx->releaseSavepoint('before_risky'));
await($tx->commit());
} catch (\Throwable $e) {
await($tx->rollback());
throw $e;
}
$promise = $client->transaction(function (TransactionInterface $tx) {
await($tx->execute('UPDATE ...'));
await($tx->execute('UPDATE ...')); // still running when cancelled
});
Loop::addTimer(2.0, fn() => $promise->cancel());
// → running query is interrupted, ROLLBACK is issued, connection returned to pool
await(
$client->transaction(function (TransactionInterface $tx) use ($externalId) {
await($tx->execute(
'INSERT INTO audit_log (event) VALUES (:event)',
['event' => 'attempt']
));
await($tx->savepoint('before_risky_op'));
try {
await($tx->execute(
'INSERT INTO external_refs (id) VALUES (:id)',
['id' => $externalId]
));
} catch (\Throwable $e) {
// Rolls back to the savepoint and clears the tainted state.
await($tx->rollbackTo('before_risky_op'));
}
await($tx->releaseSavepoint('before_risky_op'));
})
);
await(
$client->transaction(function (TransactionInterface $tx) use ($user) {
await($tx->execute(
'INSERT INTO users (name, email) VALUES (:name, :email)',
['name' => $user->name, 'email' => $user->email]
));
// Fires only if the COMMIT succeeds
$tx->onCommit(function () use ($user) {
EventDispatcher::dispatch(new UserCreated($user));
});
// Fires if the transaction rolls back (e.g., constraint violation, cancellation)
$tx->onRollback(function () use ($user) {
Logger::warning("Failed to persist user: {$user->email}");
});
})
);
$result = await($client->query('CALL get_user_with_orders(?)', [$userId]));
foreach ($result as $row) {
echo $row['name'];
}
$orders = $result->nextResult();
if ($orders !== null) {
foreach ($orders as $order) {
echo $order['total'];
}
}
$client = new MysqlClient([
'host' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'app',
'multi_statements' => true,
]);
$result = await(
$client->query('SELECT * FROM users; SELECT * FROM orders; SELECT COUNT(*) FROM stats')
);
foreach ($result as $row) { ... } // users
foreach ($result->nextResult() as $row) { ... } // orders
$count = $result->nextResult()->nextResult()->fetchOne();
// Graceful — stops new work, waits for active queries to finish, then closes
await($client->closeAsync(timeout: 30.0));
// Force — closes everything immediately, rejects pending waiters
$client->close();
$client = new MysqlClient(
config: $config,
enableStatementCache: true,
statementCacheSize: 512
);
$client->clearStatementCache(); // Invalidate all caches (e.g. after schema changes)
$result = await($client->query('SELECT * FROM users'));
echo $result->rowCount; // int — rows in result set
echo $result->affectedRows; // int — rows affected by INSERT/UPDATE/DELETE
echo $result->lastInsertId; // int — last auto-increment ID
echo $result->warningCount; // int — MySQL warnings generated
echo $result->connectionId; // int — server thread ID
echo $result->columnCount; // int — number of columns
foreach ($result->fields as $col) {
echo $col->name . ': ' . $col->typeName; // e.g. "price: DECIMAL"
}
foreach ($result as $row) {
echo $row['name'];
}
$row = $result->fetchOne();
$all = $result->fetchAll();
$col = $result->fetchColumn('name');
// Default — native PHP types from the binary protocol
$client = new MysqlClient($config);
$row = await($client->fetchOne('SELECT id, price FROM products WHERE id = ?', [1]));
// $row['id'] => int(1)
// $row['price'] => string("19.99") — DECIMAL always comes back as string
// Cast disabled — everything is a string
$client = new MysqlClient($config, castPreparedTypes: false);
$row = await($client->fetchOne('SELECT id, price FROM products WHERE id = ?', [1]));
// $row['id'] => string("1")
// $row['price'] => string("19.99")
$result = await(
$client->query('SELECT price FROM products WHERE id = :id', ['id' => 1])
);
$row = $result->fetchOne();
// Correct — preserve the exact string value from the server
$price = $row['price']; // "19.99" (string)
// Also correct — use bcmath for arithmetic on decimals
$tax = bcmul($row['price'], '0.20', 2); // "4.00"
$total = bcadd($row['price'], $tax, 2); // "23.99"
// WRONG — loses precision for large or high-decimal-place values
$price = (float) $row['price']; // may not round-trip exactly
$result = await($client->query('CALL get_user_with_orders(?)', [$userId]));
foreach ($result as $row) { ... } // first result set
$next = $result->nextResult();
if ($next !== null) {
foreach ($next as $row) { ... } // second result set
}
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.