Example #1
0
 public function testSplit()
 {
     // ASCII.
     $res = CRegex::split("He,llo;th,ere!", "/[,;]/");
     $this->assertTrue(CArray::length($res) == 4 && CString::equals($res[0], "He") && CString::equals($res[1], "llo") && CString::equals($res[2], "th") && CString::equals($res[3], "ere!"));
     $res = CRegex::split("He,llo;th.ere!", CArray::fromElements("/[,;]/", "/\\./"));
     $this->assertTrue(CArray::length($res) == 4 && CString::equals($res[0], "He") && CString::equals($res[1], "llo") && CString::equals($res[2], "th") && CString::equals($res[3], "ere!"));
     // Special cases.
     $res = CRegex::split("", "/[,;]/");
     $this->assertTrue(CArray::length($res) == 1 && CString::equals($res[0], ""));
     $res = CRegex::split("Hey", "//");
     $this->assertTrue(CArray::length($res) == 3 && CString::equals($res[0], "H") && CString::equals($res[1], "e") && CString::equals($res[2], "y"));
     $res = CRegex::split("", "//");
     $this->assertTrue(CArray::length($res) == 1 && CString::equals($res[0], ""));
     // Unicode.
     $res = CRegex::split("¡He,llo·se,ñor!", "/[,·]/u");
     $this->assertTrue(CArray::length($res) == 4 && CUString::equals($res[0], "¡He") && CUString::equals($res[1], "llo") && CUString::equals($res[2], "se") && CUString::equals($res[3], "ñor!"));
     $res = CRegex::split("¡He,llo·se.ñor!", CArray::fromElements("/[,·]/u", "/\\./u"));
     $this->assertTrue(CArray::length($res) == 4 && CUString::equals($res[0], "¡He") && CUString::equals($res[1], "llo") && CUString::equals($res[2], "se") && CUString::equals($res[3], "ñor!"));
     // Special cases.
     $res = CRegex::split("", "/[,·]/u");
     $this->assertTrue(CArray::length($res) == 1 && CUString::equals($res[0], ""));
     $res = CRegex::split("Héy", "//u");
     $this->assertTrue(CArray::length($res) == 3 && CUString::equals($res[0], "H") && CUString::equals($res[1], "é") && CUString::equals($res[2], "y"));
     $res = CRegex::split("", "//u");
     $this->assertTrue(CArray::length($res) == 1 && CUString::equals($res[0], ""));
 }
Example #2
0
 public function testSortOn()
 {
     $array = CArray::fromElements(new ClassForSorting("d"), new ClassForSorting("e"), new ClassForSorting("a"), new ClassForSorting("c"), new ClassForSorting("b"));
     CArray::sortOn($array, "value", CComparator::ORDER_ASC);
     $this->assertTrue($array[0]->value() === "a" && $array[1]->value() === "b" && $array[2]->value() === "c" && $array[3]->value() === "d" && $array[4]->value() === "e");
     $array = CArray::fromElements(new ClassForSorting(5), new ClassForSorting(2), new ClassForSorting(1), new ClassForSorting(3), new ClassForSorting(4));
     CArray::sortOn($array, "value", CComparator::ORDER_ASC);
     $this->assertTrue($array[0]->value() == 1 && $array[1]->value() == 2 && $array[2]->value() == 3 && $array[3]->value() == 4 && $array[4]->value() == 5);
     $array = CArray::fromElements(new ClassForSorting(u("d")), new ClassForSorting(u("e")), new ClassForSorting(u("a")), new ClassForSorting(u("c")), new ClassForSorting(u("b")));
     CArray::sortOn($array, "value", CComparator::ORDER_ASC);
     $this->assertTrue(CUString::equals($array[0]->value(), "a") && CUString::equals($array[1]->value(), "b") && CUString::equals($array[2]->value(), "c") && CUString::equals($array[3]->value(), "d") && CUString::equals($array[4]->value(), "e"));
 }
