Skip to content
developed by

Raw SQL parameters placeholders

When writing raw SQL, you can use the ? parameter placeholder in to arbitrarily:

  • send user input, values, that will be later parametrized when sent to the database server using the bridge, thus preventing SQL-injection,
  • escape properly tables, columns and function names or any other identifier, thus preventing SQL-injection or keyword conflicting identifiers ambiguities,
  • inject arbitrary expressions instances the writer can format for you.

Value placeholder

Independently from the final database driver, all value parameters within arbitrary SQL must be written using the ? placeholder in raw SQL:

php
use MakinaCorpus\QueryBuilder\Writer\Writer;

assert($writer instanceof Writer);

$writer->prepare(
    <<<SQL
    SELECT * FROM user WHERE mail = ?
    SQL,
    [
        'john.smith@example.com'
    ]
)

Additionnaly in order to type hint values for a later bridge value to SQL conversion to work gracefully, you can use the following syntax: ?::TYPE.

php
use MakinaCorpus\QueryBuilder\Writer\Writer;

assert($writer instanceof Writer);

$writer->prepare(
    <<<SQL
    SELECT * FROM user WHERE last_login > ?::timestamp
    SQL,
    [
        new DateTime("today 00:00:01")
    ]
)

See the value converter documentation for supported data types.

You can specify any number of parameter placeholders within the query, parameters array must be ordered:

php
use MakinaCorpus\QueryBuilder\Writer\Writer;

assert($writer instanceof Writer);

$writer->prepare(
    <<<SQL
    SELECT * FROM user WHERE last_login > ?::timestamp AND mail = ?
    SQL,
    [
        new \DateTime("today 00:00:01"),
        'john.smith@example.com'
    ]
);

WARNING

When using the ?::TYPE syntax, avoid accidentally using one of the identifier escapers.

WARNING

Using the ::TYPE syntax unpreceeded with the ? character will be left as-is within the query. This type cast syntax is PostgreSQL specific and you can use it in your raw SQL.

Identifier escaper

Placeholders can be used to set user-given values to prevent injection or complex expressions inside a raw SQL string, but they also can help you set dynamic identifiers in your queries.

For example, you might want to write such query:

php
use MakinaCorpus\QueryBuilder\Expression\ColumnName;
use MakinaCorpus\QueryBuilder\Expression\Identifier;
use MakinaCorpus\QueryBuilder\Expression\TableName;
use MakinaCorpus\QueryBuilder\Writer\Writer;

assert($writer instanceof Writer);

$writer->prepare(
    <<<SQL
    SELECT ? AS ? FROM ?
    SQL,
    [
        new ColumnName('foo', 'bar'),
        new Identifier('some.escaped.alias'),
        new TableName('bar', 'table_schema'),
    ]
);

A shortcut exists for typing expressions and enforce their conversion when query is being built, the ?::TYPE placeholder syntax.

Using the ?::TYPE PostgreSQL-like cast will trigger the corresponding value conversion from the $arguments array to be done while query is being written by the writer instance.

Use the ?::ESCAPER where ESCAPER can be any one of the following table:

EscaperType of escapingOutputsRemoves placeholder
arrayHint the value as being an array\QueryBuilder\Expression\ArrayValueNo
columnEscape string a column name\QueryBuilder\Expression\ColumnNameYes
identifierEscape string an abitrary identifier\QueryBuilder\Expression\IdentifierYes
rowHint the value as being a constant row\QueryBuilder\Expression\RowNo
tableEscape string a table name\QueryBuilder\Expression\TableNameYes
valueHint the value as being a vlaue\QueryBuilder\Expression\ValueNo

For example:

php
use MakinaCorpus\QueryBuilder\Expression\ColumnName;
use MakinaCorpus\QueryBuilder\Expression\TableName;
use MakinaCorpus\QueryBuilder\Writer\Writer;

assert($writer instanceof Writer);

$writer->prepare(
    <<<SQL
    SELECT ?::column AS ?::identifier FROM ?::table
    SQL,
    [
        'bar.foo',
        'some.escaped.alias',
        'table_schema.bar',
    ]
);

will generate the following SQL code:

sql
    select "bar"."foo" as "some.escaped.alias" from "table_schema"."bar"

WARNING

Typo errors in escaper names will not raise errors, but will be silently passed as a value type hint instead.

WARNING

Using the ::TYPE syntax unpreceeded with the ? character will be left as-is within the query. This type cast syntax is PostgreSQL specific and you can use it in your raw SQL.

Expressions instances

The placeholder is much more than a value placeholder, it can also be used to place complex SQL expressions in a raw SQL string, for example:

php
use MakinaCorpus\QueryBuilder\QueryBuilder;
use MakinaCorpus\QueryBuilder\Writer\Writer;

assert($queryBuilder instanceof QueryBuilder);
assert($writer instanceof Writer);

$nestedSelect = $queryBuilder
    ->select('some_table')
    ->column('id')
;

$writer->prepare(
    <<<SQL
    select *
    from "other_table"
    where
        "some_id" not in ?
        and "some_value" is not ?
    SQL,
    [
        $nestedSelect,
        true,
    ]
)

This will result in the following SQL code:

sql
select *
from "other_table"
where
    "some_id" not in (
        select id
        from "some_table"
    )
    and "some_value" is not ?

TIP

Please note that the first placeholder is removed from the generated SQL code and replaced with the generated SQL code corresponding to the given select expression.

The resulting argument bag will have the correct number of parameters and values matching the resulting SQL code.

Escape placeholder

In order to escape the ? character, double it, hence the following SQL query:

sql
SELECT bar ?? foo WHERE baz = ?;

will be sent to the driver rewritten as such:

sql
SELECT bar ? foo WHERE baz = 'your value';