Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F116852811
qsprintf.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Jun 9, 16:19
Size
9 KB
Mime Type
text/x-php
Expires
Wed, Jun 11, 16:19 (2 d)
Engine
blob
Format
Raw Data
Handle
26682130
Attached To
rPHU libphutil
qsprintf.php
View Options
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Format a MySQL query. This function behaves like sprintf(), except that
* all the normal conversions (like %s) will be properly escaped, and
* additional conversions are supported:
*
* %nd, %ns, %nf
* "Nullable" versions of %d, %s and %f. Will produce 'NULL' if the
* argument is a strict null.
*
* %=d, %=s, %=f
* "Nullable Test" versions of %d, %s and %f. If you pass a value, you
* get "= 3"; if you pass null, you get "IS NULL". For instance, this
* will work properly if `hatID' is a nullable column and $hat is null.
*
* qsprintf($conn, 'WHERE hatID %=d', $hat);
*
* %Ld, %Ls, %Lf
* "List" versions of %d, %s and %f. These are appropriate for use in
* an "IN" clause. For example:
*
* qsprintf($conn, 'WHERE hatID IN(%Ld)', $list_of_hats);
*
* %T ("Table")
* Escapes a table name.
*
* %C, %LC
* Escapes a column name or a list of column names.
*
* %K ("Comment")
* Escapes a comment.
*
* %Q ("Query Fragment")
* Injects a raw query fragment. Extremely dangerous! Not escaped!
*
* %~ ("Substring")
* Escapes a substring query for a LIKE (or NOT LIKE) clause. For example:
*
* // Find all rows with $search as a substing of `name`.
* qsprintf($conn, 'WHERE name LIKE %~', $search);
*
* See also %> and %<.
*
* %> ("Prefix")
* Escapes a prefix query for a LIKE clause. For example:
*
* // Find all rows where `name` starts with $prefix.
* qsprintf($conn, 'WHERE name LIKE %>', $prefix);
*
* %< ("Suffix")
* Escapes a suffix query for a LIKE clause. For example:
*
* // Find all rows where `name` ends with $suffix.
* qsprintf($conn, 'WHERE name LIKE %<', $suffix);
*/
function
qsprintf
(
$conn
,
$pattern
/*, ... */
)
{
$args
=
func_get_args
();
array_shift
(
$args
);
return
xsprintf
(
'xsprintf_query'
,
$conn
,
$args
);
}
function
vqsprintf
(
$conn
,
$pattern
,
array
$argv
)
{
array_unshift
(
$argv
,
$pattern
);
return
xsprintf
(
'xsprintf_query'
,
$conn
,
$argv
);
}
/**
* xsprintf() callback for encoding SQL queries. See qsprintf().
*/
function
xsprintf_query
(
$userdata
,
&
$pattern
,
&
$pos
,
&
$value
,
&
$length
)
{
$type
=
$pattern
[
$pos
];
$conn
=
$userdata
;
$next
=
(
strlen
(
$pattern
)
>
$pos
+
1
)
?
$pattern
[
$pos
+
1
]
:
null
;
$nullable
=
false
;
$done
=
false
;
$prefix
=
''
;
switch
(
$type
)
{
case
'='
:
// Nullable test
switch
(
$next
)
{
case
'd'
:
case
'f'
:
case
's'
:
$pattern
=
substr_replace
(
$pattern
,
''
,
$pos
,
1
);
$length
=
strlen
(
$pattern
);
$type
=
's'
;
if
(
$value
===
null
)
{
$value
=
'IS NULL'
;
$done
=
true
;
}
else
{
$prefix
=
'= '
;
$type
=
$next
;
}
break
;
default
:
throw
new
Exception
(
'Unknown conversion, try %=d, %=s, or %=f.'
);
}
break
;
case
'n'
:
// Nullable...
switch
(
$next
)
{
case
'd'
:
// ...integer.
case
'f'
:
// ...float.
case
's'
:
// ...string.
$pattern
=
substr_replace
(
$pattern
,
''
,
$pos
,
1
);
$length
=
strlen
(
$pattern
);
$type
=
$next
;
$nullable
=
true
;
break
;
default
:
throw
new
Exception
(
'Unknown conversion, try %nd or %ns.'
);
}
break
;
case
'L'
:
// List of..
_qsprintf_check_type
(
$value
,
"L{$next}"
,
$pattern
);
$pattern
=
substr_replace
(
$pattern
,
''
,
$pos
,
1
);
$length
=
strlen
(
$pattern
);
$type
=
's'
;
$done
=
true
;
switch
(
$next
)
{
case
'd'
:
// ...integers.
$value
=
implode
(
', '
,
array_map
(
'intval'
,
$value
));
break
;
case
's'
:
// ...strings.
$value
=
mysql_escape_array_of_strings_for_in_clause
(
$value
,
$conn
);
break
;
case
'C'
:
// ...columns.
$value
=
implode
(
', '
,
array_map
(
'mysql_escape_column_name'
,
$value
));
break
;
default
:
throw
new
Exception
(
"Unknown conversion %L{$next}."
);
}
break
;
}
if
(!
$done
)
{
_qsprintf_check_type
(
$value
,
$type
,
$pattern
);
switch
(
$type
)
{
case
's'
:
// String
if
(
$nullable
&&
$value
===
null
)
{
$value
=
'NULL'
;
}
else
{
$value
=
"'"
.
mysql_real_escape_string
(
$value
,
$conn
).
"'"
;
}
$type
=
's'
;
break
;
case
'Q'
:
// Query Fragment
$type
=
's'
;
break
;
case
'~'
:
// Like Substring
case
'>'
:
// Like Prefix
case
'<'
:
// Like Suffix
$value
=
mysql_real_escape_string
(
$value
,
$conn
);
// Ideally the query shouldn't be modified after safely escaping it,
// but we need to escape _ and % within LIKE terms.
$value
=
str_replace
(
// Even though we've already escaped, we need to replace \ with \\
// because MYSQL unescapes twice inside a LIKE clause. See note
// at mysql.com. However, if the \ is being used to escape a single
// quote ('), then the \ should not be escaped. Thus, after all \
// are replaced with \\, we need to revert instances of \\' back to
// \'.
array
(
'
\\
'
,
'
\\\\\'
'
,
'_'
,
'%'
),
array
(
'
\\\\
'
,
'
\\\'
'
,
'
\_
'
,
'
\%
'
),
$value
);
switch
(
$type
)
{
case
'~'
:
$value
=
"'%"
.
$value
.
"%'"
;
break
;
case
'>'
:
$value
=
"'"
.
$value
.
"%'"
;
break
;
case
'<'
:
$value
=
"'%"
.
$value
.
"'"
;
break
;
}
$type
=
's'
;
break
;
case
'f'
:
// Float
if
(
$nullable
&&
$value
===
null
)
{
$value
=
'NULL'
;
}
else
{
$value
=
(
float
)
$value
;
}
$type
=
's'
;
break
;
case
'd'
:
// Integer
if
(
$nullable
&&
$value
===
null
)
{
$value
=
'NULL'
;
}
else
{
$value
=
(
int
)
$value
;
}
$type
=
's'
;
break
;
case
'T'
:
// Table
case
'C'
:
// Column
$value
=
mysql_escape_column_name
(
$value
);
$type
=
's'
;
break
;
case
'K'
:
// Komment
$value
=
mysql_escape_multiline_comment
(
$value
);
$type
=
's'
;
break
;
default
:
throw
new
Exception
(
"Unknown conversion '%{$type}'."
);
}
}
if
(
$prefix
)
{
$value
=
$prefix
.
$value
;
}
$pattern
[
$pos
]
=
$type
;
}
function
_qsprintf_check_type
(
$value
,
$type
,
$query
)
{
switch
(
$type
)
{
case
'Ld'
:
case
'Ls'
:
case
'LC'
:
case
'LA'
:
case
'LO'
:
if
(!
is_array
(
$value
))
{
throw
new
QsprintfQueryParameterException
(
$query
,
"Expected array argument for %{$type} conversion."
);
}
if
(
empty
(
$value
))
{
throw
new
QsprintfQueryParameterException
(
$query
,
"Array for %{$type} conversion is empty."
);
}
foreach
(
$value
as
$scalar
)
{
_qsprintf_check_scalar_type
(
$scalar
,
$type
,
$query
);
}
break
;
default
:
_qsprintf_check_scalar_type
(
$value
,
$type
,
$query
);
}
}
function
_qsprintf_check_scalar_type
(
$value
,
$type
,
$query
)
{
switch
(
$type
)
{
case
'Q'
:
case
'LC'
:
case
'T'
:
case
'C'
:
if
(!
is_string
(
$value
))
{
throw
new
QsprintfQueryParameterException
(
$query
,
"Expected a string for %{$type} conversion."
);
}
break
;
case
'Ld'
:
case
'd'
:
case
'f'
:
if
(!
is_null
(
$value
)
&&
!
is_scalar
(
$value
))
{
throw
new
QsprintfQueryParameterException
(
$query
,
"Expected a scalar or null for %{$type} conversion."
);
}
break
;
case
'Ls'
:
case
's'
:
case
'~'
:
case
'>'
:
case
'<'
:
case
'K'
:
if
(!
is_null
(
$value
)
&&
!
is_scalar
(
$value
))
{
throw
new
QsprintfQueryParameterException
(
$query
,
"Expected a scalar or null for %{$type} conversion."
);
}
break
;
case
'LA'
:
case
'LO'
:
if
(!
is_null
(
$value
)
&&
!
is_scalar
(
$value
)
&&
!(
is_array
(
$value
)
&&
!
empty
(
$value
)))
{
throw
new
QsprintfQueryParameterException
(
$query
,
"Expected a scalar or null or non-empty array for "
.
"%{$type} conversion."
);
}
break
;
default
:
throw
new
Exception
(
"Unknown conversion '{$type}'."
);
}
}
function
mysql_escape_column_name
(
$value
)
{
return
'`'
.
str_replace
(
'`'
,
'
\\
`'
,
$value
).
'`'
;
}
function
mysql_escape_multiline_comment
(
$comment
)
{
// These can either terminate a comment, confuse the hell out of the parser,
// make MySQL execute the comment as a query, or, in the case of semicolon,
// are quasi-dangerous because the semicolon could turn a broken query into
// a working query plus an ignored query.
static
$bad
=
array
(
'--'
,
'*/'
,
'//'
,
'#'
,
'!'
,
';'
);
static
$safe
=
array
(
'(DOUBLEDASH)'
,
'(STARSLASH)'
,
'(SLASHSLASH)'
,
'(HASH)'
,
'(BANG)'
,
'(SEMICOLON)'
);
$comment
=
str_replace
(
$bad
,
$safe
,
$comment
);
// For good measure, kill anything else that isn't a nice printable
// character.
$comment
=
preg_replace
(
'/[^
\x
20-
\x
7F]+/'
,
' '
,
$comment
);
return
'/* '
.
$comment
.
' */'
;
}
function
mysql_escape_array_of_strings_for_in_clause
(
$strings
,
$conn
)
{
if
(!
count
(
$strings
))
{
return
'NULL'
;
}
foreach
(
$strings
as
&
$string
)
{
if
(
$string
===
null
)
{
$string
=
'NULL'
;
}
else
{
$string
=
"'"
.
mysql_real_escape_string
(
$string
,
$conn
).
"'"
;
}
}
return
implode
(
', '
,
$strings
);
}
Event Timeline
Log In to Comment