MediaWiki
REL1_23
|
00001 <?php 00026 class FormatJson { 00034 const UTF8_OK = 1; 00035 00046 const XMLMETA_OK = 2; 00047 00055 const ALL_OK = 3; 00056 00066 const WS_CLEANUP_REGEX = '/(?<=[\[{])\n\s*+(?=[\]}])/'; 00067 00074 private static $badChars = array( 00075 "\xe2\x80\xa8", // U+2028 LINE SEPARATOR 00076 "\xe2\x80\xa9", // U+2029 PARAGRAPH SEPARATOR 00077 ); 00078 00082 private static $badCharsEscaped = array( 00083 '\u2028', // U+2028 LINE SEPARATOR 00084 '\u2029', // U+2029 PARAGRAPH SEPARATOR 00085 ); 00086 00104 public static function encode( $value, $pretty = false, $escaping = 0 ) { 00105 if ( !is_string( $pretty ) ) { 00106 $pretty = $pretty ? ' ' : false; 00107 } 00108 00109 if ( defined( 'JSON_UNESCAPED_UNICODE' ) ) { 00110 return self::encode54( $value, $pretty, $escaping ); 00111 } 00112 00113 return self::encode53( $value, $pretty, $escaping ); 00114 } 00115 00126 public static function decode( $value, $assoc = false ) { 00127 return json_decode( $value, $assoc ); 00128 } 00129 00138 private static function encode54( $value, $pretty, $escaping ) { 00139 // PHP escapes '/' to prevent breaking out of inline script blocks using '</script>', 00140 // which is hardly useful when '<' and '>' are escaped (and inadequate), and such 00141 // escaping negatively impacts the human readability of URLs and similar strings. 00142 $options = JSON_UNESCAPED_SLASHES; 00143 $options |= $pretty !== false ? JSON_PRETTY_PRINT : 0; 00144 $options |= ( $escaping & self::UTF8_OK ) ? JSON_UNESCAPED_UNICODE : 0; 00145 $options |= ( $escaping & self::XMLMETA_OK ) ? 0 : ( JSON_HEX_TAG | JSON_HEX_AMP ); 00146 $json = json_encode( $value, $options ); 00147 if ( $json === false ) { 00148 return false; 00149 } 00150 00151 if ( $pretty !== false ) { 00152 // Remove whitespace inside empty arrays/objects; different JSON encoders 00153 // vary on this, and we want our output to be consistent across implementations. 00154 $json = preg_replace( self::WS_CLEANUP_REGEX, '', $json ); 00155 if ( $pretty !== ' ' ) { 00156 // Change the four-space indent to a tab indent 00157 $json = str_replace( "\n ", "\n\t", $json ); 00158 while ( strpos( $json, "\t " ) !== false ) { 00159 $json = str_replace( "\t ", "\t\t", $json ); 00160 } 00161 00162 if ( $pretty !== "\t" ) { 00163 // Change the tab indent to the provided indent 00164 $json = str_replace( "\t", $pretty, $json ); 00165 } 00166 } 00167 } 00168 if ( $escaping & self::UTF8_OK ) { 00169 $json = str_replace( self::$badChars, self::$badCharsEscaped, $json ); 00170 } 00171 00172 return $json; 00173 } 00174 00184 private static function encode53( $value, $pretty, $escaping ) { 00185 $options = ( $escaping & self::XMLMETA_OK ) ? 0 : ( JSON_HEX_TAG | JSON_HEX_AMP ); 00186 $json = json_encode( $value, $options ); 00187 if ( $json === false ) { 00188 return false; 00189 } 00190 00191 // Emulate JSON_UNESCAPED_SLASHES. Because the JSON contains no unescaped slashes 00192 // (only escaped slashes), a simple string replacement works fine. 00193 $json = str_replace( '\/', '/', $json ); 00194 00195 if ( $escaping & self::UTF8_OK ) { 00196 // JSON hex escape sequences follow the format \uDDDD, where DDDD is four hex digits 00197 // indicating the equivalent UTF-16 code unit's value. To most efficiently unescape 00198 // them, we exploit the JSON extension's built-in decoder. 00199 // * We escape the input a second time, so any such sequence becomes \\uDDDD. 00200 // * To avoid interpreting escape sequences that were in the original input, 00201 // each double-escaped backslash (\\\\) is replaced with \\\u005c. 00202 // * We strip one of the backslashes from each of the escape sequences to unescape. 00203 // * Then the JSON decoder can perform the actual unescaping. 00204 $json = str_replace( "\\\\\\\\", "\\\\\\u005c", addcslashes( $json, '\"' ) ); 00205 $json = json_decode( preg_replace( "/\\\\\\\\u(?!00[0-7])/", "\\\\u", "\"$json\"" ) ); 00206 $json = str_replace( self::$badChars, self::$badCharsEscaped, $json ); 00207 } 00208 00209 if ( $pretty !== false ) { 00210 return self::prettyPrint( $json, $pretty ); 00211 } 00212 00213 return $json; 00214 } 00215 00224 private static function prettyPrint( $json, $indentString ) { 00225 $buf = ''; 00226 $indent = 0; 00227 $json = strtr( $json, array( '\\\\' => '\\\\', '\"' => "\x01" ) ); 00228 for ( $i = 0, $n = strlen( $json ); $i < $n; $i += $skip ) { 00229 $skip = 1; 00230 switch ( $json[$i] ) { 00231 case ':': 00232 $buf .= ': '; 00233 break; 00234 case '[': 00235 case '{': 00236 ++$indent; 00237 // falls through 00238 case ',': 00239 $buf .= $json[$i] . "\n" . str_repeat( $indentString, $indent ); 00240 break; 00241 case ']': 00242 case '}': 00243 $buf .= "\n" . str_repeat( $indentString, --$indent ) . $json[$i]; 00244 break; 00245 case '"': 00246 $skip = strcspn( $json, '"', $i + 1 ) + 2; 00247 $buf .= substr( $json, $i, $skip ); 00248 break; 00249 default: 00250 $skip = strcspn( $json, ',]}"', $i + 1 ) + 1; 00251 $buf .= substr( $json, $i, $skip ); 00252 } 00253 } 00254 $buf = preg_replace( self::WS_CLEANUP_REGEX, '', $buf ); 00255 00256 return str_replace( "\x01", '\"', $buf ); 00257 } 00258 }