Example #3
0
 /**
  * Sorts the elements in an array of Unicode or ASCII strings, in the ascending order, case-insensitively, using
  * natural order comparison.
  *
  * To illustrate natural order with an example, an array with strings "a100", "a20", "a3" would be sorted into the
  * same array with `sortUStringsCi` method, but as "a3", "a20", "a100" with this method, which is the order a human
  * being would choose.
  *
  * @param  array $array The array to be sorted.
  * @param  bitfield $collationFlags **OPTIONAL. Default is** `CUString::COLLATION_DEFAULT`. The Unicode collation
  * option(s) to be used for string comparison. See the [CUString](CUString.html) class for information on collation
  * options.
  * @param  CULocale $inLocale **OPTIONAL. Default is** *the application's default locale*. The locale in which
  * strings are to be compared with each other.
  *
  * @return void
  *
  * @link   CUString.html CUString
  */
 public static function sortUStringsNatCi($array, $collationFlags = CUString::COLLATION_DEFAULT, CULocale $inLocale = null)
 {
     assert('is_carray($array)', vs(isset($this), get_defined_vars()));
     $array = splarray($array);
     $locale = isset($inLocale) ? $inLocale->name() : CULocale::defaultLocaleName();
     $coll = CUString::collatorObject(true, true, $locale, $collationFlags);
     $pArray = self::toPArray($array);
     $res = $coll->sort($pArray);
     assert('$res', vs(isset($this), get_defined_vars()));
     self::assignArrayToMap($array, $pArray);
 }
Example #4
0
 protected function maybeWrapText($text)
 {
     if (!isset($this->m_bodyWordWrappingIsDisabled) || !$this->m_bodyWordWrappingIsDisabled) {
         // Wrap the text.
         $width;
         if (!isset($this->m_bodyWordWrappingWidth)) {
             $width = self::$ms_defaultWordWrappingWidth;
         } else {
             $width = $this->m_bodyWordWrappingWidth;
         }
         $text = CUString::wordWrap($text, $width);
     }
     return $text;
 }
Example #5
0
 /**
  * Splits a string into substrings using a specified pattern or patterns as the delimiter(s) and returns the
  * resulting strings as an array.
  *
  * If no delimiter patterns were found, the resulting array contains just one element, which is the original
  * string. If a delimiter is located at the very start or at the very end of the string or next to another
  * delimiter, it will accordingly cause some string(s) in the resulting array to be empty.
  *
  * As a special case, the delimiter pattern can be empty, which will split the string into its constituting
  * characters.
  *
  * @param  string $string The string to be split.
  * @param  string|array|map $delimiterPatternOrPatterns The pattern or array of patterns to be recognized as the
  * delimiter(s).
  *
  * @return CArray The resulting strings.
  */
 public static function split($string, $delimiterPatternOrPatterns)
 {
     assert('is_cstring($string) && (is_cstring($delimiterPatternOrPatterns) || ' . 'is_collection($delimiterPatternOrPatterns))', vs(isset($this), get_defined_vars()));
     if (is_cstring($delimiterPatternOrPatterns)) {
         $numIdt = self::findGroups($delimiterPatternOrPatterns, "/^([^0-9A-Za-z\\s\\\\])(.*)\\1/", $foundGroups);
         assert('$numIdt == 2', vs(isset($this), get_defined_vars()));
         $idt = $foundGroups[1];
         if (CString::isEmpty($idt)) {
             // Special case.
             if (CString::isEmpty($string)) {
                 $resStrings = CArray::fromElements("");
                 return $resStrings;
             } else {
                 if (preg_match("/^([^0-9A-Za-z\\s\\\\])\\1[A-Za-z]*u[A-Za-z]*\\z/", $delimiterPatternOrPatterns) !== 1) {
                     $resStrings = CArray::make(strlen($string));
                     for ($i = 0; $i < strlen($string); $i++) {
                         $resStrings[$i] = $string[$i];
                     }
                     return $resStrings;
                 } else {
                     return CUString::splitIntoChars($string);
                 }
             }
         }
         $paResStrings = preg_split($delimiterPatternOrPatterns, $string);
         $qty = count($paResStrings);
         $resStrings = CArray::make($qty);
         for ($i = 0; $i < $qty; $i++) {
             $resStrings[$i] = $paResStrings[$i];
         }
         return $resStrings;
     } else {
         $resStrings = CArray::fromElements($string);
         foreach ($delimiterPatternOrPatterns as $delimiterPattern) {
             assert('is_cstring($delimiterPattern)', vs(isset($this), get_defined_vars()));
             $resStringsNew = CArray::make();
             $len = CArray::length($resStrings);
             for ($i = 0; $i < $len; $i++) {
                 CArray::pushArray($resStringsNew, self::split($resStrings[$i], $delimiterPattern));
             }
             $resStrings = $resStringsNew;
         }
         return $resStrings;
     }
 }
