diff --git a/externals/jsonlint/LICENSE b/externals/jsonlint/LICENSE new file mode 100644 index 0000000..c234487 --- /dev/null +++ b/externals/jsonlint/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/externals/jsonlint/src/Seld/JsonLint/JsonParser.php b/externals/jsonlint/src/Seld/JsonLint/JsonParser.php new file mode 100644 index 0000000..f9072b2 --- /dev/null +++ b/externals/jsonlint/src/Seld/JsonLint/JsonParser.php @@ -0,0 +1,460 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Parser class + * + * Example: + * + * $parser = new JsonParser(); + * // returns null if it's valid json, or an error object + * $parser->lint($json); + * // returns parsed json, like json_decode does, but slower, throws exceptions on failure. + * $parser->parse($json); + * + * Ported from https://github.com/zaach/jsonlint + */ +class JsonLintJsonParser +{ + const DETECT_KEY_CONFLICTS = 1; + const ALLOW_DUPLICATE_KEYS = 2; + + private $flags; + private $stack; + private $vstack; // semantic value stack + private $lstack; // location stack + + private $yy; + private $symbols = array( + 'error' => 2, + 'JSONString' => 3, + 'STRING' => 4, + 'JSONNumber' => 5, + 'NUMBER' => 6, + 'JSONNullLiteral' => 7, + 'NULL' => 8, + 'JSONBooleanLiteral' => 9, + 'TRUE' => 10, + 'FALSE' => 11, + 'JSONText' => 12, + 'JSONValue' => 13, + 'EOF' => 14, + 'JSONObject' => 15, + 'JSONArray' => 16, + '{' => 17, + '}' => 18, + 'JSONMemberList' => 19, + 'JSONMember' => 20, + ':' => 21, + ',' => 22, + '[' => 23, + ']' => 24, + 'JSONElementList' => 25, + '$accept' => 0, + '$end' => 1, + ); + + private $terminals_ = array( + 2 => "error", + 4 => "STRING", + 6 => "NUMBER", + 8 => "NULL", + 10 => "TRUE", + 11 => "FALSE", + 14 => "EOF", + 17 => "{", + 18 => "}", + 21 => ":", + 22 => ",", + 23 => "[", + 24 => "]", + ); + + private $productions_ = array( + 0, + array(3, 1), + array(5, 1), + array(7, 1), + array(9, 1), + array(9, 1), + array(12, 2), + array(13, 1), + array(13, 1), + array(13, 1), + array(13, 1), + array(13, 1), + array(13, 1), + array(15, 2), + array(15, 3), + array(20, 3), + array(19, 1), + array(19, 3), + array(16, 2), + array(16, 3), + array(25, 1), + array(25, 3) + ); + + private $table = array(array(3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 12 => 1, 13 => 2, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 1 => array(3)), array( 14 => array(1,16)), array( 14 => array(2,7), 18 => array(2,7), 22 => array(2,7), 24 => array(2,7)), array( 14 => array(2,8), 18 => array(2,8), 22 => array(2,8), 24 => array(2,8)), array( 14 => array(2,9), 18 => array(2,9), 22 => array(2,9), 24 => array(2,9)), array( 14 => array(2,10), 18 => array(2,10), 22 => array(2,10), 24 => array(2,10)), array( 14 => array(2,11), 18 => array(2,11), 22 => array(2,11), 24 => array(2,11)), array( 14 => array(2,12), 18 => array(2,12), 22 => array(2,12), 24 => array(2,12)), array( 14 => array(2,3), 18 => array(2,3), 22 => array(2,3), 24 => array(2,3)), array( 14 => array(2,4), 18 => array(2,4), 22 => array(2,4), 24 => array(2,4)), array( 14 => array(2,5), 18 => array(2,5), 22 => array(2,5), 24 => array(2,5)), array( 14 => array(2,1), 18 => array(2,1), 21 => array(2,1), 22 => array(2,1), 24 => array(2,1)), array( 14 => array(2,2), 18 => array(2,2), 22 => array(2,2), 24 => array(2,2)), array( 3 => 20, 4 => array(1,12), 18 => array(1,17), 19 => 18, 20 => 19 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 23, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15), 24 => array(1,21), 25 => 22 ), array( 1 => array(2,6)), array( 14 => array(2,13), 18 => array(2,13), 22 => array(2,13), 24 => array(2,13)), array( 18 => array(1,24), 22 => array(1,25)), array( 18 => array(2,16), 22 => array(2,16)), array( 21 => array(1,26)), array( 14 => array(2,18), 18 => array(2,18), 22 => array(2,18), 24 => array(2,18)), array( 22 => array(1,28), 24 => array(1,27)), array( 22 => array(2,20), 24 => array(2,20)), array( 14 => array(2,14), 18 => array(2,14), 22 => array(2,14), 24 => array(2,14)), array( 3 => 20, 4 => array(1,12), 20 => 29 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 30, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 14 => array(2,19), 18 => array(2,19), 22 => array(2,19), 24 => array(2,19)), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 31, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 18 => array(2,17), 22 => array(2,17)), array( 18 => array(2,15), 22 => array(2,15)), array( 22 => array(2,21), 24 => array(2,21)), + ); + + private $defaultActions = array( + 16 => array(2, 6) + ); + + /** + * @param string $input JSON string + * @return null|JsonLintParsingException null if no error is found, a JsonLintParsingException containing all details otherwise + */ + public function lint($input) + { + try { + $this->parse($input); + } catch (JsonLintParsingException $e) { + return $e; + } + } + + /** + * @param string $input JSON string + * @return mixed + * @throws JsonLintParsingException + */ + public function parse($input, $flags = 0) + { + $this->failOnBOM($input); + + $this->flags = $flags; + + $this->stack = array(0); + $this->vstack = array(null); + $this->lstack = array(); + + $yytext = ''; + $yylineno = 0; + $yyleng = 0; + $recovering = 0; + $TERROR = 2; + $EOF = 1; + + $this->lexer = new JsonLintLexer(); + $this->lexer->setInput($input); + + $yyloc = $this->lexer->yylloc; + $this->lstack[] = $yyloc; + + $symbol = null; + $preErrorSymbol = null; + $state = null; + $action = null; + $a = null; + $r = null; + $yyval = new stdClass; + $p = null; + $len = null; + $newState = null; + $expected = null; + $errStr = null; + + while (true) { + // retreive state number from top of stack + $state = $this->stack[count($this->stack)-1]; + + // use default actions if available + if (isset($this->defaultActions[$state])) { + $action = $this->defaultActions[$state]; + } else { + if ($symbol == null) { + $symbol = $this->lex(); + } + // read action for current state and first input + $action = isset($this->table[$state][$symbol]) ? $this->table[$state][$symbol] : false; + } + + // handle parse error + if (!$action || !$action[0]) { + if (!$recovering) { + // Report error + $expected = array(); + foreach ($this->table[$state] as $p => $ignore) { + if (isset($this->terminals_[$p]) && $p > 2) { + $expected[] = "'" . $this->terminals_[$p] . "'"; + } + } + + $message = null; + if (in_array("'STRING'", $expected) && in_array(substr($this->lexer->match, 0, 1), array('"', "'"))) { + $message = "Invalid string"; + if ("'" === substr($this->lexer->match, 0, 1)) { + $message .= ", it appears you used single quotes instead of double quotes"; + } elseif (preg_match('{".+?(\\\\[^"bfnrt/\\\\u])}', $this->lexer->getUpcomingInput(), $match)) { + $message .= ", it appears you have an unescaped backslash at: ".$match[1]; + } elseif (preg_match('{"(?:[^"]+|\\\\")*$}m', $this->lexer->getUpcomingInput())) { + $message .= ", it appears you forgot to terminated the string, or attempted to write a multiline string which is invalid"; + } + } + + $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n"; + $errStr .= $this->lexer->showPosition() . "\n"; + if ($message) { + $errStr .= $message; + } else { + $errStr .= (count($expected) > 1) ? "Expected one of: " : "Expected: "; + $errStr .= implode(', ', $expected); + } + + if (',' === substr(trim($this->lexer->getPastInput()), -1)) { + $errStr .= " - It appears you have an extra trailing comma"; + } + + $this->parseError($errStr, array( + 'text' => $this->lexer->match, + 'token' => !empty($this->terminals_[$symbol]) ? $this->terminals_[$symbol] : $symbol, + 'line' => $this->lexer->yylineno, + 'loc' => $yyloc, + 'expected' => $expected, + )); + } + + // just recovered from another error + if ($recovering == 3) { + if ($symbol == $EOF) { + throw new JsonLintParsingException($errStr ?: 'Parsing halted.'); + } + + // discard current lookahead and grab another + $yyleng = $this->lexer->yyleng; + $yytext = $this->lexer->yytext; + $yylineno = $this->lexer->yylineno; + $yyloc = $this->lexer->yylloc; + $symbol = $this->lex(); + } + + // try to recover from error + while (true) { + // check for error recovery rule in this state + if (array_key_exists($TERROR, $this->table[$state])) { + break; + } + if ($state == 0) { + throw new JsonLintParsingException($errStr ?: 'Parsing halted.'); + } + $this->popStack(1); + $state = $this->stack[count($this->stack)-1]; + } + + $preErrorSymbol = $symbol; // save the lookahead token + $symbol = $TERROR; // insert generic error symbol as new lookahead + $state = $this->stack[count($this->stack)-1]; + $action = isset($this->table[$state][$TERROR]) ? $this->table[$state][$TERROR] : false; + $recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } + + // this shouldn't happen, unless resolve defaults are off + if (is_array($action[0]) && count($action) > 1) { + throw new JsonLintParsingException('Parse Error: multiple actions possible at state: ' . $state . ', token: ' . $symbol); + } + + switch ($action[0]) { + case 1: // shift + $this->stack[] = $symbol; + $this->vstack[] = $this->lexer->yytext; + $this->lstack[] = $this->lexer->yylloc; + $this->stack[] = $action[1]; // push state + $symbol = null; + if (!$preErrorSymbol) { // normal execution/no error + $yyleng = $this->lexer->yyleng; + $yytext = $this->lexer->yytext; + $yylineno = $this->lexer->yylineno; + $yyloc = $this->lexer->yylloc; + if ($recovering > 0) { + $recovering--; + } + } else { // error just occurred, resume old lookahead f/ before error + $symbol = $preErrorSymbol; + $preErrorSymbol = null; + } + break; + + case 2: // reduce + $len = $this->productions_[$action[1]][1]; + + // perform semantic action + $yyval->token = $this->vstack[count($this->vstack) - $len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + $yyval->store = array( // _$ = store + 'first_line' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_line'], + 'last_line' => $this->lstack[count($this->lstack) - 1]['last_line'], + 'first_column' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_column'], + 'last_column' => $this->lstack[count($this->lstack) - 1]['last_column'], + ); + $r = $this->performAction($yyval, $yytext, $yyleng, $yylineno, $action[1], $this->vstack, $this->lstack); + + if (!$r instanceof JsonLintUndefined) { + return $r; + } + + if ($len) { + $this->popStack($len); + } + + $this->stack[] = $this->productions_[$action[1]][0]; // push nonterminal (reduce) + $this->vstack[] = $yyval->token; + $this->lstack[] = $yyval->store; + $newState = $this->table[$this->stack[count($this->stack)-2]][$this->stack[count($this->stack)-1]]; + $this->stack[] = $newState; + break; + + case 3: // accept + + return true; + } + } + + return true; + } + + protected function parseError($str, $hash) + { + throw new JsonLintParsingException($str, $hash); + } + + // $$ = $tokens // needs to be passed by ref? + // $ = $token + // _$ removed, useless? + private function performAction(stdClass $yyval, $yytext, $yyleng, $yylineno, $yystate, &$tokens) + { + // $0 = $len + $len = count($tokens) - 1; + switch ($yystate) { + case 1: + $yytext = preg_replace_callback('{(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4})}', array($this, 'stringInterpolation'), $yytext); + $yyval->token = $yytext; + break; + case 2: + if (strpos($yytext, 'e') !== false || strpos($yytext, 'E') !== false) { + $yyval->token = floatval($yytext); + } else { + $yyval->token = strpos($yytext, '.') === false ? intval($yytext) : floatval($yytext); + } + break; + case 3: + $yyval->token = null; + break; + case 4: + $yyval->token = true; + break; + case 5: + $yyval->token = false; + break; + case 6: + return $yyval->token = $tokens[$len-1]; + case 13: + $yyval->token = new stdClass; + break; + case 14: + $yyval->token = $tokens[$len-1]; + break; + case 15: + $yyval->token = array($tokens[$len-2], $tokens[$len]); + break; + case 16: + $yyval->token = new stdClass; + $property = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0]; + $yyval->token->$property = $tokens[$len][1]; + break; + case 17: + $yyval->token = $tokens[$len-2]; + $key = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0]; + if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len-2]->{$key})) { + $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n"; + $errStr .= $this->lexer->showPosition() . "\n"; + $errStr .= "Duplicate key: ".$tokens[$len][0]; + throw new JsonLintParsingException($errStr); + } elseif (($this->flags & self::ALLOW_DUPLICATE_KEYS) && isset($tokens[$len-2]->{$key})) { + $duplicateCount = 1; + do { + $duplicateKey = $key . '.' . $duplicateCount++; + } while (isset($tokens[$len-2]->$duplicateKey)); + $key = $duplicateKey; + } + $tokens[$len-2]->$key = $tokens[$len][1]; + break; + case 18: + $yyval->token = array(); + break; + case 19: + $yyval->token = $tokens[$len-1]; + break; + case 20: + $yyval->token = array($tokens[$len]); + break; + case 21: + $tokens[$len-2][] = $tokens[$len]; + $yyval->token = $tokens[$len-2]; + break; + } + + return new JsonLintUndefined(); + } + + private function stringInterpolation($match) + { + switch ($match[0]) { + case '\\\\': + return '\\'; + case '\"': + return '"'; + case '\b': + return chr(8); + case '\f': + return chr(12); + case '\n': + return "\n"; + case '\r': + return "\r"; + case '\t': + return "\t"; + case '\/': + return "/"; + default: + return html_entity_decode('&#x'.ltrim(substr($match[0], 2), '0').';', 0, 'UTF-8'); + } + } + + private function popStack($n) + { + $this->stack = array_slice($this->stack, 0, - (2 * $n)); + $this->vstack = array_slice($this->vstack, 0, - $n); + $this->lstack = array_slice($this->lstack, 0, - $n); + } + + private function lex() + { + $token = $this->lexer->lex() ?: 1; // $end = 1 + // if token isn't its numeric value, convert + if (!is_numeric($token)) { + $token = isset($this->symbols[$token]) ? $this->symbols[$token] : $token; + } + + return $token; + } + + private function failOnBOM($input) + { + // UTF-8 ByteOrderMark sequence + $bom = "\xEF\xBB\xBF"; + + if (substr($input, 0, 3) === $bom) { + $this->parseError("BOM detected, make sure your input does not include a Unicode Byte-Order-Mark", array()); + } + } +} diff --git a/externals/jsonlint/src/Seld/JsonLint/Lexer.php b/externals/jsonlint/src/Seld/JsonLint/Lexer.php new file mode 100644 index 0000000..bd179b4 --- /dev/null +++ b/externals/jsonlint/src/Seld/JsonLint/Lexer.php @@ -0,0 +1,227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Lexer class + * + * Ported from https://github.com/zaach/jsonlint + */ +class JsonLintLexer +{ + private $EOF = 1; + private $rules = array( + 0 => '/^\s+/', + 1 => '/^-?([0-9]|[1-9][0-9]+)(\.[0-9]+)?([eE][+-]?[0-9]+)?\b/', + 2 => '{^"(\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"}', + 3 => '/^\{/', + 4 => '/^\}/', + 5 => '/^\[/', + 6 => '/^\]/', + 7 => '/^,/', + 8 => '/^:/', + 9 => '/^true\b/', + 10 => '/^false\b/', + 11 => '/^null\b/', + 12 => '/^$/', + 13 => '/^./', + ); + + private $conditions = array( + "INITIAL" => array( + "rules" => array(0,1,2,3,4,5,6,7,8,9,10,11,12,13), + "inclusive" => true, + ), + ); + + private $conditionStack; + private $input; + private $more; + private $done; + private $matched; + + public $match; + public $yylineno; + public $yyleng; + public $yytext; + public $yylloc; + + public function lex() + { + $r = $this->next(); + if (!$r instanceof JsonLintUndefined) { + return $r; + } + + return $this->lex(); + } + + public function setInput($input) + { + $this->input = $input; + $this->more = false; + $this->done = false; + $this->yylineno = $this->yyleng = 0; + $this->yytext = $this->matched = $this->match = ''; + $this->conditionStack = array('INITIAL'); + $this->yylloc = array('first_line' => 1, 'first_column' => 0, 'last_line' => 1, 'last_column' => 0); + + return $this; + } + + public function showPosition() + { + $pre = str_replace("\n", '', $this->getPastInput()); + $c = str_repeat('-', strlen($pre) - 1); // new Array(pre.length + 1).join("-"); + + return $pre . str_replace("\n", '', $this->getUpcomingInput()) . "\n" . $c . "^"; + } + + public function getPastInput() + { + $past = substr($this->matched, 0, strlen($this->matched) - strlen($this->match)); + + return (strlen($past) > 20 ? '...' : '') . substr($past, -20); + } + + public function getUpcomingInput() + { + $next = $this->match; + if (strlen($next) < 20) { + $next .= substr($this->input, 0, 20 - strlen($next)); + } + + return substr($next, 0, 20) . (strlen($next) > 20 ? '...' : ''); + } + + protected function parseError($str, $hash) + { + throw new \Exception($str); + } + + private function next() + { + if ($this->done) { + return $this->EOF; + } + if (!$this->input) { + $this->done = true; + } + + $token = null; + $match = null; + $col = null; + $lines = null; + + if (!$this->more) { + $this->yytext = ''; + $this->match = ''; + } + + $rules = $this->getCurrentRules(); + $rulesLen = count($rules); + + for ($i=0; $i < $rulesLen; $i++) { + if (preg_match($this->rules[$rules[$i]], $this->input, $match)) { + preg_match_all('/\n.*/', $match[0], $lines); + $lines = $lines[0]; + if ($lines) { + $this->yylineno += count($lines); + } + + $this->yylloc = array( + 'first_line' => $this->yylloc['last_line'], + 'last_line' => $this->yylineno+1, + 'first_column' => $this->yylloc['last_column'], + 'last_column' => $lines ? strlen($lines[count($lines) - 1]) - 1 : $this->yylloc['last_column'] + strlen($match[0]), + ); + $this->yytext .= $match[0]; + $this->match .= $match[0]; + $this->matches = $match; + $this->yyleng = strlen($this->yytext); + $this->more = false; + $this->input = substr($this->input, strlen($match[0])); + $this->matched .= $match[0]; + $token = $this->performAction($rules[$i], $this->conditionStack[count($this->conditionStack)-1]); + if ($token) { + return $token; + } + + return new JsonLintUndefined(); + } + } + + if ($this->input === "") { + return $this->EOF; + } + + $this->parseError( + 'Lexical error on line ' . ($this->yylineno+1) . ". Unrecognized text.\n" . $this->showPosition(), + array( + 'text' => "", + 'token' => null, + 'line' => $this->yylineno, + ) + ); + } + + private function begin($condition) + { + $this->conditionStack[] = $condition; + } + + private function popState() + { + return array_pop($this->conditionStack); + } + + private function getCurrentRules() + { + return $this->conditions[$this->conditionStack[count($this->conditionStack)-1]]['rules']; + } + + private function performAction($avoiding_name_collisions, $YY_START) + { + $YYSTATE = $YY_START; + switch ($avoiding_name_collisions) { + case 0:/* skip whitespace */ + break; + case 1: + return 6; + break; + case 2: + $this->yytext = substr($this->yytext, 1, $this->yyleng-2); + + return 4; + case 3: + return 17; + case 4: + return 18; + case 5: + return 23; + case 6: + return 24; + case 7: + return 22; + case 8: + return 21; + case 9: + return 10; + case 10: + return 11; + case 11: + return 8; + case 12: + return 14; + case 13: + return 'INVALID'; + } + } +} diff --git a/externals/jsonlint/src/Seld/JsonLint/ParsingException.php b/externals/jsonlint/src/Seld/JsonLint/ParsingException.php new file mode 100644 index 0000000..2351cf6 --- /dev/null +++ b/externals/jsonlint/src/Seld/JsonLint/ParsingException.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class JsonLintParsingException extends Exception +{ + protected $details; + + public function __construct($message, $details = array()) + { + $this->details = $details; + parent::__construct($message); + } + + public function getDetails() + { + return $this->details; + } +} diff --git a/externals/jsonlint/src/Seld/JsonLint/Undefined.php b/externals/jsonlint/src/Seld/JsonLint/Undefined.php new file mode 100644 index 0000000..f6aa678 --- /dev/null +++ b/externals/jsonlint/src/Seld/JsonLint/Undefined.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class JsonLintUndefined +{ +} diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 40d1b2a..ddca57a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,693 +1,698 @@ 2, 'class' => array( 'AASTNode' => 'parser/aast/api/AASTNode.php', 'AASTNodeList' => 'parser/aast/api/AASTNodeList.php', 'AASTToken' => 'parser/aast/api/AASTToken.php', 'AASTTree' => 'parser/aast/api/AASTTree.php', 'AbstractDirectedGraph' => 'utils/AbstractDirectedGraph.php', 'AbstractDirectedGraphTestCase' => 'utils/__tests__/AbstractDirectedGraphTestCase.php', 'AphrontDatabaseConnection' => 'aphront/storage/connection/AphrontDatabaseConnection.php', 'AphrontDatabaseTransactionState' => 'aphront/storage/connection/AphrontDatabaseTransactionState.php', 'AphrontIsolatedDatabaseConnection' => 'aphront/storage/connection/AphrontIsolatedDatabaseConnection.php', 'AphrontMySQLDatabaseConnection' => 'aphront/storage/connection/mysql/AphrontMySQLDatabaseConnection.php', 'AphrontMySQLDatabaseConnectionBase' => 'aphront/storage/connection/mysql/AphrontMySQLDatabaseConnectionBase.php', 'AphrontMySQLiDatabaseConnection' => 'aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php', 'AphrontQueryAccessDeniedException' => 'aphront/storage/exception/AphrontQueryAccessDeniedException.php', 'AphrontQueryCharacterSetException' => 'aphront/storage/exception/AphrontQueryCharacterSetException.php', 'AphrontQueryConnectionException' => 'aphront/storage/exception/AphrontQueryConnectionException.php', 'AphrontQueryConnectionLostException' => 'aphront/storage/exception/AphrontQueryConnectionLostException.php', 'AphrontQueryCountException' => 'aphront/storage/exception/AphrontQueryCountException.php', 'AphrontQueryDeadlockException' => 'aphront/storage/exception/AphrontQueryDeadlockException.php', 'AphrontQueryDuplicateKeyException' => 'aphront/storage/exception/AphrontQueryDuplicateKeyException.php', 'AphrontQueryException' => 'aphront/storage/exception/AphrontQueryException.php', 'AphrontQueryNotSupportedException' => 'aphront/storage/exception/AphrontQueryNotSupportedException.php', 'AphrontQueryObjectMissingException' => 'aphront/storage/exception/AphrontQueryObjectMissingException.php', 'AphrontQueryParameterException' => 'aphront/storage/exception/AphrontQueryParameterException.php', 'AphrontQueryRecoverableException' => 'aphront/storage/exception/AphrontQueryRecoverableException.php', 'AphrontQuerySchemaException' => 'aphront/storage/exception/AphrontQuerySchemaException.php', 'AphrontScopedUnguardedWriteCapability' => 'aphront/writeguard/AphrontScopedUnguardedWriteCapability.php', 'AphrontWriteGuard' => 'aphront/writeguard/AphrontWriteGuard.php', 'AphrontWriteGuardExitEventListener' => 'aphront/writeguard/event/AphrontWriteGuardExitEventListener.php', 'BaseHTTPFuture' => 'future/http/BaseHTTPFuture.php', 'CommandException' => 'future/exec/CommandException.php', 'ConduitClient' => 'conduit/ConduitClient.php', 'ConduitClientException' => 'conduit/ConduitClientException.php', 'ConduitFuture' => 'conduit/ConduitFuture.php', 'ExecFuture' => 'future/exec/ExecFuture.php', 'ExecFutureTestCase' => 'future/exec/__tests__/ExecFutureTestCase.php', 'ExecPassthruTestCase' => 'future/exec/__tests__/ExecPassthruTestCase.php', 'FileFinder' => 'filesystem/FileFinder.php', 'FileFinderTestCase' => 'filesystem/__tests__/FileFinderTestCase.php', 'FileList' => 'filesystem/FileList.php', 'Filesystem' => 'filesystem/Filesystem.php', 'FilesystemException' => 'filesystem/FilesystemException.php', 'FilesystemTestCase' => 'filesystem/__tests__/FilesystemTestCase.php', 'Future' => 'future/Future.php', 'FutureIterator' => 'future/FutureIterator.php', 'FutureIteratorTestCase' => 'future/__tests__/FutureIteratorTestCase.php', 'FutureProxy' => 'future/FutureProxy.php', 'HTTPFuture' => 'future/http/HTTPFuture.php', 'HTTPFutureResponseStatus' => 'future/http/status/HTTPFutureResponseStatus.php', 'HTTPFutureResponseStatusCURL' => 'future/http/status/HTTPFutureResponseStatusCURL.php', 'HTTPFutureResponseStatusHTTP' => 'future/http/status/HTTPFutureResponseStatusHTTP.php', 'HTTPFutureResponseStatusParse' => 'future/http/status/HTTPFutureResponseStatusParse.php', 'HTTPFutureResponseStatusTransport' => 'future/http/status/HTTPFutureResponseStatusTransport.php', 'HTTPSFuture' => 'future/http/HTTPSFuture.php', 'ImmediateFuture' => 'future/ImmediateFuture.php', 'LinesOfALarge' => 'filesystem/linesofalarge/LinesOfALarge.php', 'LinesOfALargeExecFuture' => 'filesystem/linesofalarge/LinesOfALargeExecFuture.php', 'LinesOfALargeExecFutureTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeExecFutureTestCase.php', 'LinesOfALargeFile' => 'filesystem/linesofalarge/LinesOfALargeFile.php', 'LinesOfALargeFileTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeFileTestCase.php', 'MFilterTestHelper' => 'utils/__tests__/MFilterTestHelper.php', 'PHPASTParserTestCase' => 'parser/xhpast/__tests__/PHPASTParserTestCase.php', 'PhageAgentBootloader' => 'phage/bootloader/PhageAgentBootloader.php', 'PhageAgentTestCase' => 'phage/__tests__/PhageAgentTestCase.php', 'PhagePHPAgent' => 'phage/agent/PhagePHPAgent.php', 'PhagePHPAgentBootloader' => 'phage/bootloader/PhagePHPAgentBootloader.php', 'Phobject' => 'object/Phobject.php', 'PhutilAWSEC2Future' => 'future/aws/PhutilAWSEC2Future.php', 'PhutilAWSException' => 'future/aws/PhutilAWSException.php', 'PhutilAWSFuture' => 'future/aws/PhutilAWSFuture.php', 'PhutilAggregateException' => 'error/PhutilAggregateException.php', 'PhutilArgumentParser' => 'parser/argument/PhutilArgumentParser.php', 'PhutilArgumentParserException' => 'parser/argument/exception/PhutilArgumentParserException.php', 'PhutilArgumentParserTestCase' => 'parser/argument/__tests__/PhutilArgumentParserTestCase.php', 'PhutilArgumentSpecification' => 'parser/argument/PhutilArgumentSpecification.php', 'PhutilArgumentSpecificationException' => 'parser/argument/exception/PhutilArgumentSpecificationException.php', 'PhutilArgumentSpecificationTestCase' => 'parser/argument/__tests__/PhutilArgumentSpecificationTestCase.php', 'PhutilArgumentUsageException' => 'parser/argument/exception/PhutilArgumentUsageException.php', 'PhutilArgumentWorkflow' => 'parser/argument/workflow/PhutilArgumentWorkflow.php', 'PhutilArray' => 'utils/PhutilArray.php', 'PhutilArrayTestCase' => 'utils/__tests__/PhutilArrayTestCase.php', 'PhutilArrayWithDefaultValue' => 'utils/PhutilArrayWithDefaultValue.php', 'PhutilAsanaFuture' => 'future/asana/PhutilAsanaFuture.php', 'PhutilAuthAdapter' => 'auth/PhutilAuthAdapter.php', 'PhutilAuthAdapterEmpty' => 'auth/PhutilAuthAdapterEmpty.php', 'PhutilAuthAdapterLDAP' => 'auth/PhutilAuthAdapterLDAP.php', 'PhutilAuthAdapterOAuth' => 'auth/PhutilAuthAdapterOAuth.php', 'PhutilAuthAdapterOAuth1' => 'auth/PhutilAuthAdapterOAuth1.php', 'PhutilAuthAdapterOAuthAmazon' => 'auth/PhutilAuthAdapterOAuthAmazon.php', 'PhutilAuthAdapterOAuthAsana' => 'auth/PhutilAuthAdapterOAuthAsana.php', 'PhutilAuthAdapterOAuthDisqus' => 'auth/PhutilAuthAdapterOAuthDisqus.php', 'PhutilAuthAdapterOAuthFacebook' => 'auth/PhutilAuthAdapterOAuthFacebook.php', 'PhutilAuthAdapterOAuthGitHub' => 'auth/PhutilAuthAdapterOAuthGitHub.php', 'PhutilAuthAdapterOAuthGoogle' => 'auth/PhutilAuthAdapterOAuthGoogle.php', 'PhutilAuthAdapterOAuthJIRA' => 'auth/PhutilAuthAdapterOAuthJIRA.php', 'PhutilAuthAdapterOAuthTwitch' => 'auth/PhutilAuthAdapterOAuthTwitch.php', 'PhutilAuthAdapterOAuthTwitter' => 'auth/PhutilAuthAdapterOAuthTwitter.php', 'PhutilAuthAdapterOAuthWordPress' => 'auth/PhutilAuthAdapterOAuthWordPress.php', 'PhutilAuthAdapterPersona' => 'auth/PhutilAuthAdapterPersona.php', 'PhutilAuthConfigurationException' => 'auth/exception/PhutilAuthConfigurationException.php', 'PhutilAuthCredentialException' => 'auth/exception/PhutilAuthCredentialException.php', 'PhutilAuthException' => 'auth/exception/PhutilAuthException.php', 'PhutilAuthUserAbortedException' => 'auth/exception/PhutilAuthUserAbortedException.php', 'PhutilBallOfPHP' => 'phage/util/PhutilBallOfPHP.php', 'PhutilBufferedIterator' => 'utils/PhutilBufferedIterator.php', 'PhutilBufferedIteratorExample' => 'utils/PhutilBufferedIteratorExample.php', 'PhutilBufferedIteratorTestCase' => 'utils/__tests__/PhutilBufferedIteratorTestCase.php', 'PhutilBugtraqParser' => 'parser/PhutilBugtraqParser.php', 'PhutilBugtraqParserTestCase' => 'parser/__tests__/PhutilBugtraqParserTestCase.php', 'PhutilCLikeCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilCLikeCodeSnippetContextFreeGrammar.php', 'PhutilCallbackFilterIterator' => 'utils/PhutilCallbackFilterIterator.php', 'PhutilChannel' => 'channel/PhutilChannel.php', 'PhutilChannelChannel' => 'channel/PhutilChannelChannel.php', 'PhutilChannelTestCase' => 'channel/__tests__/PhutilChannelTestCase.php', 'PhutilChunkedIterator' => 'utils/PhutilChunkedIterator.php', 'PhutilChunkedIteratorTestCase' => 'utils/__tests__/PhutilChunkedIteratorTestCase.php', 'PhutilCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilCodeSnippetContextFreeGrammar.php', 'PhutilCommandString' => 'xsprintf/PhutilCommandString.php', 'PhutilConsole' => 'console/PhutilConsole.php', 'PhutilConsoleFormatter' => 'console/PhutilConsoleFormatter.php', 'PhutilConsoleMessage' => 'console/PhutilConsoleMessage.php', 'PhutilConsoleProgressBar' => 'console/PhutilConsoleProgressBar.php', 'PhutilConsoleServer' => 'console/PhutilConsoleServer.php', 'PhutilConsoleServerChannel' => 'console/PhutilConsoleServerChannel.php', 'PhutilConsoleStdinNotInteractiveException' => 'console/PhutilConsoleStdinNotInteractiveException.php', 'PhutilConsoleSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilConsoleSyntaxHighlighter.php', 'PhutilConsoleTable' => 'console/PhutilConsoleTable.php', 'PhutilConsoleWrapTestCase' => 'console/__tests__/PhutilConsoleWrapTestCase.php', 'PhutilContextFreeGrammar' => 'grammar/PhutilContextFreeGrammar.php', 'PhutilDaemon' => 'daemon/PhutilDaemon.php', 'PhutilDaemonOverseer' => 'daemon/PhutilDaemonOverseer.php', 'PhutilDefaultSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilDefaultSyntaxHighlighter.php', 'PhutilDefaultSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php', 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'markup/syntax/highlighter/pygments/PhutilDefaultSyntaxHighlighterEnginePygmentsFuture.php', 'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'markup/syntax/engine/__tests__/PhutilDefaultSyntaxHighlighterEngineTestCase.php', 'PhutilDeferredLog' => 'filesystem/PhutilDeferredLog.php', 'PhutilDeferredLogTestCase' => 'filesystem/__tests__/PhutilDeferredLogTestCase.php', 'PhutilDirectedScalarGraph' => 'utils/PhutilDirectedScalarGraph.php', 'PhutilDirectoryFixture' => 'filesystem/PhutilDirectoryFixture.php', 'PhutilDivinerSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilDivinerSyntaxHighlighter.php', 'PhutilDocblockParser' => 'parser/PhutilDocblockParser.php', 'PhutilDocblockParserTestCase' => 'parser/__tests__/PhutilDocblockParserTestCase.php', 'PhutilEditDistanceMatrix' => 'utils/PhutilEditDistanceMatrix.php', 'PhutilEditDistanceMatrixTestCase' => 'utils/__tests__/PhutilEditDistanceMatrixTestCase.php', 'PhutilEmailAddress' => 'parser/PhutilEmailAddress.php', 'PhutilEmailAddressTestCase' => 'parser/__tests__/PhutilEmailAddressTestCase.php', 'PhutilErrorHandler' => 'error/PhutilErrorHandler.php', 'PhutilErrorHandlerTestCase' => 'error/__tests__/PhutilErrorHandlerTestCase.php', 'PhutilErrorTrap' => 'error/PhutilErrorTrap.php', 'PhutilEvent' => 'events/PhutilEvent.php', 'PhutilEventConstants' => 'events/constant/PhutilEventConstants.php', 'PhutilEventEngine' => 'events/PhutilEventEngine.php', 'PhutilEventListener' => 'events/PhutilEventListener.php', 'PhutilEventType' => 'events/constant/PhutilEventType.php', 'PhutilExcessiveServiceCallsDaemon' => 'daemon/torture/PhutilExcessiveServiceCallsDaemon.php', 'PhutilExecChannel' => 'channel/PhutilExecChannel.php', 'PhutilExecPassthru' => 'future/exec/PhutilExecPassthru.php', 'PhutilExtensionsTestCase' => 'moduleutils/__tests__/PhutilExtensionsTestCase.php', 'PhutilFatalDaemon' => 'daemon/torture/PhutilFatalDaemon.php', 'PhutilFileLock' => 'filesystem/PhutilFileLock.php', 'PhutilFileLockTestCase' => 'filesystem/__tests__/PhutilFileLockTestCase.php', 'PhutilFileTree' => 'filesystem/PhutilFileTree.php', 'PhutilGitURI' => 'parser/PhutilGitURI.php', 'PhutilGitURITestCase' => 'parser/__tests__/PhutilGitURITestCase.php', 'PhutilHangForeverDaemon' => 'daemon/torture/PhutilHangForeverDaemon.php', 'PhutilHelpArgumentWorkflow' => 'parser/argument/workflow/PhutilHelpArgumentWorkflow.php', 'PhutilInfrastructureTestCase' => '__tests__/PhutilInfrastructureTestCase.php', 'PhutilInteractiveEditor' => 'console/PhutilInteractiveEditor.php', 'PhutilInvisibleSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilInvisibleSyntaxHighlighter.php', 'PhutilJSON' => 'parser/PhutilJSON.php', + 'PhutilJSONParser' => 'parser/PhutilJSONParser.php', + 'PhutilJSONParserException' => 'parser/exception/PhutilJSONParserException.php', + 'PhutilJSONParserTestCase' => 'parser/__tests__/PhutilJSONParserTestCase.php', 'PhutilJSONProtocolChannel' => 'channel/PhutilJSONProtocolChannel.php', 'PhutilJSONProtocolChannelTestCase' => 'channel/__tests__/PhutilJSONProtocolChannelTestCase.php', 'PhutilJSONTestCase' => 'parser/__tests__/PhutilJSONTestCase.php', 'PhutilJavaCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilJavaCodeSnippetContextFreeGrammar.php', 'PhutilKeyValueCache' => 'cache/PhutilKeyValueCache.php', 'PhutilKeyValueCacheAPC' => 'cache/PhutilKeyValueCacheAPC.php', 'PhutilKeyValueCacheDirectory' => 'cache/PhutilKeyValueCacheDirectory.php', 'PhutilKeyValueCacheInRequest' => 'cache/PhutilKeyValueCacheInRequest.php', 'PhutilKeyValueCacheMemcache' => 'cache/PhutilKeyValueCacheMemcache.php', 'PhutilKeyValueCacheNamespace' => 'cache/PhutilKeyValueCacheNamespace.php', 'PhutilKeyValueCacheOnDisk' => 'cache/PhutilKeyValueCacheOnDisk.php', 'PhutilKeyValueCacheProfiler' => 'cache/PhutilKeyValueCacheProfiler.php', 'PhutilKeyValueCacheProxy' => 'cache/PhutilKeyValueCacheProxy.php', 'PhutilKeyValueCacheStack' => 'cache/PhutilKeyValueCacheStack.php', 'PhutilKeyValueCacheTestCase' => 'cache/__tests__/PhutilKeyValueCacheTestCase.php', 'PhutilLanguageGuesser' => 'parser/PhutilLanguageGuesser.php', 'PhutilLanguageGuesserTestCase' => 'parser/__tests__/PhutilLanguageGuesserTestCase.php', 'PhutilLexer' => 'lexer/PhutilLexer.php', 'PhutilLexerSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilLexerSyntaxHighlighter.php', 'PhutilLipsumContextFreeGrammar' => 'grammar/PhutilLipsumContextFreeGrammar.php', 'PhutilLock' => 'filesystem/PhutilLock.php', 'PhutilLockException' => 'filesystem/PhutilLockException.php', 'PhutilLogfileChannel' => 'channel/PhutilLogfileChannel.php', 'PhutilLunarPhase' => 'utils/PhutilLunarPhase.php', 'PhutilLunarPhaseTestCase' => 'utils/__tests__/PhutilLunarPhaseTestCase.php', 'PhutilMarkupEngine' => 'markup/PhutilMarkupEngine.php', 'PhutilMarkupTestCase' => 'markup/__tests__/PhutilMarkupTestCase.php', 'PhutilMetricsChannel' => 'channel/PhutilMetricsChannel.php', 'PhutilMissingSymbolException' => 'symbols/exception/PhutilMissingSymbolException.php', 'PhutilNiceDaemon' => 'daemon/torture/PhutilNiceDaemon.php', 'PhutilNumber' => 'internationalization/PhutilNumber.php', 'PhutilOAuth1Future' => 'future/oauth/PhutilOAuth1Future.php', 'PhutilOAuth1FutureTestCase' => 'future/oauth/__tests__/PhutilOAuth1FutureTestCase.php', 'PhutilOpaqueEnvelope' => 'error/PhutilOpaqueEnvelope.php', 'PhutilOpaqueEnvelopeKey' => 'error/PhutilOpaqueEnvelopeKey.php', 'PhutilOpaqueEnvelopeTestCase' => 'error/__tests__/PhutilOpaqueEnvelopeTestCase.php', 'PhutilPHPCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilPHPCodeSnippetContextFreeGrammar.php', 'PhutilPHPFragmentLexer' => 'lexer/PhutilPHPFragmentLexer.php', 'PhutilPHPFragmentLexerHighlighterTestCase' => 'markup/syntax/highlighter/__tests__/PhutilPHPFragmentLexerHighlighterTestCase.php', 'PhutilPHPFragmentLexerTestCase' => 'lexer/__tests__/PhutilPHPFragmentLexerTestCase.php', 'PhutilPHPObjectProtocolChannel' => 'channel/PhutilPHPObjectProtocolChannel.php', 'PhutilPHPObjectProtocolChannelTestCase' => 'channel/__tests__/PhutilPHPObjectProtocolChannelTestCase.php', 'PhutilPHTTestCase' => 'internationalization/__tests__/PhutilPHTTestCase.php', 'PhutilParserGenerator' => 'parser/PhutilParserGenerator.php', 'PhutilParserGeneratorException' => 'parser/generator/exception/PhutilParserGeneratorException.php', 'PhutilParserGeneratorInvalidRuleException' => 'parser/generator/exception/PhutilParserGeneratorInvalidRuleException.php', 'PhutilParserGeneratorIrreducibleRuleException' => 'parser/generator/exception/PhutilParserGeneratorIrreducibleRuleException.php', 'PhutilParserGeneratorTestCase' => 'parser/__tests__/PhutilParserGeneratorTestCase.php', 'PhutilParserGeneratorUnknownSymbolException' => 'parser/generator/exception/PhutilParserGeneratorUnknownSymbolException.php', 'PhutilParserGeneratorUnreachableRuleException' => 'parser/generator/exception/PhutilParserGeneratorUnreachableRuleException.php', 'PhutilParserGeneratorUnreachableTerminalException' => 'parser/generator/exception/PhutilParserGeneratorUnreachableTerminalException.php', 'PhutilPayPalAPIFuture' => 'future/paypal/PhutilPayPalAPIFuture.php', 'PhutilPerson' => 'internationalization/PhutilPerson.php', 'PhutilPersonTest' => 'internationalization/__tests__/PhutilPersonTest.php', 'PhutilPhobjectTestCase' => 'object/__tests__/PhutilPhobjectTestCase.php', 'PhutilProcessGroupDaemon' => 'daemon/torture/PhutilProcessGroupDaemon.php', 'PhutilProtocolChannel' => 'channel/PhutilProtocolChannel.php', 'PhutilProxyException' => 'error/PhutilProxyException.php', 'PhutilPygmentsSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php', 'PhutilPythonFragmentLexer' => 'lexer/PhutilPythonFragmentLexer.php', 'PhutilQsprintfInterface' => 'xsprintf/PhutilQsprintfInterface.php', 'PhutilQueryStringParser' => 'parser/PhutilQueryStringParser.php', 'PhutilQueryStringParserTestCase' => 'parser/__tests__/PhutilQueryStringParserTestCase.php', 'PhutilRainbowSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilRainbowSyntaxHighlighter.php', 'PhutilReadableSerializer' => 'readableserializer/PhutilReadableSerializer.php', 'PhutilReadableSerializerTestCase' => 'readableserializer/__tests__/PhutilReadableSerializerTestCase.php', 'PhutilRealnameContextFreeGrammar' => 'grammar/PhutilRealnameContextFreeGrammar.php', 'PhutilRemarkupBlockInterpreter' => 'markup/engine/remarkup/blockrule/PhutilRemarkupBlockInterpreter.php', 'PhutilRemarkupBlockStorage' => 'markup/engine/remarkup/PhutilRemarkupBlockStorage.php', 'PhutilRemarkupEngine' => 'markup/engine/PhutilRemarkupEngine.php', 'PhutilRemarkupEngineBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineBlockRule.php', 'PhutilRemarkupEngineRemarkupCodeBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupCodeBlockRule.php', 'PhutilRemarkupEngineRemarkupDefaultBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupDefaultBlockRule.php', 'PhutilRemarkupEngineRemarkupHeaderBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHeaderBlockRule.php', 'PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule.php', 'PhutilRemarkupEngineRemarkupInlineBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInlineBlockRule.php', 'PhutilRemarkupEngineRemarkupInterpreterRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInterpreterRule.php', 'PhutilRemarkupEngineRemarkupListBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupListBlockRule.php', 'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupLiteralBlockRule.php', 'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupNoteBlockRule.php', 'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupQuotesBlockRule.php', 'PhutilRemarkupEngineRemarkupReplyBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupReplyBlockRule.php', 'PhutilRemarkupEngineRemarkupSimpleTableBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupSimpleTableBlockRule.php', 'PhutilRemarkupEngineRemarkupTableBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTableBlockRule.php', 'PhutilRemarkupEngineRemarkupTestInterpreterRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTestInterpreterRule.php', 'PhutilRemarkupEngineTestCase' => 'markup/engine/__tests__/PhutilRemarkupEngineTestCase.php', 'PhutilRemarkupRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRule.php', 'PhutilRemarkupRuleBold' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleBold.php', 'PhutilRemarkupRuleDel' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleDel.php', 'PhutilRemarkupRuleDocumentLink' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleDocumentLink.php', 'PhutilRemarkupRuleEscapeRemarkup' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleEscapeRemarkup.php', 'PhutilRemarkupRuleHyperlink' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleHyperlink.php', 'PhutilRemarkupRuleItalic' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleItalic.php', 'PhutilRemarkupRuleLinebreaks' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleLinebreaks.php', 'PhutilRemarkupRuleMonospace' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleMonospace.php', 'PhutilRemarkupRuleUnderline' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleUnderline.php', 'PhutilRope' => 'utils/PhutilRope.php', 'PhutilRopeTestCase' => 'utils/__tests__/PhutilRopeTestCase.php', 'PhutilSafeHTML' => 'markup/PhutilSafeHTML.php', 'PhutilSafeHTMLProducerInterface' => 'markup/PhutilSafeHTMLProducerInterface.php', 'PhutilSafeHTMLTestCase' => 'markup/__tests__/PhutilSafeHTMLTestCase.php', 'PhutilSaturateStdoutDaemon' => 'daemon/torture/PhutilSaturateStdoutDaemon.php', 'PhutilServiceProfiler' => 'serviceprofiler/PhutilServiceProfiler.php', 'PhutilShellLexer' => 'lexer/PhutilShellLexer.php', 'PhutilShellLexerTestCase' => 'lexer/__tests__/PhutilShellLexerTestCase.php', 'PhutilSimpleOptions' => 'parser/PhutilSimpleOptions.php', 'PhutilSimpleOptionsLexer' => 'lexer/PhutilSimpleOptionsLexer.php', 'PhutilSimpleOptionsLexerTestCase' => 'lexer/__tests__/PhutilSimpleOptionsLexerTestCase.php', 'PhutilSimpleOptionsTestCase' => 'parser/__tests__/PhutilSimpleOptionsTestCase.php', 'PhutilSocketChannel' => 'channel/PhutilSocketChannel.php', 'PhutilSprite' => 'sprites/PhutilSprite.php', 'PhutilSpriteSheet' => 'sprites/PhutilSpriteSheet.php', 'PhutilSymbolLoader' => 'symbols/PhutilSymbolLoader.php', 'PhutilSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilSyntaxHighlighter.php', 'PhutilSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilSyntaxHighlighterEngine.php', 'PhutilSyntaxHighlighterException' => 'markup/syntax/highlighter/PhutilSyntaxHighlighterException.php', 'PhutilTestCase' => 'infrastructure/testing/PhutilTestCase.php', 'PhutilTestPhobject' => 'object/__tests__/PhutilTestPhobject.php', 'PhutilTortureTestDaemon' => 'daemon/torture/PhutilTortureTestDaemon.php', 'PhutilTranslator' => 'internationalization/PhutilTranslator.php', 'PhutilTranslatorTestCase' => 'internationalization/__tests__/PhutilTranslatorTestCase.php', 'PhutilTwitchFuture' => 'future/twitch/PhutilTwitchFuture.php', 'PhutilTypeCheckException' => 'parser/exception/PhutilTypeCheckException.php', 'PhutilTypeExtraParametersException' => 'parser/exception/PhutilTypeExtraParametersException.php', 'PhutilTypeLexer' => 'lexer/PhutilTypeLexer.php', 'PhutilTypeMissingParametersException' => 'parser/exception/PhutilTypeMissingParametersException.php', 'PhutilTypeSpec' => 'parser/PhutilTypeSpec.php', 'PhutilTypeSpecTestCase' => 'parser/__tests__/PhutilTypeSpecTestCase.php', 'PhutilURI' => 'parser/PhutilURI.php', 'PhutilURITestCase' => 'parser/__tests__/PhutilURITestCase.php', 'PhutilUTF8TestCase' => 'utils/__tests__/PhutilUTF8TestCase.php', 'PhutilUtilsTestCase' => 'utils/__tests__/PhutilUtilsTestCase.php', 'PhutilWordPressFuture' => 'future/wordpress/PhutilWordPressFuture.php', 'PhutilXHPASTSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php', 'PhutilXHPASTSyntaxHighlighterFuture' => 'markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php', 'PhutilXHPASTSyntaxHighlighterTestCase' => 'markup/syntax/highlighter/__tests__/PhutilXHPASTSyntaxHighlighterTestCase.php', 'PhutilcsprintfTestCase' => 'xsprintf/__tests__/PhutilcsprintfTestCase.php', 'PhutilurisprintfTestCase' => 'xsprintf/__tests__/PhutilurisprintfTestCase.php', 'PhutilxsprintfTestCase' => 'xsprintf/__tests__/PhutilxsprintfTestCase.php', 'QueryFuture' => 'future/query/QueryFuture.php', 'TempFile' => 'filesystem/TempFile.php', 'TestAbstractDirectedGraph' => 'utils/__tests__/TestAbstractDirectedGraph.php', 'XHPASTNode' => 'parser/xhpast/api/XHPASTNode.php', 'XHPASTNodeTestCase' => 'parser/xhpast/api/__tests__/XHPASTNodeTestCase.php', 'XHPASTSyntaxErrorException' => 'parser/xhpast/api/XHPASTSyntaxErrorException.php', 'XHPASTToken' => 'parser/xhpast/api/XHPASTToken.php', 'XHPASTTree' => 'parser/xhpast/api/XHPASTTree.php', 'XHPASTTreeTestCase' => 'parser/xhpast/api/__tests__/XHPASTTreeTestCase.php', ), 'function' => array( 'Futures' => 'future/functions.php', '_qsprintf_check_scalar_type' => 'xsprintf/qsprintf.php', '_qsprintf_check_type' => 'xsprintf/qsprintf.php', 'array_fuse' => 'utils/utils.php', 'array_interleave' => 'utils/utils.php', 'array_mergev' => 'utils/utils.php', 'array_select_keys' => 'utils/utils.php', 'assert_instances_of' => 'utils/utils.php', 'assert_stringlike' => 'utils/utils.php', 'coalesce' => 'utils/utils.php', 'csprintf' => 'xsprintf/csprintf.php', 'exec_manual' => 'future/exec/execx.php', 'execx' => 'future/exec/execx.php', 'head' => 'utils/utils.php', 'head_key' => 'utils/utils.php', 'hgsprintf' => 'xsprintf/hgsprintf.php', 'hsprintf' => 'markup/render.php', 'id' => 'utils/utils.php', 'idx' => 'utils/utils.php', 'ifilter' => 'utils/utils.php', 'igroup' => 'utils/utils.php', 'ipull' => 'utils/utils.php', 'isort' => 'utils/utils.php', 'jsprintf' => 'xsprintf/jsprintf.php', 'last' => 'utils/utils.php', 'last_key' => 'utils/utils.php', 'ldap_sprintf' => 'xsprintf/ldapsprintf.php', 'mfilter' => 'utils/utils.php', 'mgroup' => 'utils/utils.php', 'mpull' => 'utils/utils.php', 'msort' => 'utils/utils.php', 'newv' => 'utils/utils.php', 'nonempty' => 'utils/utils.php', 'phlog' => 'error/phlog.php', 'pht' => 'internationalization/pht.php', 'phutil_censor_credentials' => 'utils/utils.php', 'phutil_console_confirm' => 'console/format.php', 'phutil_console_format' => 'console/format.php', 'phutil_console_get_terminal_width' => 'console/format.php', 'phutil_console_prompt' => 'console/format.php', 'phutil_console_require_tty' => 'console/format.php', 'phutil_console_wrap' => 'console/format.php', 'phutil_deprecated' => 'moduleutils/moduleutils.php', 'phutil_error_listener_example' => 'error/phlog.php', 'phutil_escape_html' => 'markup/render.php', 'phutil_escape_html_newlines' => 'markup/render.php', 'phutil_escape_uri' => 'markup/render.php', 'phutil_escape_uri_path_component' => 'markup/render.php', 'phutil_exit' => 'utils/utils.php', 'phutil_fwrite_nonblocking_stream' => 'utils/utils.php', 'phutil_get_library_name_for_root' => 'moduleutils/moduleutils.php', 'phutil_get_library_root' => 'moduleutils/moduleutils.php', 'phutil_get_library_root_for_path' => 'moduleutils/moduleutils.php', 'phutil_get_signal_name' => 'future/exec/execx.php', 'phutil_implode_html' => 'markup/render.php', 'phutil_is_hiphop_runtime' => 'utils/utils.php', 'phutil_is_utf8' => 'utils/utf8.php', 'phutil_is_utf8_with_only_bmp_characters' => 'utils/utf8.php', 'phutil_is_windows' => 'utils/utils.php', 'phutil_json_decode' => 'utils/utils.php', 'phutil_loggable_string' => 'utils/utils.php', 'phutil_passthru' => 'future/exec/execx.php', 'phutil_safe_html' => 'markup/render.php', 'phutil_split_lines' => 'utils/utils.php', 'phutil_tag' => 'markup/render.php', 'phutil_tag_div' => 'markup/render.php', 'phutil_unescape_uri_path_component' => 'markup/render.php', 'phutil_units' => 'utils/utils.php', 'phutil_utf8_console_strlen' => 'utils/utf8.php', 'phutil_utf8_convert' => 'utils/utf8.php', 'phutil_utf8_hard_wrap' => 'utils/utf8.php', 'phutil_utf8_hard_wrap_html' => 'utils/utf8.php', 'phutil_utf8_is_combining_character' => 'utils/utf8.php', 'phutil_utf8_shorten' => 'utils/utf8.php', 'phutil_utf8_strlen' => 'utils/utf8.php', 'phutil_utf8_strtolower' => 'utils/utf8.php', 'phutil_utf8_strtoupper' => 'utils/utf8.php', 'phutil_utf8_strtr' => 'utils/utf8.php', 'phutil_utf8_ucwords' => 'utils/utf8.php', 'phutil_utf8ize' => 'utils/utf8.php', 'phutil_utf8v' => 'utils/utf8.php', 'phutil_utf8v_codepoints' => 'utils/utf8.php', 'phutil_utf8v_combined' => 'utils/utf8.php', 'ppull' => 'utils/utils.php', 'qsprintf' => 'xsprintf/qsprintf.php', 'queryfx' => 'xsprintf/queryfx.php', 'queryfx_all' => 'xsprintf/queryfx.php', 'queryfx_one' => 'xsprintf/queryfx.php', 'urisprintf' => 'xsprintf/urisprintf.php', 'vcsprintf' => 'xsprintf/csprintf.php', 'vjsprintf' => 'xsprintf/jsprintf.php', 'vqsprintf' => 'xsprintf/qsprintf.php', 'vqueryfx' => 'xsprintf/queryfx.php', 'vqueryfx_all' => 'xsprintf/queryfx.php', 'vurisprintf' => 'xsprintf/urisprintf.php', 'xhp_parser_node_constants' => 'parser/xhpast/parser_nodes.php', 'xhpast_build' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_get_binary_path' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_get_build_instructions' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_get_parser_future' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_is_available' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_parser_token_constants' => 'parser/xhpast/parser_tokens.php', 'xsprintf' => 'xsprintf/xsprintf.php', 'xsprintf_callback_example' => 'xsprintf/xsprintf.php', 'xsprintf_command' => 'xsprintf/csprintf.php', 'xsprintf_javascript' => 'xsprintf/jsprintf.php', 'xsprintf_ldap' => 'xsprintf/ldapsprintf.php', 'xsprintf_mercurial' => 'xsprintf/hgsprintf.php', 'xsprintf_query' => 'xsprintf/qsprintf.php', 'xsprintf_uri' => 'xsprintf/urisprintf.php', ), 'xmap' => array( 'AASTNodeList' => array( 0 => 'Iterator', 1 => 'Countable', ), 'AbstractDirectedGraphTestCase' => 'PhutilTestCase', 'AphrontDatabaseConnection' => 'PhutilQsprintfInterface', 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontMySQLDatabaseConnection' => 'AphrontMySQLDatabaseConnectionBase', 'AphrontMySQLDatabaseConnectionBase' => 'AphrontDatabaseConnection', 'AphrontMySQLiDatabaseConnection' => 'AphrontMySQLDatabaseConnectionBase', 'AphrontQueryAccessDeniedException' => 'AphrontQueryRecoverableException', 'AphrontQueryCharacterSetException' => 'AphrontQueryException', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', 'AphrontQueryCountException' => 'AphrontQueryException', 'AphrontQueryDeadlockException' => 'AphrontQueryRecoverableException', 'AphrontQueryDuplicateKeyException' => 'AphrontQueryException', 'AphrontQueryException' => 'Exception', 'AphrontQueryNotSupportedException' => 'AphrontQueryException', 'AphrontQueryObjectMissingException' => 'AphrontQueryException', 'AphrontQueryParameterException' => 'AphrontQueryException', 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontQuerySchemaException' => 'AphrontQueryException', 'AphrontWriteGuardExitEventListener' => 'PhutilEventListener', 'BaseHTTPFuture' => 'Future', 'CommandException' => 'Exception', 'ConduitClientException' => 'Exception', 'ConduitFuture' => 'FutureProxy', 'ExecFuture' => 'Future', 'ExecFutureTestCase' => 'PhutilTestCase', 'ExecPassthruTestCase' => 'PhutilTestCase', 'FileFinderTestCase' => 'PhutilTestCase', 'FilesystemException' => 'Exception', 'FilesystemTestCase' => 'PhutilTestCase', 'FutureIterator' => 'Iterator', 'FutureIteratorTestCase' => 'PhutilTestCase', 'FutureProxy' => 'Future', 'HTTPFuture' => 'BaseHTTPFuture', 'HTTPFutureResponseStatus' => 'Exception', 'HTTPFutureResponseStatusCURL' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusHTTP' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusParse' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusTransport' => 'HTTPFutureResponseStatus', 'HTTPSFuture' => 'BaseHTTPFuture', 'ImmediateFuture' => 'Future', 'LinesOfALarge' => 'Iterator', 'LinesOfALargeExecFuture' => 'LinesOfALarge', 'LinesOfALargeExecFutureTestCase' => 'PhutilTestCase', 'LinesOfALargeFile' => 'LinesOfALarge', 'LinesOfALargeFileTestCase' => 'PhutilTestCase', 'PHPASTParserTestCase' => 'PhutilTestCase', 'PhageAgentTestCase' => 'PhutilTestCase', 'PhagePHPAgentBootloader' => 'PhageAgentBootloader', 'Phobject' => 'Iterator', 'PhutilAWSEC2Future' => 'PhutilAWSFuture', 'PhutilAWSException' => 'Exception', 'PhutilAWSFuture' => 'FutureProxy', 'PhutilAggregateException' => 'Exception', 'PhutilArgumentParserException' => 'Exception', 'PhutilArgumentParserTestCase' => 'PhutilTestCase', 'PhutilArgumentSpecificationException' => 'PhutilArgumentParserException', 'PhutilArgumentSpecificationTestCase' => 'PhutilTestCase', 'PhutilArgumentUsageException' => 'PhutilArgumentParserException', 'PhutilArray' => array( 0 => 'Phobject', 1 => 'Countable', 2 => 'ArrayAccess', 3 => 'Iterator', ), 'PhutilArrayTestCase' => 'PhutilTestCase', 'PhutilArrayWithDefaultValue' => 'PhutilArray', 'PhutilAsanaFuture' => 'FutureProxy', 'PhutilAuthAdapterEmpty' => 'PhutilAuthAdapter', 'PhutilAuthAdapterLDAP' => 'PhutilAuthAdapter', 'PhutilAuthAdapterOAuth' => 'PhutilAuthAdapter', 'PhutilAuthAdapterOAuth1' => 'PhutilAuthAdapter', 'PhutilAuthAdapterOAuthAmazon' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterOAuthAsana' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterOAuthDisqus' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterOAuthFacebook' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterOAuthGitHub' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterOAuthGoogle' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterOAuthJIRA' => 'PhutilAuthAdapterOAuth1', 'PhutilAuthAdapterOAuthTwitch' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterOAuthTwitter' => 'PhutilAuthAdapterOAuth1', 'PhutilAuthAdapterOAuthWordPress' => 'PhutilAuthAdapterOAuth', 'PhutilAuthAdapterPersona' => 'PhutilAuthAdapter', 'PhutilAuthConfigurationException' => 'PhutilAuthException', 'PhutilAuthCredentialException' => 'PhutilAuthException', 'PhutilAuthException' => 'Exception', 'PhutilAuthUserAbortedException' => 'PhutilAuthException', 'PhutilBufferedIterator' => 'Iterator', 'PhutilBufferedIteratorExample' => 'PhutilBufferedIterator', 'PhutilBufferedIteratorTestCase' => 'PhutilTestCase', 'PhutilBugtraqParserTestCase' => 'PhutilTestCase', 'PhutilCLikeCodeSnippetContextFreeGrammar' => 'PhutilCodeSnippetContextFreeGrammar', 'PhutilCallbackFilterIterator' => 'FilterIterator', 'PhutilChannelChannel' => 'PhutilChannel', 'PhutilChannelTestCase' => 'PhutilTestCase', 'PhutilChunkedIterator' => 'Iterator', 'PhutilChunkedIteratorTestCase' => 'PhutilTestCase', 'PhutilCodeSnippetContextFreeGrammar' => 'PhutilContextFreeGrammar', 'PhutilCommandString' => 'Phobject', 'PhutilConsoleProgressBar' => 'Phobject', 'PhutilConsoleServerChannel' => 'PhutilChannelChannel', 'PhutilConsoleStdinNotInteractiveException' => 'Exception', 'PhutilConsoleTable' => 'Phobject', 'PhutilConsoleWrapTestCase' => 'PhutilTestCase', 'PhutilDefaultSyntaxHighlighterEngine' => 'PhutilSyntaxHighlighterEngine', 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'FutureProxy', 'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'PhutilTestCase', 'PhutilDeferredLogTestCase' => 'PhutilTestCase', 'PhutilDirectedScalarGraph' => 'AbstractDirectedGraph', 'PhutilDocblockParserTestCase' => 'PhutilTestCase', 'PhutilEditDistanceMatrixTestCase' => 'PhutilTestCase', 'PhutilEmailAddressTestCase' => 'PhutilTestCase', 'PhutilErrorHandlerTestCase' => 'PhutilTestCase', 'PhutilErrorTrap' => 'Phobject', 'PhutilEventType' => 'PhutilEventConstants', 'PhutilExcessiveServiceCallsDaemon' => 'PhutilTortureTestDaemon', 'PhutilExecChannel' => 'PhutilChannel', 'PhutilExecPassthru' => 'Phobject', 'PhutilExtensionsTestCase' => 'PhutilTestCase', 'PhutilFatalDaemon' => 'PhutilTortureTestDaemon', 'PhutilFileLock' => 'PhutilLock', 'PhutilFileLockTestCase' => 'PhutilTestCase', 'PhutilGitURITestCase' => 'PhutilTestCase', 'PhutilHangForeverDaemon' => 'PhutilTortureTestDaemon', 'PhutilHelpArgumentWorkflow' => 'PhutilArgumentWorkflow', 'PhutilInfrastructureTestCase' => 'PhutilTestCase', + 'PhutilJSONParserException' => 'Exception', + 'PhutilJSONParserTestCase' => 'PhutilTestCase', 'PhutilJSONProtocolChannel' => 'PhutilProtocolChannel', 'PhutilJSONProtocolChannelTestCase' => 'PhutilTestCase', 'PhutilJSONTestCase' => 'PhutilTestCase', 'PhutilJavaCodeSnippetContextFreeGrammar' => 'PhutilCLikeCodeSnippetContextFreeGrammar', 'PhutilKeyValueCacheAPC' => 'PhutilKeyValueCache', 'PhutilKeyValueCacheDirectory' => 'PhutilKeyValueCache', 'PhutilKeyValueCacheInRequest' => 'PhutilKeyValueCache', 'PhutilKeyValueCacheMemcache' => 'PhutilKeyValueCache', 'PhutilKeyValueCacheNamespace' => 'PhutilKeyValueCacheProxy', 'PhutilKeyValueCacheOnDisk' => 'PhutilKeyValueCache', 'PhutilKeyValueCacheProfiler' => 'PhutilKeyValueCacheProxy', 'PhutilKeyValueCacheProxy' => 'PhutilKeyValueCache', 'PhutilKeyValueCacheStack' => 'PhutilKeyValueCache', 'PhutilKeyValueCacheTestCase' => 'ArcanistPhutilTestCase', 'PhutilLanguageGuesserTestCase' => 'PhutilTestCase', 'PhutilLexerSyntaxHighlighter' => 'PhutilSyntaxHighlighter', 'PhutilLipsumContextFreeGrammar' => 'PhutilContextFreeGrammar', 'PhutilLockException' => 'Exception', 'PhutilLogfileChannel' => 'PhutilChannelChannel', 'PhutilLunarPhaseTestCase' => 'PhutilTestCase', 'PhutilMarkupTestCase' => 'PhutilTestCase', 'PhutilMetricsChannel' => 'PhutilChannelChannel', 'PhutilMissingSymbolException' => 'Exception', 'PhutilNiceDaemon' => 'PhutilTortureTestDaemon', 'PhutilOAuth1Future' => 'FutureProxy', 'PhutilOAuth1FutureTestCase' => 'PhutilTestCase', 'PhutilOpaqueEnvelopeTestCase' => 'PhutilTestCase', 'PhutilPHPCodeSnippetContextFreeGrammar' => 'PhutilCLikeCodeSnippetContextFreeGrammar', 'PhutilPHPFragmentLexer' => 'PhutilLexer', 'PhutilPHPFragmentLexerHighlighterTestCase' => 'PhutilTestCase', 'PhutilPHPFragmentLexerTestCase' => 'PhutilTestCase', 'PhutilPHPObjectProtocolChannel' => 'PhutilProtocolChannel', 'PhutilPHPObjectProtocolChannelTestCase' => 'PhutilTestCase', 'PhutilPHTTestCase' => 'PhutilTestCase', 'PhutilParserGeneratorException' => 'Exception', 'PhutilParserGeneratorInvalidRuleException' => 'PhutilParserGeneratorException', 'PhutilParserGeneratorIrreducibleRuleException' => 'PhutilParserGeneratorException', 'PhutilParserGeneratorTestCase' => 'PhutilTestCase', 'PhutilParserGeneratorUnknownSymbolException' => 'PhutilParserGeneratorException', 'PhutilParserGeneratorUnreachableRuleException' => 'PhutilParserGeneratorException', 'PhutilParserGeneratorUnreachableTerminalException' => 'PhutilParserGeneratorException', 'PhutilPayPalAPIFuture' => 'FutureProxy', 'PhutilPersonTest' => 'PhutilPerson', 'PhutilPhobjectTestCase' => 'PhutilTestCase', 'PhutilProcessGroupDaemon' => 'PhutilTortureTestDaemon', 'PhutilProtocolChannel' => 'PhutilChannelChannel', 'PhutilProxyException' => 'Exception', 'PhutilPythonFragmentLexer' => 'PhutilLexer', 'PhutilQueryStringParserTestCase' => 'PhutilTestCase', 'PhutilReadableSerializerTestCase' => 'PhutilTestCase', 'PhutilRealnameContextFreeGrammar' => 'PhutilContextFreeGrammar', 'PhutilRemarkupEngine' => 'PhutilMarkupEngine', 'PhutilRemarkupEngineRemarkupCodeBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupDefaultBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupHeaderBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupInlineBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupInterpreterRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupListBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupReplyBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupSimpleTableBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupTableBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupTestInterpreterRule' => 'PhutilRemarkupBlockInterpreter', 'PhutilRemarkupEngineTestCase' => 'PhutilTestCase', 'PhutilRemarkupRuleBold' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleDel' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleDocumentLink' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleEscapeRemarkup' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleHyperlink' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleItalic' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleLinebreaks' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleMonospace' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleUnderline' => 'PhutilRemarkupRule', 'PhutilRope' => 'Phobject', 'PhutilRopeTestCase' => 'PhutilTestCase', 'PhutilSafeHTMLTestCase' => 'PhutilTestCase', 'PhutilSaturateStdoutDaemon' => 'PhutilTortureTestDaemon', 'PhutilShellLexer' => 'PhutilLexer', 'PhutilShellLexerTestCase' => 'PhutilTestCase', 'PhutilSimpleOptionsLexer' => 'PhutilLexer', 'PhutilSimpleOptionsLexerTestCase' => 'PhutilTestCase', 'PhutilSimpleOptionsTestCase' => 'PhutilTestCase', 'PhutilSocketChannel' => 'PhutilChannel', 'PhutilSyntaxHighlighterException' => 'Exception', 'PhutilTestCase' => 'ArcanistPhutilTestCase', 'PhutilTestPhobject' => 'Phobject', 'PhutilTortureTestDaemon' => 'PhutilDaemon', 'PhutilTranslatorTestCase' => 'PhutilTestCase', 'PhutilTwitchFuture' => 'FutureProxy', 'PhutilTypeCheckException' => 'Exception', 'PhutilTypeExtraParametersException' => 'Exception', 'PhutilTypeLexer' => 'PhutilLexer', 'PhutilTypeMissingParametersException' => 'Exception', 'PhutilTypeSpecTestCase' => 'PhutilTestCase', 'PhutilURITestCase' => 'PhutilTestCase', 'PhutilUTF8TestCase' => 'PhutilTestCase', 'PhutilUtilsTestCase' => 'PhutilTestCase', 'PhutilWordPressFuture' => 'FutureProxy', 'PhutilXHPASTSyntaxHighlighterFuture' => 'FutureProxy', 'PhutilXHPASTSyntaxHighlighterTestCase' => 'PhutilTestCase', 'PhutilcsprintfTestCase' => 'ArcanistTestCase', 'PhutilurisprintfTestCase' => 'ArcanistTestCase', 'PhutilxsprintfTestCase' => 'ArcanistTestCase', 'QueryFuture' => 'Future', 'TestAbstractDirectedGraph' => 'AbstractDirectedGraph', 'XHPASTNode' => 'AASTNode', 'XHPASTNodeTestCase' => 'PhutilTestCase', 'XHPASTSyntaxErrorException' => 'Exception', 'XHPASTToken' => 'AASTToken', 'XHPASTTree' => 'AASTTree', 'XHPASTTreeTestCase' => 'PhutilTestCase', ), )); diff --git a/src/parser/PhutilJSONParser.php b/src/parser/PhutilJSONParser.php new file mode 100644 index 0000000..1575691 --- /dev/null +++ b/src/parser/PhutilJSONParser.php @@ -0,0 +1,42 @@ +parse($json); + } catch (JsonLintParsingException $ex) { + throw new PhutilJSONParserException($ex->getMessage()); + } + + if (!$output instanceof stdClass && !is_array($output)) { + throw new PhutilJSONParserException( + pht( + '%s is not a valid JSON object.', + PhutilReadableSerializer::printShort($json))); + } + + return (array)$output; + } + +} diff --git a/src/parser/__tests__/PhutilJSONParserTestCase.php b/src/parser/__tests__/PhutilJSONParserTestCase.php new file mode 100644 index 0000000..c97e342 --- /dev/null +++ b/src/parser/__tests__/PhutilJSONParserTestCase.php @@ -0,0 +1,50 @@ + array(), + '[]' => array(), + '{"foo": "bar"}' => array('foo' => 'bar'), + '[1, "foo", true, null]' => array(1, 'foo', true, null), + ); + + foreach ($tests as $input => $expect) { + $this->assertEqual( + $expect, + $parser->parse($input), + 'Parsing JSON: '.$input); + } + } + + public function testInvalidJSON() { + $parser = new PhutilJSONParser(); + + $tests = array( + '', + 'null', + 'false', + 'true', + '"quack quack I am a duck lol"', + '{', + '[', + '{"foo":', + '{"foo":"bar",}', + '{{}', + ); + + foreach ($tests as $input) { + $caught = null; + try { + $parser->parse($input); + } catch (Exception $ex) { + $caught = $ex; + } + $this->assertTrue($caught instanceof PhutilJSONParserException); + } + } + +} diff --git a/src/parser/exception/PhutilJSONParserException.php b/src/parser/exception/PhutilJSONParserException.php new file mode 100644 index 0000000..ef44939 --- /dev/null +++ b/src/parser/exception/PhutilJSONParserException.php @@ -0,0 +1,3 @@ +