Skip to content

Parameter binding

Greg Bowler edited this page Apr 2, 2026 · 4 revisions

Parameter binding is what lets us keep SQL readable while passing values safely from PHP.

Positional placeholders

Use ? when the query only needs a small number of values and the order is obvious.

select 
	id, 
	name, 
	email
	
from 
	user
	
where 
	id = ?
	
limit 1
$userRow = $db->fetch("user/getById", 105);

This style is compact and clear when there is only one or two values to bind.

Named placeholders

Use named placeholders when the meaning of the values matters more than their order.

select 
	id, 
	name, 
	email
	
from 
	user
	
where 
	email = :email
and 
	isActive = :isActive
$userRow = $db->fetch("user/getByEmail", [
	"email" => "dev@example.com",
	"isActive" => true,
]);

This tends to be the better choice once a query grows beyond a couple of values.

Automatic value handling

The library helps with a few common conversions for us:

  • bool becomes a database-friendly truthy value.
  • DateTimeInterface is formatted as Y-m-d H:i:s.
  • arrays expand into indexed placeholders such as :ids__0, :ids__1 (for using within in() conditions, for example).

Special bindings

Some placeholders are designed for safe structural SQL fragments:

  • :groupBy
  • :orderBy
  • :limit
  • :offset
  • :infileName

Example:

select 
	id, 
	email
from 
	user
	
order by :orderBy
limit :limit
offset :offset
$resultSet = $db->fetchAll("user/list", [
	"orderBy" => "id desc",
	"limit" => 20,
	"offset" => 40,
]);

Dynamic bindings

For more dynamic query shapes, these reserved bindings are available:

  • :__dynamicValueSet
  • :__dynamicIn
  • :__dynamicOr

These are useful when we want flexible SQL without dropping into manual string concatenation.

Example: https://github.com/PhpGt/Database/blob/master/example/04-dynamic-bindings.php

Tip

In WebEngine, binding works in exactly the same way because the query execution still comes from this package. The WebEngine layer mainly saves you from connection setup and command wiring.


Next, move on to Raw SQL and result sets to see what comes back from the database layer and when executeSql() is appropriate.

Clone this wiki locally