/** * Returns the Metaphone key "heard" from a string. * * The algorithm used to render the [Metaphone](http://en.wikipedia.org/wiki/Metaphone) key is the first-generation * one. * * @param string $string The source string. * @param bool $transliterate **OPTIONAL. Default is** `true`. Tells whether to transliterate the string into the * Latin script and then flatten it to ASCII before generating the key. Since the Metaphone algorithm is not * Unicode-aware, the touch of transliteration is something that any arbitrary Unicode string would wish for. For * example, "こんにちは" is transliterated to "kon'nichiha". * * @return string The Metaphone key of the string. */ public static function metaphoneKey($string, $transliterate = true) { assert('is_cstring($string) && is_bool($transliterate)', vs(isset($this), get_defined_vars())); $string = CEString::flattenUnicodeToAscii($string, $transliterate); return CString::metaphoneKey($string); }
/** * Decodes the JSON-encoded string provided earlier to the decoder and returns the result. * * @param reference $success **OPTIONAL. OUTPUT.** After the method is called with this parameter provided, the * parameter's value tells whether the decoding was successful. * * @return mixed The decoded value of type `CMapObject` or `CArrayObject`. */ public function decode(&$success = null) { assert('is_cstring($this->m_source)', vs(isset($this), get_defined_vars())); $success = true; $source = $this->m_source; if ($this->m_decodingStrictness == self::LENIENT && !CUString::isValid($source)) { // Change the character encoding or try fixing it. if (CEString::looksLikeLatin1($source)) { $source = CEString::convertLatin1ToUtf8($source); } else { $source = CEString::fixUtf8($source); } } if ($this->m_decodingStrictness == self::STRICT_WITH_COMMENTS || $this->m_decodingStrictness == self::LENIENT) { if (CRegex::find($source, "/\\/\\/|\\/\\*/u")) { // Remove "//..." and "/*...*/" comments. $source = CRegex::remove($source, "/(?<!\\\\)\"(?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\"(*SKIP)(*FAIL)|" . "\\/\\/.*|\\/\\*\\C*?\\*\\//u"); } } if ($this->m_decodingStrictness == self::LENIENT) { if (CRegex::find($source, "/[:\\[,]\\s*'([^\\\\']++|\\\\{2}|\\\\\\C)*'(?=\\s*[,}\\]])/u")) { // Convert single-quoted string values into double-quoted, taking care of double quotes within such // strings before and single quotes after. This needs to go in front of the rest of the leniency fixes. while (true) { $prevSource = $source; $source = CRegex::replace($source, "/(?<!\\\\)\"(?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\"(*SKIP)(*FAIL)|" . "([:\\[,]\\s*'(?:[^\\\\'\"]++|\\\\{2}|\\\\\\C)*)\"((?:[^\\\\']++|\\\\{2}|\\\\\\C)*')/u", "\$1\\\"\$2"); if (CString::equals($source, $prevSource) || is_null($source)) { break; } } if (is_null($source)) { $source = ""; } $source = CRegex::replace($source, "/(?<!\\\\)\"(?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\"(*SKIP)(*FAIL)|" . "([:\\[,]\\s*)'((?:[^\\\\']++|\\\\{2}|\\\\\\C)*)'(?=\\s*[,}\\]])/u", "\$1\"\$2\""); while (true) { $prevSource = $source; $source = CRegex::replace($source, "/([:\\[,]\\s*\"(?:[^\\\\\"]++|\\\\{2}|\\\\[^'])*)\\\\'((?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\")" . "(?=\\s*[,}\\]])/u", "\$1'\$2"); if (CString::equals($source, $prevSource) || is_null($source)) { break; } } if (is_null($source)) { $source = ""; } } if (CRegex::find($source, "/[{,]\\s*[\\w\\-.]+\\s*:/u")) { // Put property names in double quotes. $source = CRegex::replace($source, "/(?<!\\\\)\"(?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\"(*SKIP)(*FAIL)|" . "([{,]\\s*)([\\w\\-.]+)(\\s*:)/u", "\$1\"\$2\"\$3"); } if (CRegex::find($source, "/[{,]\\s*'[\\w\\-.]+'\\s*:/u")) { // Put property names that are in single quotes in double quotes. $source = CRegex::replace($source, "/(?<!\\\\)\"(?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\"(*SKIP)(*FAIL)|" . "([{,]\\s*)'([\\w\\-.]+)'(\\s*:)/u", "\$1\"\$2\"\$3"); } if (CRegex::find($source, "/,\\s*[}\\]]/u")) { // Remove trailing commas. $source = CRegex::remove($source, "/(?<!\\\\)\"(?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\"(*SKIP)(*FAIL)|" . ",(?=\\s*[}\\]])/u"); } // Within string values, convert byte values for BS, FF, LF, CR, and HT, which are prohibited in JSON, // to their escaped equivalents. $stringValueSubjectRe = "/(?<!\\\\)\"(?:[^\\\\\"]++|\\\\{2}|\\\\\\C)*\"/u"; $source = CRegex::replaceWithCallback($source, $stringValueSubjectRe, function ($matches) { return CRegex::replace($matches[0], "/\\x{0008}/u", "\\b"); }); $source = CRegex::replaceWithCallback($source, $stringValueSubjectRe, function ($matches) { return CRegex::replace($matches[0], "/\\x{000C}/u", "\\f"); }); $source = CRegex::replaceWithCallback($source, $stringValueSubjectRe, function ($matches) { return CRegex::replace($matches[0], "/\\x{000A}/u", "\\n"); }); $source = CRegex::replaceWithCallback($source, $stringValueSubjectRe, function ($matches) { return CRegex::replace($matches[0], "/\\x{000D}/u", "\\r"); }); $source = CRegex::replaceWithCallback($source, $stringValueSubjectRe, function ($matches) { return CRegex::replace($matches[0], "/\\x{0009}/u", "\\t"); }); } $decodedValue = @json_decode($source, false, self::$ms_maxRecursionDepth); if (is_null($decodedValue)) { if ($this->m_decodingStrictness == self::STRICT || $this->m_decodingStrictness == self::STRICT_WITH_COMMENTS) { $success = false; } else { if (CRegex::find($source, "/^\\s*[\\w.]+\\s*\\(/u")) { // The source string appears to be a JSONP. Extract the function's argument and try decoding again. $source = CRegex::replace($source, "/^\\s*[\\w.]+\\s*\\((\\C+)\\)/u", "\$1"); $decodedValue = @json_decode($source, false, self::$ms_maxRecursionDepth); if (is_null($decodedValue)) { $success = false; } } } } if (!$success) { return; } if ($this->m_decodingStrictness == self::STRICT || $this->m_decodingStrictness == self::STRICT_WITH_COMMENTS) { if (!is_object($decodedValue) && !is_array($decodedValue)) { $success = false; return; } } // Recursively convert any object into a CMapObject/CMap and any PHP array into a CArrayObject/CArray. $decodedValue = self::recurseValueAfterDecoding($decodedValue, 0); return $decodedValue; }