Example #6
0
 /**
  * Determines if a string contains any characters from the Chinese, Japanese, or Korean scripts.
  *
  * @return bool `true` if the string contains at least one CJK character, `false` otherwise.
  */
 public function hasCjkChar()
 {
     return CUString::hasCjkChar($this);
 }
Example #7
0
 /**
  * Filters a string or a collection of strings according to the expected output type(s) and returns the output
  * value(s).
  *
  * @param  mixed $inputStringOrDecodedCollection The string to be filtered or the array or map containing the
  * strings to be filtered. If the parameter's value is a JSON-encoded string, the output value is going to be
  * either an array or map.
  * @param  reference $success **OUTPUT.** After the method is called, the value of this parameter tells whether
  * the filtering was successful.
  *
  * @return mixed The output value or a collection of values of the expected type(s) after having been put through
  * the filter.
  */
 public function filter($inputStringOrDecodedCollection, &$success)
 {
     assert('is_cstring($inputStringOrDecodedCollection) || is_collection($inputStringOrDecodedCollection)', vs(isset($this), get_defined_vars()));
     $success = true;
     if ($this->m_expectedType != self::CARRAY && $this->m_expectedType != self::CMAP) {
         // The expected output type is not a collection; the input value must be of string type.
         if (!is_cstring($inputStringOrDecodedCollection)) {
             $success = false;
             return oop_x($this->m_defaultValue);
         }
         $inputString = $inputStringOrDecodedCollection;
         if ($this->m_expectedType == self::BOOL || $this->m_expectedType == self::INT || $this->m_expectedType == self::FLOAT || $this->m_expectedType == self::EMAIL || $this->m_expectedType == self::URL || $this->m_expectedType == self::IP) {
             // Trim the input string on both sides from whitespace, including Unicode whitespace and control
             // characters.
             $trimmingSubjectRe = CUString::TRIMMING_AND_SPACING_NORM_SUBJECT_RE;
             $inputString = CRegex::remove($inputString, "/^({$trimmingSubjectRe})+|({$trimmingSubjectRe})+\\z/u");
         }
         // Pre-process the string for integer and floating-point types.
         $looksLikeHex;
         if ($this->m_expectedType == self::INT || $this->m_expectedType == self::FLOAT) {
             if (CString::startsWith($inputString, "+")) {
                 // Remove the plus sign.
                 $inputString = CString::substr($inputString, 1);
             }
             $looksLikeHex = CRegex::find($inputString, "/^-?0x/i");
             if ($this->m_allowLeadingZeros && !($this->m_expectedType == self::INT && $this->m_allowHex && $looksLikeHex)) {
                 // Remove any leading zeros (except for special cases).
                 $inputString = CRegex::replace($inputString, "/^(\\D*)0*(?!\\b)/", "\$1");
             }
             if ($this->m_allowComma) {
                 $inputString = CRegex::remove($inputString, "/,(?=\\d{3}\\b)/");
             }
         }
         // Validate and sanitize the value according to its expected type.
         if ($this->m_expectedType == self::BOOL) {
             if (!CRegex::find($inputString, "/^(1|true|yes|on|0|false|no|off)\\z/i")) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             return CString::equals($inputString, "1") || CString::equalsCi($inputString, "true") || CString::equalsCi($inputString, "yes") || CString::equalsCi($inputString, "on");
         }
         if ($this->m_expectedType == self::INT) {
             $value;
             if (!($this->m_allowHex && $looksLikeHex)) {
                 // Regular.
                 if (!CRegex::find($inputString, "/^-?(?!0(?!\\b))\\d+\\z/")) {
                     $success = false;
                     return $this->m_defaultValue;
                 }
                 $value = CString::toInt($inputString);
             } else {
                 // Hex.
                 if (!CRegex::find($inputString, "/^-?0x[0-9A-F]+\\z/i")) {
                     $success = false;
                     return $this->m_defaultValue;
                 }
                 $value = CString::toIntFromHex($inputString);
             }
             if (isset($this->m_intValidMin) && $value < $this->m_intValidMin || isset($this->m_intValidMax) && $value > $this->m_intValidMax) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             if (isset($this->m_intClampingMin) && $value < $this->m_intClampingMin) {
                 $value = $this->m_intClampingMin;
             }
             if (isset($this->m_intClampingMax) && $value > $this->m_intClampingMax) {
                 $value = $this->m_intClampingMax;
             }
             return $value;
         }
         if ($this->m_expectedType == self::FLOAT) {
             if (!CRegex::find($inputString, "/^-?(?!0(?!\\b))\\d*\\.?\\d+(e[\\-+]?\\d+)?\\z/i")) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             $value = CString::toFloat($inputString);
             if (isset($this->m_floatValidMin) && $value < $this->m_floatValidMin || isset($this->m_floatValidMax) && $value > $this->m_floatValidMax) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             if (isset($this->m_floatClampingMin) && $value < $this->m_floatClampingMin) {
                 $value = $this->m_floatClampingMin;
             }
             if (isset($this->m_floatClampingMax) && $value > $this->m_floatClampingMax) {
                 $value = $this->m_floatClampingMax;
             }
             return $value;
         }
         if ($this->m_expectedType == self::CSTRING) {
             $value = $inputString;
             if (!CString::isValid($value)) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             if (!$this->m_keepAbnormalNewlines) {
                 $value = CString::normNewlines($value);
             }
             if (!$this->m_keepNonPrintable) {
                 if (!$this->m_keepTabsAndNewlines) {
                     $value = CRegex::remove($value, "/[\\x00-\\x1F\\x7F-\\xFF]/");
                 } else {
                     $value = CRegex::remove($value, "/[\\x00-\\x1F\\x7F-\\xFF](?<![\\x09\\x0A\\x0D])/");
                 }
             } else {
                 if (!$this->m_keepTabsAndNewlines) {
                     $value = CRegex::remove($value, "/[\\x09\\x0A\\x0D]/");
                 }
             }
             if (!$this->m_keepSideSpacing) {
                 $value = CString::trim($value);
             }
             if (!$this->m_keepExtraSpacing) {
                 $value = CString::normSpacing($value);
             }
             return $value;
         }
         if ($this->m_expectedType == self::CUSTRING) {
             $value = $inputString;
             if (!CUString::isValid($value)) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             if (!$this->m_keepAbnormalNewlines) {
                 $value = CUString::normNewlines($value);
             }
             if (!$this->m_keepNonPrintable) {
                 if (!$this->m_keepTabsAndNewlines) {
                     $value = CRegex::remove($value, "/\\p{C}|\\p{Zl}|\\p{Zp}/u");
                 } else {
                     $value = CRegex::remove($value, "/\\p{C}(?<!\\x{0009}|\\x{000A}|\\x{000D})/u");
                 }
             } else {
                 if (!$this->m_keepTabsAndNewlines) {
                     $value = CRegex::remove($value, "/\\x{0009}|\\x{000A}|\\x{000D}|\\p{Zl}|\\p{Zp}/u");
                 }
             }
             if (!$this->m_keepSideSpacing) {
                 $value = CUString::trim($value);
             }
             if (!$this->m_keepExtraSpacing) {
                 $value = CUString::normSpacing($value);
             }
             return $value;
         }
         if ($this->m_expectedType == self::EMAIL) {
             $value = filter_var($inputString, FILTER_VALIDATE_EMAIL);
             if (!is_cstring($value)) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             return $value;
         }
         if ($this->m_expectedType == self::URL) {
             $value = $inputString;
             if (!CUrl::isValid($value, $this->m_ignoreProtocolAbsence)) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             if ($this->m_ignoreProtocolAbsence) {
                 $value = CUrl::ensureProtocol($value);
             }
             return $value;
         }
         if ($this->m_expectedType == self::IP) {
             $value = $inputString;
             $options = CBitField::ALL_UNSET;
             if (!$this->m_allowPrivateRange) {
                 $options |= CIp::DISALLOW_PRIVATE_RANGE;
             }
             if (!$this->m_allowReservedRange) {
                 $options |= CIp::DISALLOW_RESERVED_RANGE;
             }
             $isValid;
             if (!$this->m_ipV6 && !$this->m_ipV4OrV6) {
                 $isValid = CIp::isValidV4($value, $options);
             } else {
                 if (!$this->m_ipV4OrV6) {
                     $isValid = CIp::isValidV6($value, $options);
                 } else {
                     $isValid = CIp::isValidV4($value, $options) || CIp::isValidV6($value, $options);
                 }
             }
             if (!$isValid) {
                 $success = false;
                 return $this->m_defaultValue;
             }
             return $value;
         }
     } else {
         if ($this->m_expectedType == self::CARRAY) {
             if (!is_cstring($inputStringOrDecodedCollection) && !is_carray($inputStringOrDecodedCollection)) {
                 $success = false;
                 return oop_x($this->m_defaultValue);
             }
             $value;
             if (is_cstring($inputStringOrDecodedCollection)) {
                 // Assume JSON format for the input string.
                 $json = new CJson($inputStringOrDecodedCollection, $this->m_jsonStrictness);
                 $value = $json->decode($success);
                 if (!$success) {
                     return oop_x($this->m_defaultValue);
                 }
                 if (!is_carray($value)) {
                     $success = false;
                     return oop_x($this->m_defaultValue);
                 }
             } else {
                 $value = $inputStringOrDecodedCollection;
             }
             $value = self::recurseCollectionFiltering($value, $this->m_collectionInputFilters, $success, 0);
             if (!$success) {
                 return oop_x($this->m_defaultValue);
             }
             return $value;
         } else {
             if (!is_cstring($inputStringOrDecodedCollection) && !is_cmap($inputStringOrDecodedCollection)) {
                 $success = false;
                 return oop_x($this->m_defaultValue);
             }
             $value;
             if (is_cstring($inputStringOrDecodedCollection)) {
                 // Assume JSON format for the input string.
                 $json = new CJson($inputStringOrDecodedCollection, $this->m_jsonStrictness);
                 $value = $json->decode($success);
                 if (!$success) {
                     return oop_x($this->m_defaultValue);
                 }
                 if (!is_cmap($value)) {
                     $success = false;
                     return oop_x($this->m_defaultValue);
                 }
             } else {
                 $value = $inputStringOrDecodedCollection;
             }
             $value = self::recurseCollectionFiltering($value, $this->m_collectionInputFilters, $success, 0);
             if (!$success) {
                 return oop_x($this->m_defaultValue);
             }
             return $value;
         }
     }
 }
Example #8
0
 /**
  * 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;
 }