1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-26 23:40:56 +01:00

Merge all unmerged "libphutil" changes into Aracnist "wilds" branch

Ref T13098.
This commit is contained in:
epriestley 2019-02-01 18:24:48 -08:00
parent bd58840220
commit 01d31e291d
32 changed files with 4318 additions and 2555 deletions

View file

@ -22,6 +22,8 @@ phutil_register_library_map(array(
'AphrontConnectionQueryException' => 'aphront/storage/exception/AphrontConnectionQueryException.php',
'AphrontCountQueryException' => 'aphront/storage/exception/AphrontCountQueryException.php',
'AphrontDatabaseConnection' => 'aphront/storage/connection/AphrontDatabaseConnection.php',
'AphrontDatabaseTableRef' => 'xsprintf/AphrontDatabaseTableRef.php',
'AphrontDatabaseTableRefInterface' => 'xsprintf/AphrontDatabaseTableRefInterface.php',
'AphrontDatabaseTransactionState' => 'aphront/storage/connection/AphrontDatabaseTransactionState.php',
'AphrontDeadlockQueryException' => 'aphront/storage/exception/AphrontDeadlockQueryException.php',
'AphrontDuplicateKeyQueryException' => 'aphront/storage/exception/AphrontDuplicateKeyQueryException.php',
@ -793,6 +795,7 @@ phutil_register_library_map(array(
'PhutilPygmentsSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php',
'PhutilPythonFragmentLexer' => 'lexer/PhutilPythonFragmentLexer.php',
'PhutilQsprintfInterface' => 'xsprintf/PhutilQsprintfInterface.php',
'PhutilQueryString' => 'xsprintf/PhutilQueryString.php',
'PhutilQueryStringParser' => 'parser/PhutilQueryStringParser.php',
'PhutilQueryStringParserTestCase' => 'parser/__tests__/PhutilQueryStringParserTestCase.php',
'PhutilRainbowSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilRainbowSyntaxHighlighter.php',
@ -955,6 +958,7 @@ phutil_register_library_map(array(
'nonempty' => 'utils/utils.php',
'phlog' => 'error/phlog.php',
'pht' => 'internationalization/pht.php',
'phutil_build_http_querystring' => 'utils/utils.php',
'phutil_censor_credentials' => 'utils/utils.php',
'phutil_console_confirm' => 'console/format.php',
'phutil_console_format' => 'console/format.php',
@ -965,6 +969,7 @@ phutil_register_library_map(array(
'phutil_console_wrap' => 'console/format.php',
'phutil_count' => 'internationalization/pht.php',
'phutil_date_format' => 'utils/viewutils.php',
'phutil_decode_mime_header' => 'utils/utils.php',
'phutil_deprecated' => 'moduleutils/moduleutils.php',
'phutil_error_listener_example' => 'error/phlog.php',
'phutil_escape_html' => 'markup/render.php',
@ -996,6 +1001,7 @@ phutil_register_library_map(array(
'phutil_json_encode' => 'utils/utils.php',
'phutil_load_library' => 'moduleutils/core.php',
'phutil_loggable_string' => 'utils/utils.php',
'phutil_microseconds_since' => 'utils/utils.php',
'phutil_parse_bytes' => 'utils/viewutils.php',
'phutil_passthru' => 'future/exec/execx.php',
'phutil_person' => 'internationalization/pht.php',
@ -1075,6 +1081,10 @@ phutil_register_library_map(array(
'Phobject',
'PhutilQsprintfInterface',
),
'AphrontDatabaseTableRef' => array(
'Phobject',
'AphrontDatabaseTableRefInterface',
),
'AphrontDatabaseTransactionState' => 'Phobject',
'AphrontDeadlockQueryException' => 'AphrontRecoverableQueryException',
'AphrontDuplicateKeyQueryException' => 'AphrontQueryException',
@ -1869,6 +1879,7 @@ phutil_register_library_map(array(
'PhutilPygmentizeParserTestCase' => 'PhutilTestCase',
'PhutilPygmentsSyntaxHighlighter' => 'Phobject',
'PhutilPythonFragmentLexer' => 'PhutilLexer',
'PhutilQueryString' => 'Phobject',
'PhutilQueryStringParser' => 'Phobject',
'PhutilQueryStringParserTestCase' => 'PhutilTestCase',
'PhutilRainbowSyntaxHighlighter' => 'Phobject',

View file

@ -17,7 +17,7 @@ abstract class AphrontDatabaseConnection
abstract public function getInsertID();
abstract public function getAffectedRows();
abstract public function selectAllResults();
abstract public function executeRawQuery($raw_query);
abstract public function executeQuery(PhutilQueryString $query);
abstract public function executeRawQueries(array $raw_queries);
abstract public function close();
abstract public function openConnection();
@ -93,6 +93,27 @@ abstract class AphrontDatabaseConnection
throw new Exception(pht('Async queries are not supported.'));
}
/**
* Is this connection idle and safe to close?
*
* A connection is "idle" if it can be safely closed without loss of state.
* Connections inside a transaction or holding locks are not idle, even
* though they may not actively be executing queries.
*
* @return bool True if the connection is idle and can be safely closed.
*/
public function isIdle() {
if ($this->isInsideTransaction()) {
return false;
}
if ($this->isHoldingAnyLock()) {
return false;
}
return true;
}
/* -( Global Locks )------------------------------------------------------- */

View file

@ -66,12 +66,15 @@ final class AphrontIsolatedDatabaseConnection
return $this->allResults;
}
public function executeRawQuery($raw_query) {
public function executeQuery(PhutilQueryString $query) {
// NOTE: "[\s<>K]*" allows any number of (properly escaped) comments to
// appear prior to the allowed keyword, since this connection escapes
// them as "<K>" (above).
$display_query = $query->getMaskedString();
$raw_query = $query->getUnmaskedString();
$keywords = array(
'INSERT',
'UPDATE',
@ -94,10 +97,10 @@ final class AphrontIsolatedDatabaseConnection
"trying to issue a query which does not begin with an allowed ".
"keyword (%s): '%s'.",
implode(', ', $keywords),
$raw_query));
$display_query));
}
$this->transcript[] = $raw_query;
$this->transcript[] = $display_query;
// NOTE: This method is intentionally simplified for now, since we're only
// using it to stub out inserts/updates. In the future it will probably need

View file

@ -152,7 +152,10 @@ abstract class AphrontBaseMySQLDatabaseConnection
return $result;
}
public function executeRawQuery($raw_query) {
public function executeQuery(PhutilQueryString $query) {
$display_query = $query->getMaskedString();
$raw_query = $query->getUnmaskedString();
$this->lastResult = null;
$retries = max(1, $this->getConfiguration('retries', 3));
while ($retries--) {
@ -165,7 +168,7 @@ abstract class AphrontBaseMySQLDatabaseConnection
array(
'type' => 'query',
'config' => $this->configuration,
'query' => $raw_query,
'query' => $display_query,
'write' => $is_write,
));
@ -297,10 +300,10 @@ abstract class AphrontBaseMySQLDatabaseConnection
throw new AphrontConnectionLostQueryException($message);
case 2006: // Gone Away
$more = pht(
"This error may occur if your MySQL '%s' or '%s' ".
"configuration values are set too low.",
'wait_timeout',
'max_allowed_packet');
'This error may occur if your configured MySQL "wait_timeout" or '.
'"max_allowed_packet" values are too small. This may also indicate '.
'that something used the MySQL "KILL <process>" command to kill '.
'the connection running the query.');
throw new AphrontConnectionLostQueryException("{$message}\n\n{$more}");
case 1213: // Deadlock
throw new AphrontDeadlockQueryException($message);

View file

@ -81,6 +81,11 @@ final class AphrontMySQLiDatabaseConnection
$this->throwConnectionException($errno, $error, $user, $host);
}
// See T13238. Attempt to prevent "LOAD DATA LOCAL INFILE", which allows a
// malicious server to ask the client for any file. At time of writing,
// this option MUST be set after "real_connect()" on all PHP versions.
$conn->options(MYSQLI_OPT_LOCAL_INFILE, 0);
$this->connectionOpen = true;
$ok = @$conn->set_charset('utf8mb4');
@ -122,7 +127,36 @@ final class AphrontMySQLiDatabaseConnection
return @$conn->reap_async_query();
}
return @$conn->query($raw_query);
$trap = new PhutilErrorTrap();
$result = @$conn->query($raw_query);
$err = $trap->getErrorsAsString();
$trap->destroy();
// See T13238 and PHI1014. Sometimes, the call to "$conn->query()" may fail
// without setting an error code on the connection. One way to reproduce
// this is to use "LOAD DATA LOCAL INFILE" with "mysqli.allow_local_infile"
// disabled.
// If we have no result and no error code, raise a synthetic query error
// with whatever error message was raised as a local PHP warning.
if (!$result) {
$error_code = $this->getErrorCode($conn);
if (!$error_code) {
if (strlen($err)) {
$message = $err;
} else {
$message = pht(
'Call to "mysqli->query()" failed, but did not set an error '.
'code or emit an error message.');
}
$this->throwQueryCodeException(777777, $message);
}
}
return $result;
}
protected function rawQueries(array $raw_queries) {

View file

@ -14,21 +14,11 @@ final class PhutilGoogleAuthAdapter extends PhutilOAuthAuthAdapter {
}
public function getAccountID() {
$emails = $this->getOAuthAccountData('emails', array());
foreach ($emails as $email) {
if (idx($email, 'type') == 'account') {
return idx($email, 'value');
}
}
throw new Exception(
pht(
'Expected to retrieve an "account" email from Google Plus API call '.
'to identify account, but failed.'));
return $this->getAccountEmail();
}
public function getAccountEmail() {
return $this->getAccountID();
return $this->getOAuthAccountData('email');
}
public function getAccountName() {
@ -40,8 +30,7 @@ final class PhutilGoogleAuthAdapter extends PhutilOAuthAuthAdapter {
}
public function getAccountImageURI() {
$image = $this->getOAuthAccountData('image', array());
$uri = idx($image, 'url');
$uri = $this->getOAuthAccountData('picture');
// Change the "sz" parameter ("size") from the default to 100 to ask for
// a 100x100px image.
@ -55,24 +44,11 @@ final class PhutilGoogleAuthAdapter extends PhutilOAuthAuthAdapter {
}
public function getAccountURI() {
return $this->getOAuthAccountData('url');
return $this->getOAuthAccountData('link');
}
public function getAccountRealName() {
$name = $this->getOAuthAccountData('name', array());
// TODO: This could probably be made cleaner by looking up the API, but
// this should work to unbreak logins.
$parts = array();
$parts[] = idx($name, 'givenName');
unset($name['givenName']);
$parts[] = idx($name, 'familyName');
unset($name['familyName']);
$parts = array_merge($parts, $name);
$parts = array_filter($parts);
return implode(' ', $parts);
return $this->getOAuthAccountData('name');
}
protected function getAuthenticateBaseURI() {
@ -105,77 +81,25 @@ final class PhutilGoogleAuthAdapter extends PhutilOAuthAuthAdapter {
}
protected function loadOAuthAccountData() {
$uri = new PhutilURI('https://www.googleapis.com/plus/v1/people/me');
$uri = new PhutilURI('https://www.googleapis.com/userinfo/v2/me');
$uri->setQueryParam('access_token', $this->getAccessToken());
$future = new HTTPSFuture($uri);
list($status, $body) = $future->resolve();
if ($status->isError()) {
$this->tryToThrowSpecializedError($status, $body);
throw $status;
}
try {
return phutil_json_decode($body);
$result = phutil_json_decode($body);
} catch (PhutilJSONParserException $ex) {
throw new PhutilProxyException(
pht('Expected valid JSON response from Google account data request.'),
$ex);
}
}
private function tryToThrowSpecializedError($status, $raw_body) {
if (!($status instanceof HTTPFutureHTTPResponseStatus)) {
return;
}
if ($status->getStatusCode() != 403) {
return;
}
$body = phutil_json_decode($raw_body);
if (!$body) {
return;
}
if (empty($body['error']['errors'][0])) {
return;
}
$error = $body['error']['errors'][0];
$domain = idx($error, 'domain');
$reason = idx($error, 'reason');
if ($domain == 'usageLimits' && $reason == 'accessNotConfigured') {
throw new PhutilAuthConfigurationException(
pht(
'Google returned an "%s" error. This usually means you need to '.
'enable the "Google+ API" in your Google Cloud Console, under '.
'"APIs".'.
"\n\n".
'Around March 2014, Google made some API changes which require this '.
'configuration adjustment.'.
"\n\n".
'Normally, you can resolve this issue by going to %s, then '.
'clicking "API Project", then "APIs & auth", then turning the '.
'"Google+ API" on. The names you see on the console may be '.
'different depending on how your integration is set up. If you '.
'are not sure, you can hunt through the projects until you find '.
'the one associated with the right Application ID under '.
'"Credentials". The Application ID this install is using is "%s".'.
"\n\n".
'(If you are unable to log into Phabricator, you can use '.
'"%s" to recover access to an administrator account.)'.
"\n\n".
'Full HTTP Response'.
"\n\n%s",
'accessNotConfigured',
'https://console.developers.google.com/',
$this->getClientID(),
'bin/auth recover',
$raw_body));
}
return $result;
}
}

View file

@ -535,6 +535,56 @@ final class Filesystem extends Phobject {
}
/**
* Generate a random integer value in a given range.
*
* This method uses less-entropic random sources under older versions of PHP.
*
* @param int Minimum value, inclusive.
* @param int Maximum value, inclusive.
*/
public static function readRandomInteger($min, $max) {
if (!is_int($min)) {
throw new Exception(pht('Minimum value must be an integer.'));
}
if (!is_int($max)) {
throw new Exception(pht('Maximum value must be an integer.'));
}
if ($min > $max) {
throw new Exception(
pht(
'Minimum ("%d") must not be greater than maximum ("%d").',
$min,
$max));
}
// Under PHP 7.2.0 and newer, we can just use "random_int()". This function
// is intended to generate cryptographically usable entropy.
if (function_exists('random_int')) {
return random_int($min, $max);
}
// We could find a stronger source for this, but correctly converting raw
// bytes to an integer range without biases is fairly hard and it seems
// like we're more likely to get that wrong than suffer a PRNG prediction
// issue by falling back to "mt_rand()".
if (($max - $min) > mt_getrandmax()) {
throw new Exception(
pht('mt_rand() range is smaller than the requested range.'));
}
$result = mt_rand($min, $max);
if (!is_int($result)) {
throw new Exception(pht('Bad return value from mt_rand().'));
}
return $result;
}
/**
* Identify the MIME type of a file. This returns only the MIME type (like
* text/plain), not the encoding (like charset=utf-8).

View file

@ -170,4 +170,42 @@ final class FilesystemTestCase extends PhutilTestCase {
}
}
public function testRandomIntegers() {
$valid_ranges = array(
array(5, 5),
array(-1, 1),
array(0, 10000),
array(0, 999999999),
array(-65535, 65536),
);
foreach ($valid_ranges as $case) {
list($min, $max) = $case;
$result = Filesystem::readRandomInteger($min, $max);
$this->assertTrue($min <= $result, pht('%d <= %d', $min, $result));
$this->assertTrue($max >= $result, pht('%d >= %d', $max, $result));
}
$invalid_ranges = array(
array('1', '2'),
array(1.0, 2.0),
array(5, 3),
);
foreach ($invalid_ranges as $case) {
list($min, $max) = $case;
$caught = null;
try {
Filesystem::readRandomInteger($min, $max);
} catch (Exception $ex) {
$caught = $ex;
}
$this->assertTrue($caught instanceof Exception);
}
}
}

View file

@ -350,8 +350,19 @@ final class ExecFuture extends PhutilExecutableFuture {
list($err, $stdout, $stderr) = $this->resolve($timeout);
if ($err) {
$cmd = $this->command;
if ($this->getWasKilledByTimeout()) {
// NOTE: The timeout can be a float and PhutilNumber only handles
// integers, so just use "%s" to render it.
$message = pht(
'Command killed by timeout after running for more than %s seconds.',
$this->terminateTimeout);
} else {
$message = pht('Command failed with error #%d!', $err);
}
throw new CommandException(
pht('Command failed with error #%d!', $err),
$message,
$cmd,
$err,
$stdout,

View file

@ -273,7 +273,7 @@ abstract class BaseHTTPFuture extends Future {
return strlen($data);
}
return strlen(http_build_query($data, '', '&'));
return strlen(phutil_build_http_querystring($data));
}

View file

@ -244,7 +244,7 @@ final class HTTPFuture extends BaseHTTPFuture {
if ($this->getMethod() == 'GET') {
if (is_array($data)) {
$data = http_build_query($data, '', '&');
$data = phutil_build_http_querystring($data);
if (strpos($uri, '?') !== false) {
$uri .= '&'.$data;
} else {
@ -254,7 +254,7 @@ final class HTTPFuture extends BaseHTTPFuture {
}
} else {
if (is_array($data)) {
$data = http_build_query($data, '', '&')."\r\n";
$data = phutil_build_http_querystring($data)."\r\n";
$add_headers[] = array(
'Content-Type',
'application/x-www-form-urlencoded',

View file

@ -524,7 +524,7 @@ final class HTTPSFuture extends BaseHTTPFuture {
// If we don't have any files, just encode the data as a query string,
// make sure it's not including any files, and we're good to go.
if (is_array($data)) {
$data = http_build_query($data, '', '&');
$data = phutil_build_http_querystring($data);
}
$this->checkForDangerousCURLMagic($data, $is_query_string = true);

View file

@ -6,6 +6,7 @@ final class PhutilPostmarkFuture extends FutureProxy {
private $accessToken;
private $method;
private $parameters;
private $timeout;
public function __construct() {
parent::__construct(null);
@ -27,6 +28,15 @@ final class PhutilPostmarkFuture extends FutureProxy {
return $this;
}
public function setTimeout($timeout) {
$this->timeout = $timeout;
return $this;
}
public function getTimeout() {
return $this->timeout;
}
protected function getProxiedFuture() {
if (!$this->future) {
if ($this->accessToken === null) {
@ -49,6 +59,11 @@ final class PhutilPostmarkFuture extends FutureProxy {
->addHeader('Accept', 'application/json')
->addHeader('Content-Type', 'application/json');
$timeout = $this->getTimeout();
if ($timeout) {
$future->setTimeout($timeout);
}
$this->future = $future;
}

View file

@ -81,7 +81,7 @@ final class PhutilJavaFragmentLexer extends PhutilLexer {
array('(package|import\\s+static|import)\\b', 'kn', 'import'),
array('('.implode('|', $constants).')\\b', 'kc'),
array('(class|interface)\\b', 'kd', 'class'),
array('"(\\\\\\\\|\\\\"|[^"]+)*"', 's'),
array('"(\\\\.|[^"\\\\]+)*"', 's'),
array("'(\\\\.|[^\\\\]|\\\\u[0-9a-f-A-F]{4})'", 's'),
array('([^\\W\\d]|\\$)[\\w$]*:', 'nl'),
array('([^\\W\\d]|\\$)[\\w$]*', 'n'),

View file

@ -11,7 +11,7 @@ final class PhutilRemarkupTestInterpreterRule
return sprintf(
"Content: (%s)\nArgv: (%s)",
$content,
http_build_query($argv));
phutil_build_http_querystring($argv));
}
}

View file

@ -21,15 +21,28 @@ final class PhutilPygmentsSyntaxHighlighter extends Phobject {
if ($language) {
$language = $this->getPygmentsLexerNameFromLanguageName($language);
// See T13224. Under Ubuntu, avoid leaving an intermedite "dash" shell
// process so we hit "pygmentize" directly if we have to SIGKILL this
// because it explodes.
$future = new ExecFuture(
'pygmentize -O encoding=utf-8 -O stripnl=False -f html -l %s',
'exec pygmentize -O encoding=utf-8 -O stripnl=False -f html -l %s',
$language);
$scrub = false;
if ($language == 'php' && strpos($source, '<?') === false) {
$source = "<?php\n".$source;
$scrub = true;
}
// See T13224. In some cases, "pygmentize" has explosive runtime on small
// inputs. Put a hard cap on how long it is allowed to run for to limit
// the amount of damage it can do.
$future->setTimeout(15);
$future->write($source);
return new PhutilDefaultSyntaxHighlighterEnginePygmentsFuture(
$future,
$source,

View file

@ -174,7 +174,7 @@ final class PhutilURI extends Phobject {
}
if ($this->query) {
$query = '?'.http_build_query($this->query, '', '&');
$query = '?'.phutil_build_http_querystring($this->query);
} else {
$query = null;
}

View file

@ -4,6 +4,10 @@ final class PhutilArgumentSpellingCorrector extends Phobject {
private $editDistanceMatrix;
private $maximumDistance;
private $mode;
const MODE_COMMANDS = 'commands';
const MODE_FLAGS = 'flags';
/**
* Build a new corrector with parameters for correcting commands, like
@ -31,6 +35,7 @@ final class PhutilArgumentSpellingCorrector extends Phobject {
return id(new self())
->setEditDistanceMatrix($matrix)
->setMode(self::MODE_COMMANDS)
->setMaximumDistance($max_distance);
}
@ -55,9 +60,19 @@ final class PhutilArgumentSpellingCorrector extends Phobject {
return id(new self())
->setEditDistanceMatrix($matrix)
->setMode(self::MODE_FLAGS)
->setMaximumDistance($max_distance);
}
public function setMode($mode) {
$this->mode = $mode;
return $this;
}
public function getMode() {
return $this->mode;
}
public function setEditDistanceMatrix(PhutilEditDistanceMatrix $matrix) {
$this->editDistanceMatrix = $matrix;
return $this;
@ -87,6 +102,14 @@ final class PhutilArgumentSpellingCorrector extends Phobject {
throw new PhutilInvalidStateException('setMaximumDistance');
}
// If we're correcting commands, never correct an input which begins
// with "-", since this is almost certainly intended to be a flag.
if ($this->getMode() === self::MODE_COMMANDS) {
if (preg_match('/^-/', $input)) {
return array();
}
}
$input = $this->normalizeString($input);
foreach ($options as $key => $option) {
$options[$key] = $this->normalizeString($option);

View file

@ -0,0 +1,599 @@
<?php
$a = new class {};
$b = new class(10) extends c implements d {
private $num;
public function __construct($num) {
$this->num = $num;
}
};
~~~~~~~~~~
pass
~~~~~~~~~~
{
"tree": [
9000,
0,
64,
[
[
9006,
0,
63,
[
[
9007,
0,
0
],
[
9004,
2,
12,
[
[
9077,
2,
11,
[
[
9047,
2,
2
],
[
9081,
4,
4
],
[
9074,
6,
11,
[
[
9051,
8,
11,
[
[
0,
8,
8
],
[
9005
],
[
9005
],
[
9005
],
[
9006,
10,
11
]
]
],
[
9005
]
]
]
]
]
]
],
[
9004,
14,
63,
[
[
9077,
14,
62,
[
[
9047,
14,
14
],
[
9081,
16,
16
],
[
9074,
18,
62,
[
[
9051,
20,
62,
[
[
0,
20,
20
],
[
9005
],
[
9054,
25,
27,
[
[
9090,
27,
27
]
]
],
[
9055,
29,
31,
[
[
9090,
31,
31
]
]
],
[
9006,
33,
62,
[
[
9004,
35,
38,
[
[
9063,
35,
37,
[
[
9070,
35,
35,
[
[
9013,
35,
35
]
]
],
[
9064,
37,
37,
[
[
9047,
37,
37
],
[
9005
]
]
]
]
]
]
],
[
9004,
40,
60,
[
[
9067,
40,
60,
[
[
9068,
40,
40,
[
[
9013,
40,
40
]
]
],
[
9005
],
[
9013,
44,
44
],
[
9059,
45,
47,
[
[
9060,
46,
46,
[
[
9005
],
[
9047,
46,
46
],
[
9005
]
]
]
]
],
[
9005
],
[
9005
],
[
9006,
49,
60,
[
[
9004,
51,
58,
[
[
9077,
51,
57,
[
[
9092,
51,
53,
[
[
9047,
51,
51
],
[
9013,
53,
53
]
]
],
[
9081,
55,
55
],
[
9047,
57,
57
]
]
]
]
]
]
]
]
]
]
]
]
]
]
],
[
9095,
21,
23,
[
[
9086,
22,
22
]
]
]
]
]
]
]
]
]
]
]
]
],
"stream": [
[
373,
5
],
[
377,
2
],
[
313,
2
],
[
377,
1
],
[
61,
1
],
[
377,
1
],
[
302,
3
],
[
377,
1
],
[
358,
5
],
[
377,
1
],
[
123,
1
],
[
125,
1
],
[
59,
1
],
[
377,
1
],
[
313,
2
],
[
377,
1
],
[
61,
1
],
[
377,
1
],
[
302,
3
],
[
377,
1
],
[
358,
5
],
[
40,
1
],
[
309,
2
],
[
41,
1
],
[
377,
1
],
[
360,
7
],
[
377,
1
],
[
311,
1
],
[
377,
1
],
[
361,
10
],
[
377,
1
],
[
311,
1
],
[
377,
1
],
[
123,
1
],
[
377,
3
],
[
350,
7
],
[
377,
1
],
[
313,
4
],
[
59,
1
],
[
377,
4
],
[
352,
6
],
[
377,
1
],
[
339,
8
],
[
377,
1
],
[
311,
11
],
[
40,
1
],
[
313,
4
],
[
41,
1
],
[
377,
1
],
[
123,
1
],
[
377,
5
],
[
313,
5
],
[
362,
2
],
[
311,
3
],
[
377,
1
],
[
61,
1
],
[
377,
1
],
[
313,
4
],
[
59,
1
],
[
377,
3
],
[
125,
1
],
[
377,
1
],
[
125,
1
],
[
59,
1
],
[
377,
1
]
]
}

View file

@ -8,7 +8,7 @@ final class PhutilXHPASTBinary extends Phobject {
* This is the version that would be obtained with an up-to-date XHPAST
* build. The //actual// XHPAST build version may vary.
*/
const EXPECTED_VERSION = '7.1.2';
const EXPECTED_VERSION = '7.1.3';
/**
* The XHPAST build version.

View file

@ -671,13 +671,9 @@ final class PhutilUtilsTestCase extends PhutilTestCase {
phutil_var_export(
array('foo' => array('bar' => array('baz' => array())))));
// Objects
$this->assertEqual(
"stdClass::__set_state(array(\n))",
phutil_var_export(new stdClass()));
$this->assertEqual(
"PhutilTestPhobject::__set_state(array(\n))",
phutil_var_export(new PhutilTestPhobject()));
// NOTE: Object behavior differs across PHP versions. Older versions of
// PHP export objects as "stdClass::__set_state(array())". Newer versions
// of PHP (7.3+) export objects as "(object) array()".
}
public function testFnmatch() {
@ -883,4 +879,43 @@ final class PhutilUtilsTestCase extends PhutilTestCase {
return array_select_keys($map, $keys);
}
public function testQueryStringEncoding() {
$expect = array();
// As a starting point, we expect every character to encode as an "%XX"
// escaped version.
foreach (range(0, 255) as $byte) {
$c = chr($byte);
$expect[$c] = sprintf('%%%02X', $byte);
}
// We expect these characters to not be escaped.
$ranges = array(
range('a', 'z'),
range('A', 'Z'),
range('0', '9'),
array('-', '.', '_', '~'),
);
foreach ($ranges as $range) {
foreach ($range as $preserve_char) {
$expect[$preserve_char] = $preserve_char;
}
}
foreach (range(0, 255) as $byte) {
$c = chr($byte);
$expect_c = $expect[$c];
$expect_str = "{$expect_c}={$expect_c}";
$actual_str = phutil_build_http_querystring(array($c => $c));
$this->assertEqual(
$expect_str,
$actual_str,
pht('HTTP querystring for byte "%s".', sprintf('0x%02x', $byte)));
}
}
}

View file

@ -1014,7 +1014,19 @@ function phutil_fwrite_nonblocking_stream($stream, $bytes) {
$write = array($stream);
$except = array();
@stream_select($read, $write, $except, 0);
$result = @stream_select($read, $write, $except, 0);
if ($result === false) {
// See T13243. If the select is interrupted by a signal, it may return
// "false" indicating an underlying EINTR condition. In this case, the
// results (notably, "$write") are not usable because "stream_select()"
// didn't update them.
// In this case, treat this stream as blocked and tell the caller to
// retry, since EINTR is the only condition we're currently aware of that
// can cause "fread()" to return "0" and "stream_select()" to return
// "false" on the same stream.
return 0;
}
if (!$write) {
// The stream isn't writable, so we conclude that it probably really is
@ -1137,6 +1149,29 @@ function phutil_units($description) {
}
/**
* Compute the number of microseconds that have elapsed since an earlier
* timestamp (from `microtime(true)`).
*
* @param double Microsecond-precision timestamp, from `microtime(true)`.
* @return int Elapsed microseconds.
*/
function phutil_microseconds_since($timestamp) {
if (!is_float($timestamp)) {
throw new Exception(
pht(
'Argument to "phutil_microseconds_since(...)" should be a value '.
'returned from "microtime(true)".'));
}
$delta = (microtime(true) - $timestamp);
$delta = 1000000 * $delta;
$delta = (int)$delta;
return $delta;
}
/**
* Decode a JSON dictionary.
*
@ -1514,3 +1549,39 @@ function phutil_hashes_are_identical($u, $v) {
return ($bits === 0);
}
/**
* Build a query string from a dictionary.
*
* @param map<string, string> Dictionary of parameters.
* @return string HTTP query string.
*/
function phutil_build_http_querystring(array $parameters) {
// We want to encode in RFC3986 mode, but "http_build_query()" did not get
// a flag for that mode until PHP 5.4.0. This is equivalent to calling
// "http_build_query()" with the "PHP_QUERY_RFC3986" flag.
$query = array();
foreach ($parameters as $key => $value) {
$query[] = rawurlencode($key).'='.rawurlencode($value);
}
$query = implode($query, '&');
return $query;
}
function phutil_decode_mime_header($header) {
if (function_exists('iconv_mime_decode')) {
return iconv_mime_decode($header, 0, 'UTF-8');
}
if (function_exists('mb_decode_mimeheader')) {
return mb_decode_mimeheader($header);
}
throw new Exception(
pht(
'Unable to decode MIME header: install "iconv" or "mbstring" '.
'extension.'));
}

View file

@ -0,0 +1,23 @@
<?php
final class AphrontDatabaseTableRef
extends Phobject
implements AphrontDatabaseTableRefInterface {
private $database;
private $table;
public function __construct($database, $table) {
$this->database = $database;
$this->table = $table;
}
public function getAphrontRefDatabaseName() {
return $this->database;
}
public function getAphrontRefTableName() {
return $this->table;
}
}

View file

@ -0,0 +1,8 @@
<?php
interface AphrontDatabaseTableRefInterface {
public function getAphrontRefDatabaseName();
public function getAphrontRefTableName();
}

View file

@ -0,0 +1,39 @@
<?php
final class PhutilQueryString extends Phobject {
private $escaper;
private $argv;
public function __construct(PhutilQsprintfInterface $escaper, array $argv) {
$this->escaper = $escaper;
$this->argv = $argv;
// This makes sure we throw immediately if there are errors in the
// parameters.
$this->getMaskedString();
}
public function __toString() {
return $this->getMaskedString();
}
public function getUnmaskedString() {
return $this->renderString(true);
}
public function getMaskedString() {
return $this->renderString(false);
}
private function renderString($unmasked) {
return xsprintf(
'xsprintf_query',
array(
'escaper' => $this->escaper,
'unmasked' => $unmasked,
),
$this->argv);
}
}

View file

@ -20,23 +20,34 @@
* "List" versions of %d, %s, %f and %B. These are appropriate for use in
* an "IN" clause. For example:
*
* qsprintf($escaper, 'WHERE hatID IN(%Ld)', $list_of_hats);
* qsprintf($escaper, 'WHERE hatID IN (%Ld)', $list_of_hats);
*
* %B ("Binary String")
* Escapes a string for insertion into a pure binary column, ignoring
* tests for characters outside of the basic multilingual plane.
*
* %T ("Table")
* Escapes a table name.
*
* %C, %LC
* Escapes a column name or a list of column names.
* %C, %LC, %LK ("Column", "Key Column")
* Escapes a column name or a list of column names. The "%LK" variant
* escapes a list of key column specifications which may look like
* "column(32)".
*
* %K ("Comment")
* Escapes a comment.
*
* %Q ("Query Fragment")
* Injects a raw query fragment. Extremely dangerous! Not escaped!
* %Q, %LA, %LO, %LQ, %LJ ("Query Fragment")
* Injects a query fragment from a prior call to qsprintf(). The list
* variants join a list of query fragments with AND, OR, comma, or space.
*
* %Z ("Raw Query")
* Injects a raw, unescaped query fragment. Dangerous!
*
* %R ("Database and Table Reference")
* Behaves like "%T.%T" and prints a full reference to a table including
* the database. Accepts a AphrontDatabaseTableRefInterface.
*
* %P ("Password or Secret")
* Behaves like "%s", but shows "********" when the query is printed in
* logs or traces. Accepts a PhutilOpaqueEnvelope.
*
* %~ ("Substring")
* Escapes a substring query for a LIKE (or NOT LIKE) clause. For example:
@ -57,16 +68,19 @@
*
* // Find all rows where `name` ends with $suffix.
* qsprintf($escaper, 'WHERE name LIKE %<', $suffix);
*
* %T ("Table")
* Escapes a table name. In most cases, you should use "%R" instead.
*/
function qsprintf(PhutilQsprintfInterface $escaper, $pattern /* , ... */) {
$args = func_get_args();
array_shift($args);
return xsprintf('xsprintf_query', $escaper, $args);
return new PhutilQueryString($escaper, $args);
}
function vqsprintf(PhutilQsprintfInterface $escaper, $pattern, array $argv) {
array_unshift($argv, $pattern);
return xsprintf('xsprintf_query', $escaper, $argv);
return new PhutilQueryString($escaper, $argv);
}
/**
@ -74,12 +88,19 @@ function vqsprintf(PhutilQsprintfInterface $escaper, $pattern, array $argv) {
* @{function:qsprintf}.
*/
function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) {
$type = $pattern[$pos];
$escaper = $userdata;
$next = (strlen($pattern) > $pos + 1) ? $pattern[$pos + 1] : null;
$type = $pattern[$pos];
if (is_array($userdata)) {
$escaper = $userdata['escaper'];
$unmasked = $userdata['unmasked'];
} else {
$escaper = $userdata;
$unmasked = false;
}
$next = (strlen($pattern) > $pos + 1) ? $pattern[$pos + 1] : null;
$nullable = false;
$done = false;
$done = false;
$prefix = '';
@ -162,6 +183,73 @@ function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) {
}
$value = implode(', ', $value);
break;
case 'K': // ...key columns.
// This is like "%LC", but for escaping column lists passed to key
// specifications. These should be escaped as "`column`(123)". For
// example:
//
// ALTER TABLE `x` ADD KEY `y` (`u`(16), `v`(32));
foreach ($value as $k => $v) {
$matches = null;
if (preg_match('/\((\d+)\)\z/', $v, $matches)) {
$v = substr($v, 0, -(strlen($matches[1]) + 2));
$prefix_len = '('.((int)$matches[1]).')';
} else {
$prefix_len = '';
}
$value[$k] = $escaper->escapeColumnName($v).$prefix_len;
}
$value = implode(', ', $value);
break;
case 'Q':
// TODO: Here, and in "%LO", "%LA", and "%LJ", we should eventually
// stop accepting strings.
foreach ($value as $k => $v) {
if (is_string($v)) {
continue;
}
$value[$k] = $v->getUnmaskedString();
}
$value = implode(', ', $value);
break;
case 'O':
foreach ($value as $k => $v) {
if (is_string($v)) {
continue;
}
$value[$k] = $v->getUnmaskedString();
}
if (count($value) == 1) {
$value = '('.head($value).')';
} else {
$value = '(('.implode(') OR (', $value).'))';
}
break;
case 'A':
foreach ($value as $k => $v) {
if (is_string($v)) {
continue;
}
$value[$k] = $v->getUnmaskedString();
}
if (count($value) == 1) {
$value = '('.head($value).')';
} else {
$value = '(('.implode(') AND (', $value).'))';
}
break;
case 'J':
foreach ($value as $k => $v) {
if (is_string($v)) {
continue;
}
$value[$k] = $v->getUnmaskedString();
}
$value = implode(' ', $value);
break;
default:
throw new XsprintfUnknownConversionException("%L{$next}");
}
@ -190,6 +278,13 @@ function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) {
break;
case 'Q': // Query Fragment
if ($value instanceof PhutilQueryString) {
$value = $value->getUnmaskedString();
}
$type = 's';
break;
case 'Z': // Raw Query Fragment
$type = 's';
break;
@ -234,6 +329,27 @@ function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) {
$type = 's';
break;
case 'R': // Database + Table Reference
$database_name = $value->getAphrontRefDatabaseName();
$database_name = $escaper->escapeColumnName($database_name);
$table_name = $value->getAphrontRefTableName();
$table_name = $escaper->escapeColumnName($table_name);
$value = $database_name.'.'.$table_name;
$type = 's';
break;
case 'P': // Password or Secret
if ($unmasked) {
$value = $value->openEnvelope();
$value = "'".$escaper->escapeUTF8String($value)."'";
} else {
$value = '********';
}
$type = 's';
break;
default:
throw new XsprintfUnknownConversionException($type);
}
@ -242,6 +358,7 @@ function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) {
if ($prefix) {
$value = $prefix.$value;
}
$pattern[$pos] = $type;
}
@ -250,8 +367,13 @@ function qsprintf_check_type($value, $type, $query) {
case 'Ld':
case 'Ls':
case 'LC':
case 'LK':
case 'LB':
case 'Lf':
case 'LQ':
case 'LA':
case 'LO':
case 'LJ':
if (!is_array($value)) {
throw new AphrontParameterQueryException(
$query,
@ -275,8 +397,62 @@ function qsprintf_check_type($value, $type, $query) {
function qsprintf_check_scalar_type($value, $type, $query) {
switch ($type) {
case 'LQ':
case 'LA':
case 'LO':
case 'LJ':
// TODO: See T13217. Remove this eventually.
if (is_string($value)) {
phlog(
pht(
'UNSAFE: Raw string ("%s") passed to query ("%s") subclause '.
'for "%%%s" conversion. Subclause conversions should be passed '.
'a list of PhutilQueryString objects.',
$value,
$query,
$type));
break;
}
if (!($value instanceof PhutilQueryString)) {
throw new AphrontParameterQueryException(
$query,
pht(
'Expected a list of PhutilQueryString objects for %%%s '.
'conversion.',
$type));
}
break;
case 'Q':
// TODO: See T13217. Remove this eventually.
if (is_string($value)) {
phlog(
pht(
'UNSAFE: Raw string ("%s") passed to query ("%s") for "%%Q" '.
'conversion. %%Q should be passed a query string.',
$value,
$query));
break;
}
if (!($value instanceof PhutilQueryString)) {
throw new AphrontParameterQueryException(
$query,
pht('Expected a PhutilQueryString for %%%s conversion.', $type));
}
break;
case 'Z':
if (!is_string($value)) {
throw new AphrontParameterQueryException(
$query,
pht('Value for "%%Z" conversion should be a raw string.'));
}
break;
case 'LC':
case 'LK':
case 'T':
case 'C':
if (!is_string($value)) {
@ -312,6 +488,28 @@ function qsprintf_check_scalar_type($value, $type, $query) {
}
break;
case 'R':
if (!($value instanceof AphrontDatabaseTableRefInterface)) {
throw new AphrontParameterQueryException(
$query,
pht(
'Parameter to "%s" conversion in "qsprintf(...)" is not an '.
'instance of AphrontDatabaseTableRefInterface.',
'%R'));
}
break;
case 'P':
if (!($value instanceof PhutilOpaqueEnvelope)) {
throw new AphrontParameterQueryException(
$query,
pht(
'Parameter to "%s" conversion in "qsprintf(...)" is not an '.
'instance of PhutilOpaqueEnvelope.',
'%P'));
}
break;
default:
throw new XsprintfUnknownConversionException($type);
}

View file

@ -5,7 +5,7 @@ function queryfx(AphrontDatabaseConnection $conn, $sql /* , ... */) {
$query = call_user_func_array('qsprintf', $argv);
$conn->setLastActiveEpoch(time());
$conn->executeRawQuery($query);
$conn->executeQuery($query);
}
function queryfx_all(AphrontDatabaseConnection $conn, $sql /* , ... */) {

View file

@ -2746,6 +2746,21 @@ new_expr:
$1->appendChild($3);
$$ = $1;
}
| T_NEW T_CLASS ctor_arguments extends_from implements_list
'{' class_statement_list '}' {
$$ = NNEW(n_CLASS_DECLARATION);
$$->appendChild($2);
$$->appendChild(NNEW(n_EMPTY));
$$->appendChild($4);
$$->appendChild($5);
$$->appendChild(NEXPAND($6, $7, $8));
NMORE($$, $8);
NTYPE($1, n_NEW);
$1->appendChild($$);
$1->appendChild($3);
$$ = $1;
}
;
class_constant:

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -12,12 +12,234 @@
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 35
#define YY_FLEX_MINOR_VERSION 6
#define YY_FLEX_SUBMINOR_VERSION 4
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
#ifdef yy_create_buffer
#define xhpast_create_buffer_ALREADY_DEFINED
#else
#define yy_create_buffer xhpast_create_buffer
#endif
#ifdef yy_delete_buffer
#define xhpast_delete_buffer_ALREADY_DEFINED
#else
#define yy_delete_buffer xhpast_delete_buffer
#endif
#ifdef yy_scan_buffer
#define xhpast_scan_buffer_ALREADY_DEFINED
#else
#define yy_scan_buffer xhpast_scan_buffer
#endif
#ifdef yy_scan_string
#define xhpast_scan_string_ALREADY_DEFINED
#else
#define yy_scan_string xhpast_scan_string
#endif
#ifdef yy_scan_bytes
#define xhpast_scan_bytes_ALREADY_DEFINED
#else
#define yy_scan_bytes xhpast_scan_bytes
#endif
#ifdef yy_init_buffer
#define xhpast_init_buffer_ALREADY_DEFINED
#else
#define yy_init_buffer xhpast_init_buffer
#endif
#ifdef yy_flush_buffer
#define xhpast_flush_buffer_ALREADY_DEFINED
#else
#define yy_flush_buffer xhpast_flush_buffer
#endif
#ifdef yy_load_buffer_state
#define xhpast_load_buffer_state_ALREADY_DEFINED
#else
#define yy_load_buffer_state xhpast_load_buffer_state
#endif
#ifdef yy_switch_to_buffer
#define xhpast_switch_to_buffer_ALREADY_DEFINED
#else
#define yy_switch_to_buffer xhpast_switch_to_buffer
#endif
#ifdef yypush_buffer_state
#define xhpastpush_buffer_state_ALREADY_DEFINED
#else
#define yypush_buffer_state xhpastpush_buffer_state
#endif
#ifdef yypop_buffer_state
#define xhpastpop_buffer_state_ALREADY_DEFINED
#else
#define yypop_buffer_state xhpastpop_buffer_state
#endif
#ifdef yyensure_buffer_stack
#define xhpastensure_buffer_stack_ALREADY_DEFINED
#else
#define yyensure_buffer_stack xhpastensure_buffer_stack
#endif
#ifdef yylex
#define xhpastlex_ALREADY_DEFINED
#else
#define yylex xhpastlex
#endif
#ifdef yyrestart
#define xhpastrestart_ALREADY_DEFINED
#else
#define yyrestart xhpastrestart
#endif
#ifdef yylex_init
#define xhpastlex_init_ALREADY_DEFINED
#else
#define yylex_init xhpastlex_init
#endif
#ifdef yylex_init_extra
#define xhpastlex_init_extra_ALREADY_DEFINED
#else
#define yylex_init_extra xhpastlex_init_extra
#endif
#ifdef yylex_destroy
#define xhpastlex_destroy_ALREADY_DEFINED
#else
#define yylex_destroy xhpastlex_destroy
#endif
#ifdef yyget_debug
#define xhpastget_debug_ALREADY_DEFINED
#else
#define yyget_debug xhpastget_debug
#endif
#ifdef yyset_debug
#define xhpastset_debug_ALREADY_DEFINED
#else
#define yyset_debug xhpastset_debug
#endif
#ifdef yyget_extra
#define xhpastget_extra_ALREADY_DEFINED
#else
#define yyget_extra xhpastget_extra
#endif
#ifdef yyset_extra
#define xhpastset_extra_ALREADY_DEFINED
#else
#define yyset_extra xhpastset_extra
#endif
#ifdef yyget_in
#define xhpastget_in_ALREADY_DEFINED
#else
#define yyget_in xhpastget_in
#endif
#ifdef yyset_in
#define xhpastset_in_ALREADY_DEFINED
#else
#define yyset_in xhpastset_in
#endif
#ifdef yyget_out
#define xhpastget_out_ALREADY_DEFINED
#else
#define yyget_out xhpastget_out
#endif
#ifdef yyset_out
#define xhpastset_out_ALREADY_DEFINED
#else
#define yyset_out xhpastset_out
#endif
#ifdef yyget_leng
#define xhpastget_leng_ALREADY_DEFINED
#else
#define yyget_leng xhpastget_leng
#endif
#ifdef yyget_text
#define xhpastget_text_ALREADY_DEFINED
#else
#define yyget_text xhpastget_text
#endif
#ifdef yyget_lineno
#define xhpastget_lineno_ALREADY_DEFINED
#else
#define yyget_lineno xhpastget_lineno
#endif
#ifdef yyset_lineno
#define xhpastset_lineno_ALREADY_DEFINED
#else
#define yyset_lineno xhpastset_lineno
#endif
#ifdef yyget_column
#define xhpastget_column_ALREADY_DEFINED
#else
#define yyget_column xhpastget_column
#endif
#ifdef yyset_column
#define xhpastset_column_ALREADY_DEFINED
#else
#define yyset_column xhpastset_column
#endif
#ifdef yywrap
#define xhpastwrap_ALREADY_DEFINED
#else
#define yywrap xhpastwrap
#endif
#ifdef yyget_lval
#define xhpastget_lval_ALREADY_DEFINED
#else
#define yyget_lval xhpastget_lval
#endif
#ifdef yyset_lval
#define xhpastset_lval_ALREADY_DEFINED
#else
#define yyset_lval xhpastset_lval
#endif
#ifdef yyalloc
#define xhpastalloc_ALREADY_DEFINED
#else
#define yyalloc xhpastalloc
#endif
#ifdef yyrealloc
#define xhpastrealloc_ALREADY_DEFINED
#else
#define yyrealloc xhpastrealloc
#endif
#ifdef yyfree
#define xhpastfree_ALREADY_DEFINED
#else
#define yyfree xhpastfree
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
@ -88,29 +310,23 @@ typedef unsigned int flex_uint32_t;
#define UINT32_MAX (4294967295U)
#endif
#ifndef SIZE_MAX
#define SIZE_MAX (~(size_t)0)
#endif
#endif /* ! C99 */
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
/* begin standard C++ headers. */
/* The "const" storage-class-modifier is valid. */
#define YY_USE_CONST
#else /* ! __cplusplus */
/* C99 requires __STDC__ to be defined as 1. */
#if defined (__STDC__)
#define YY_USE_CONST
#endif /* defined (__STDC__) */
#endif /* ! __cplusplus */
#ifdef YY_USE_CONST
/* TODO: this is always defined, so inline it */
#define yyconst const
#if defined(__GNUC__) && __GNUC__ >= 3
#define yynoreturn __attribute__((__noreturn__))
#else
#define yyconst
#define yynoreturn
#endif
/* An opaque pointer. */
@ -165,7 +381,7 @@ struct yy_buffer_state
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
yy_size_t yy_buf_size;
int yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
@ -193,7 +409,7 @@ struct yy_buffer_state
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
@ -204,25 +420,25 @@ struct yy_buffer_state
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
void xhpastrestart (FILE *input_file ,yyscan_t yyscanner );
void xhpast_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
YY_BUFFER_STATE xhpast_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
void xhpast_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void xhpast_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void xhpastpush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
void xhpastpop_buffer_state (yyscan_t yyscanner );
void yyrestart ( FILE *input_file , yyscan_t yyscanner );
void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
void yypop_buffer_state ( yyscan_t yyscanner );
YY_BUFFER_STATE xhpast_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
YY_BUFFER_STATE xhpast_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE xhpast_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
void *xhpastalloc (yy_size_t ,yyscan_t yyscanner );
void *xhpastrealloc (void *,yy_size_t ,yyscan_t yyscanner );
void xhpastfree (void * ,yyscan_t yyscanner );
void *yyalloc ( yy_size_t , yyscan_t yyscanner );
void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
void yyfree ( void * , yyscan_t yyscanner );
/* Begin user sect3 */
#define xhpastwrap(n) 1
#define xhpastwrap(yyscanner) (/*CONSTCOND*/1)
#define YY_SKIP_YYWRAP
#define yytext_ptr yytext_r
@ -254,42 +470,46 @@ void xhpastfree (void * ,yyscan_t yyscanner );
#define YY_EXTRA_TYPE void *
#endif
int xhpastlex_init (yyscan_t* scanner);
int yylex_init (yyscan_t* scanner);
int xhpastlex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int xhpastlex_destroy (yyscan_t yyscanner );
int yylex_destroy ( yyscan_t yyscanner );
int xhpastget_debug (yyscan_t yyscanner );
int yyget_debug ( yyscan_t yyscanner );
void xhpastset_debug (int debug_flag ,yyscan_t yyscanner );
void yyset_debug ( int debug_flag , yyscan_t yyscanner );
YY_EXTRA_TYPE xhpastget_extra (yyscan_t yyscanner );
YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
void xhpastset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
FILE *xhpastget_in (yyscan_t yyscanner );
FILE *yyget_in ( yyscan_t yyscanner );
void xhpastset_in (FILE * in_str ,yyscan_t yyscanner );
void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
FILE *xhpastget_out (yyscan_t yyscanner );
FILE *yyget_out ( yyscan_t yyscanner );
void xhpastset_out (FILE * out_str ,yyscan_t yyscanner );
void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
int xhpastget_leng (yyscan_t yyscanner );
int yyget_leng ( yyscan_t yyscanner );
char *xhpastget_text (yyscan_t yyscanner );
char *yyget_text ( yyscan_t yyscanner );
int xhpastget_lineno (yyscan_t yyscanner );
int yyget_lineno ( yyscan_t yyscanner );
void xhpastset_lineno (int line_number ,yyscan_t yyscanner );
void yyset_lineno ( int _line_number , yyscan_t yyscanner );
YYSTYPE * xhpastget_lval (yyscan_t yyscanner );
int yyget_column ( yyscan_t yyscanner );
void xhpastset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
void yyset_column ( int _column_no , yyscan_t yyscanner );
YYSTYPE * yyget_lval ( yyscan_t yyscanner );
void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner );
/* Macros after this point can all be overridden by user definitions in
* section 1.
@ -297,18 +517,18 @@ void xhpastset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int xhpastwrap (yyscan_t yyscanner );
extern "C" int yywrap ( yyscan_t yyscanner );
#else
extern int xhpastwrap (yyscan_t yyscanner );
extern int yywrap ( yyscan_t yyscanner );
#endif
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
#endif
#ifndef YY_NO_INPUT
@ -336,10 +556,10 @@ static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int xhpastlex \
(YYSTYPE * yylval_param ,yyscan_t yyscanner);
extern int yylex \
(YYSTYPE * yylval_param , yyscan_t yyscanner);
#define YY_DECL int xhpastlex \
#define YY_DECL int yylex \
(YYSTYPE * yylval_param , yyscan_t yyscanner)
#endif /* !YY_DECL */
@ -357,10 +577,155 @@ extern int xhpastlex \
#undef YY_DECL
#endif
#line 367 "scanner.l"
#ifndef xhpast_create_buffer_ALREADY_DEFINED
#undef yy_create_buffer
#endif
#ifndef xhpast_delete_buffer_ALREADY_DEFINED
#undef yy_delete_buffer
#endif
#ifndef xhpast_scan_buffer_ALREADY_DEFINED
#undef yy_scan_buffer
#endif
#ifndef xhpast_scan_string_ALREADY_DEFINED
#undef yy_scan_string
#endif
#ifndef xhpast_scan_bytes_ALREADY_DEFINED
#undef yy_scan_bytes
#endif
#ifndef xhpast_init_buffer_ALREADY_DEFINED
#undef yy_init_buffer
#endif
#ifndef xhpast_flush_buffer_ALREADY_DEFINED
#undef yy_flush_buffer
#endif
#ifndef xhpast_load_buffer_state_ALREADY_DEFINED
#undef yy_load_buffer_state
#endif
#ifndef xhpast_switch_to_buffer_ALREADY_DEFINED
#undef yy_switch_to_buffer
#endif
#ifndef xhpastpush_buffer_state_ALREADY_DEFINED
#undef yypush_buffer_state
#endif
#ifndef xhpastpop_buffer_state_ALREADY_DEFINED
#undef yypop_buffer_state
#endif
#ifndef xhpastensure_buffer_stack_ALREADY_DEFINED
#undef yyensure_buffer_stack
#endif
#ifndef xhpastlex_ALREADY_DEFINED
#undef yylex
#endif
#ifndef xhpastrestart_ALREADY_DEFINED
#undef yyrestart
#endif
#ifndef xhpastlex_init_ALREADY_DEFINED
#undef yylex_init
#endif
#ifndef xhpastlex_init_extra_ALREADY_DEFINED
#undef yylex_init_extra
#endif
#ifndef xhpastlex_destroy_ALREADY_DEFINED
#undef yylex_destroy
#endif
#ifndef xhpastget_debug_ALREADY_DEFINED
#undef yyget_debug
#endif
#ifndef xhpastset_debug_ALREADY_DEFINED
#undef yyset_debug
#endif
#ifndef xhpastget_extra_ALREADY_DEFINED
#undef yyget_extra
#endif
#ifndef xhpastset_extra_ALREADY_DEFINED
#undef yyset_extra
#endif
#ifndef xhpastget_in_ALREADY_DEFINED
#undef yyget_in
#endif
#ifndef xhpastset_in_ALREADY_DEFINED
#undef yyset_in
#endif
#ifndef xhpastget_out_ALREADY_DEFINED
#undef yyget_out
#endif
#ifndef xhpastset_out_ALREADY_DEFINED
#undef yyset_out
#endif
#ifndef xhpastget_leng_ALREADY_DEFINED
#undef yyget_leng
#endif
#ifndef xhpastget_text_ALREADY_DEFINED
#undef yyget_text
#endif
#ifndef xhpastget_lineno_ALREADY_DEFINED
#undef yyget_lineno
#endif
#ifndef xhpastset_lineno_ALREADY_DEFINED
#undef yyset_lineno
#endif
#ifndef xhpastget_column_ALREADY_DEFINED
#undef yyget_column
#endif
#ifndef xhpastset_column_ALREADY_DEFINED
#undef yyset_column
#endif
#ifndef xhpastwrap_ALREADY_DEFINED
#undef yywrap
#endif
#ifndef xhpastget_lval_ALREADY_DEFINED
#undef yyget_lval
#endif
#ifndef xhpastset_lval_ALREADY_DEFINED
#undef yyset_lval
#endif
#ifndef xhpastget_lloc_ALREADY_DEFINED
#undef yyget_lloc
#endif
#ifndef xhpastset_lloc_ALREADY_DEFINED
#undef yyset_lloc
#endif
#ifndef xhpastalloc_ALREADY_DEFINED
#undef yyalloc
#endif
#ifndef xhpastrealloc_ALREADY_DEFINED
#undef yyrealloc
#endif
#ifndef xhpastfree_ALREADY_DEFINED
#undef yyfree
#endif
#ifndef xhpasttext_ALREADY_DEFINED
#undef yytext
#endif
#ifndef xhpastleng_ALREADY_DEFINED
#undef yyleng
#endif
#ifndef xhpastin_ALREADY_DEFINED
#undef yyin
#endif
#ifndef xhpastout_ALREADY_DEFINED
#undef yyout
#endif
#ifndef xhpast_flex_debug_ALREADY_DEFINED
#undef yy_flex_debug
#endif
#ifndef xhpastlineno_ALREADY_DEFINED
#undef yylineno
#endif
#ifndef xhpasttables_fload_ALREADY_DEFINED
#undef yytables_fload
#endif
#ifndef xhpasttables_destroy_ALREADY_DEFINED
#undef yytables_destroy
#endif
#ifndef xhpastTABLES_NAME_ALREADY_DEFINED
#undef yyTABLES_NAME
#endif
#line 368 "scanner.l"
#line 364 "scanner.lex.hpp"
#line 729 "scanner.lex.hpp"
#undef xhpastIN_HEADER
#endif /* xhpastHEADER_H */
/* @generated */

View file

@ -12,7 +12,7 @@ void print_node(xhpast::Node *node);
int main(int argc, char* argv[]) {
if (argc != 1) {
// Coupling: modify also src/parser/xhpast/bin/PhutilXHPASTBinary.php
cout << "7.1.2\n";
cout << "7.1.3\n";
return 0;
}