Changeset View
Changeset View
Standalone View
Standalone View
src/xsprintf/qsprintf.php
| Show First 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
| * Escapes a suffix query for a LIKE clause. For example: | * Escapes a suffix query for a LIKE clause. For example: | ||||
| * | * | ||||
| * // Find all rows where `name` ends with $suffix. | * // Find all rows where `name` ends with $suffix. | ||||
| * qsprintf($escaper, 'WHERE name LIKE %<', $suffix); | * qsprintf($escaper, 'WHERE name LIKE %<', $suffix); | ||||
| */ | */ | ||||
| function qsprintf(PhutilQsprintfInterface $escaper, $pattern /* , ... */) { | function qsprintf(PhutilQsprintfInterface $escaper, $pattern /* , ... */) { | ||||
| $args = func_get_args(); | $args = func_get_args(); | ||||
| array_shift($args); | array_shift($args); | ||||
| return xsprintf('xsprintf_query', $escaper, $args); | return new PhutilQueryString($escaper, $args); | ||||
| } | } | ||||
| function vqsprintf(PhutilQsprintfInterface $escaper, $pattern, array $argv) { | function vqsprintf(PhutilQsprintfInterface $escaper, $pattern, array $argv) { | ||||
| array_unshift($argv, $pattern); | array_unshift($argv, $pattern); | ||||
| return xsprintf('xsprintf_query', $escaper, $argv); | return new PhutilQueryString($escaper, $argv); | ||||
| } | } | ||||
| /** | /** | ||||
| * @{function:xsprintf} callback for encoding SQL queries. See | * @{function:xsprintf} callback for encoding SQL queries. See | ||||
| * @{function:qsprintf}. | * @{function:qsprintf}. | ||||
| */ | */ | ||||
| function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) { | function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) { | ||||
| $type = $pattern[$pos]; | $type = $pattern[$pos]; | ||||
| if (is_array($userdata)) { | |||||
| $escaper = $userdata['escaper']; | |||||
| $unmasked = $userdata['unmasked']; | |||||
| } else { | |||||
| $escaper = $userdata; | $escaper = $userdata; | ||||
| $next = (strlen($pattern) > $pos + 1) ? $pattern[$pos + 1] : null; | $unmasked = false; | ||||
| } | |||||
| $next = (strlen($pattern) > $pos + 1) ? $pattern[$pos + 1] : null; | |||||
| $nullable = false; | $nullable = false; | ||||
| $done = false; | $done = false; | ||||
| $prefix = ''; | $prefix = ''; | ||||
| if (!($escaper instanceof PhutilQsprintfInterface)) { | if (!($escaper instanceof PhutilQsprintfInterface)) { | ||||
| throw new InvalidArgumentException(pht('Invalid database escaper.')); | throw new InvalidArgumentException(pht('Invalid database escaper.')); | ||||
| } | } | ||||
| switch ($type) { | switch ($type) { | ||||
| ▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | switch ($type) { | ||||
| $value = 'NULL'; | $value = 'NULL'; | ||||
| } else { | } else { | ||||
| $value = "'".$escaper->escapeBinaryString((string)$value)."'"; | $value = "'".$escaper->escapeBinaryString((string)$value)."'"; | ||||
| } | } | ||||
| $type = 's'; | $type = 's'; | ||||
| break; | break; | ||||
| case 'Q': // Query Fragment | case 'Q': // Query Fragment | ||||
| if ($value instanceof PhutilQueryString) { | |||||
amckinley: Worth adding a "TODO" here to clean up later? Or is the conversion for this going to be… | |||||
| $value = $value->getUnmaskedString(); | |||||
| } | |||||
| $type = 's'; | $type = 's'; | ||||
| break; | break; | ||||
| case '~': // Like Substring | case '~': // Like Substring | ||||
| case '>': // Like Prefix | case '>': // Like Prefix | ||||
| case '<': // Like Suffix | case '<': // Like Suffix | ||||
| $value = $escaper->escapeStringForLikeClause($value); | $value = $escaper->escapeStringForLikeClause($value); | ||||
| switch ($type) { | switch ($type) { | ||||
| ▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | default: | ||||
| qsprintf_check_scalar_type($value, $type, $query); | qsprintf_check_scalar_type($value, $type, $query); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| function qsprintf_check_scalar_type($value, $type, $query) { | function qsprintf_check_scalar_type($value, $type, $query) { | ||||
| switch ($type) { | switch ($type) { | ||||
| case 'Q': | case 'Q': | ||||
| // TODO: See T13217. Remove this eventually. | |||||
Lint: TODO Comment This comment has a TODO. Lint: TODO Comment: This comment has a TODO. | |||||
| if (is_string($value)) { | |||||
| phlog( | |||||
| pht( | |||||
| 'UNSAFE: Raw string ("%s") passed to query ("%s") for "%%Q" '. | |||||
| 'conversion. %%Q should be passed a query string.', | |||||
| $value, | |||||
| $query)); | |||||
| break; | |||||
| } | |||||
| if (!($value instanceof PhutilQueryString)) { | |||||
| throw new AphrontParameterQueryException( | |||||
| $query, | |||||
| pht('Expected a PhutilQueryString for %%%s conversion.', $type)); | |||||
| } | |||||
| break; | |||||
| case 'LC': | case 'LC': | ||||
| case 'T': | case 'T': | ||||
| case 'C': | case 'C': | ||||
| if (!is_string($value)) { | if (!is_string($value)) { | ||||
| throw new AphrontParameterQueryException( | throw new AphrontParameterQueryException( | ||||
| $query, | $query, | ||||
| pht('Expected a string for %%%s conversion.', $type)); | pht('Expected a string for %%%s conversion.', $type)); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 42 Lines • Show Last 20 Lines | |||||
Worth adding a "TODO" here to clean up later? Or is the conversion for this going to be effectively endless?