X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FUtils%2FJS.php;h=66c03e0f483d6175b3a8337723eb98d332cae5ed;hb=25a9911e56ec529c80ebc0c4d47852a42ca0e634;hp=bcd80dd79967e5223b3518c738dc511554206d31;hpb=060ea8f8e944292abc945b136c66cb7ccabd4fc1;p=civicrm-core.git diff --git a/CRM/Utils/JS.php b/CRM/Utils/JS.php index bcd80dd799..66c03e0f48 100644 --- a/CRM/Utils/JS.php +++ b/CRM/Utils/JS.php @@ -54,13 +54,15 @@ class CRM_Utils_JS { * Note that you can only dedupe closures if they are directly adjacent and * have exactly the same parameters. * + * Also dedupes the "use strict" directive as it is only meaningful at the beginning of a closure. + * * @param array $scripts * Javascript source. * @param array $localVars * Ordered list of JS vars to identify the start of a closure. * @param array $inputVals * Ordered list of input values passed into the closure. - * @return string + * @return string[] * Javascript source. */ public static function dedupeClosures($scripts, $localVars, $inputVals) { @@ -70,7 +72,7 @@ class CRM_Utils_JS { return preg_quote($v, '/'); }, $localVars)); $opening .= '\)\s*\{'; - $opening = '/^' . $opening . '/'; + $opening = '/^' . $opening . '\s*(?:"use strict";\s|\'use strict\';\s)?/'; // Example closing: })(angular, CRM.$, CRM._); $closing = '\}\s*\)\s*\(\s*'; @@ -107,7 +109,7 @@ class CRM_Utils_JS { * @return string */ public static function stripComments($script) { - return preg_replace(":^\\s*//[^\n]+$:m", "", $script); + return preg_replace("#^\\s*//[^\n]*$(?:\r\n|\n)?#m", "", $script); } /** @@ -123,25 +125,52 @@ class CRM_Utils_JS { * ] * * @param string $js + * @param bool $throwException * @return mixed + * @throws CRM_Core_Exception */ - public static function decode($js) { + public static function decode($js, $throwException = FALSE) { $js = trim($js); $first = substr($js, 0, 1); $last = substr($js, -1); - if ($last === $first && ($first === "'" || $first === '"')) { - // Use a temp placeholder for escaped backslashes - $backslash = chr(0) . 'backslash' . chr(0); - return str_replace(['\\\\', "\\'", '\\"', '\\&', '\\/', $backslash], [$backslash, "'", '"', '&', '/', '\\'], substr($js, 1, -1)); + if ($first === "'" && $last === "'") { + $js = self::convertSingleQuoteString($js, $throwException); } - if (($first === '{' && $last === '}') || ($first === '[' && $last === ']')) { + elseif (($first === '{' && $last === '}') || ($first === '[' && $last === ']')) { $obj = self::getRawProps($js); foreach ($obj as $idx => $item) { - $obj[$idx] = self::decode($item); + $obj[$idx] = self::decode($item, $throwException); } return $obj; } - return json_decode($js); + $result = json_decode($js); + if ($throwException && $result === NULL && $js !== 'null') { + throw new CRM_Core_Exception(json_last_error_msg()); + } + return $result; + } + + /** + * @param string $str + * @param bool $throwException + * @return string|null + * @throws CRM_Core_Exception + */ + public static function convertSingleQuoteString(string $str, $throwException) { + // json_decode can only handle double quotes around strings, so convert single-quoted strings + $backslash = chr(0) . 'backslash' . chr(0); + $str = str_replace(['\\\\', '\\"', '"', '\\&', '\\/', $backslash], [$backslash, '"', '\\"', '&', '/', '\\'], substr($str, 1, -1)); + // Ensure the string doesn't terminate early by checking that all single quotes are escaped + $pos = -1; + while (($pos = strpos($str, "'", $pos + 1)) !== FALSE) { + if (($pos - strlen(rtrim(substr($str, 0, $pos)))) % 2) { + if ($throwException) { + throw new CRM_Core_Exception('Invalid string passed to CRM_Utils_JS::decode'); + } + return NULL; + } + } + return '"' . $str . '"'; } /** @@ -187,7 +216,7 @@ class CRM_Utils_JS { * * @param $js * @return array - * @throws \Exception + * @throws Exception */ public static function getRawProps($js) { $js = trim($js);