From 2c4bba53e056473398a740a8c2c24e3eb6e52754 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 20 Feb 2018 14:27:31 -0800 Subject: [PATCH] (civicrm-setup/1) CRM_Core_I18n - Loosen coupling to DB layer Overview -------- The `ts()`/`{ts}` feature from `CRM_Core_I18n` has an option to escape output (e.g. `{ts escape="sql"}Activities{/ts}`). However, SQL encoding is subjective (depending on the connection details). For `{ts}` to support this feature, it must have a dependency on the DB subsystem (e.g. it eventually calls `CRM_Core_DAO`). For https://github.com/civicrm/civicrm-setup/issues/1, we have an issue where expressions like `{ts escape="sql"}Activities{/ts}` fail during installation because `CRM_Core_DAO` is not fully available. This change loosens the coupling, so that we can use `{ts escape="sql"}Activities{/ts}` without needing `CRM_Core_DAO` per se. Before ------ `ts`/`CRM_Core_I18n` is tightly coupled to `CRM_Core_DAO`. There is no way to use `{ts escape=sql}Activities{/ts}` without spinning-up `CRM_Core_DAO`. After ----- `ts`/`CRM_Core_I18n` *defaults* to calling `CRM_Core_DAO`. However, this can be overriden by manipulating a property. Comments -------- * I feel a little dirty keeping any coupling between i18n and the DB. However, changing this would mean removing support for the `{ts escape=sql}` option, and that would be a clear compatibility-break. * Arguably, there may be a microsecond-level penalty in using `call_user_func($SQL_ESCAPER)` rather than a specific class/function. However, it's only incurred if you actually call `{ts escape=sql}` while setting `$SQL_ESCAPER`, and that's pretty rare circumstance. The typical runtime use-cases for `ts()` are unaffected. --- CRM/Core/I18n.php | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/CRM/Core/I18n.php b/CRM/Core/I18n.php index dc434f1f04..e9b7e77665 100644 --- a/CRM/Core/I18n.php +++ b/CRM/Core/I18n.php @@ -39,6 +39,34 @@ class CRM_Core_I18n { */ const NONE = 'none', AUTO = 'auto'; + /** + * @var callable|NULL + * A callback function which handles SQL string encoding. + * Set NULL to use the default, CRM_Core_DAO::escapeString(). + * This is used by `ts(..., [escape=>sql])`. + * + * This option is not intended for general consumption. It is only intended + * for certain pre-boot/pre-install contexts. + * + * You might ask, "Why on Earth does string-translation have an opinion on + * SQL escaping?" Good question! + */ + public static $SQL_ESCAPER = NULL; + + /** + * Encode a string for use in SQL. + * + * @param string $text + * @return string + */ + protected static function escapeSql($text) { + if (self::$SQL_ESCAPER == NULL) { + return CRM_Core_DAO::escapeString($text); + } + else { + return call_user_func(self::$SQL_ESCAPER, $text); + } + } /** * A PHP-gettext instance for string translation; @@ -289,7 +317,7 @@ class CRM_Core_I18n { // in such cases we return early, only doing SQL/JS escaping if (isset($params['skip']) and $params['skip']) { if (isset($escape) and ($escape == 'sql')) { - $text = CRM_Core_DAO::escapeString($text); + $text = self::escapeSql($text); } if (isset($escape) and ($escape == 'js')) { $text = addcslashes($text, "'"); @@ -351,7 +379,7 @@ class CRM_Core_I18n { // escape SQL if we were asked for it if (isset($escape) and ($escape == 'sql')) { - $text = CRM_Core_DAO::escapeString($text); + $text = self::escapeSql($text); } // escape for JavaScript (if requested) -- 2.25.1